Rebrand: Nexus Terminal → Alchementary Link
Renames the product across the codebase: Go module path, config/data paths, keychain service name, export file extension, build tooling, docs, and UI text. Sidebar brand mark now reads ">_ AlchementaryLink" with "Alchementary" in the connection-group gold accent colour, and the window title is "Alchementary Link". Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019T8bJS8BCtY5GeSG2HBj1n
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
114060127f
commit
1a380a0e99
+3
-3
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Nexus Terminal</title>
|
||||
<title>Alchementary Link</title>
|
||||
|
||||
<!-- Vendor styles -->
|
||||
<link rel="stylesheet" href="/src/css/lib/xterm.css" />
|
||||
@@ -52,10 +52,10 @@
|
||||
<span class="sidebar-product-switch" aria-label="Application mode">
|
||||
<button
|
||||
class="sidebar-title sidebar-mode-toggle is-active"
|
||||
id="mode-nexus"
|
||||
id="mode-link"
|
||||
type="button"
|
||||
>
|
||||
Nexus Terminal
|
||||
<span class="sidebar-brand-gold">Alchementary</span>Link
|
||||
</button>
|
||||
<button
|
||||
class="sidebar-title sidebar-mode-toggle"
|
||||
|
||||
Generated
+2
-2
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "nexus-terminal-frontend",
|
||||
"name": "alchementary-link-frontend",
|
||||
"version": "0.1.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "nexus-terminal-frontend",
|
||||
"name": "alchementary-link-frontend",
|
||||
"version": "0.1.0",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "nexus-terminal-frontend",
|
||||
"name": "alchementary-link-frontend",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"description": "Frontend for Nexus Terminal desktop SSH client",
|
||||
"description": "Frontend for Alchementary Link desktop SSH client",
|
||||
"scripts": {
|
||||
"install:assets": "node scripts/copy-assets.js",
|
||||
"bundle:novnc": "node scripts/bundle-novnc.js",
|
||||
|
||||
@@ -1 +1 @@
|
||||
c004a0f41f9a4d08a1439b48dba5780a
|
||||
24ecf1ed6af3b41cf2f6d8fa0bc732ec
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* build.js — Build script for the Nexus Terminal frontend.
|
||||
* build.js — Build script for the Alchementary Link frontend.
|
||||
*
|
||||
* In production mode (no flags): copies vendor assets, assembles the
|
||||
* dist/ directory with all files needed by the Wails embed, and validates
|
||||
|
||||
@@ -8,7 +8,7 @@ const frontendRoot = path.resolve(__dirname, "..");
|
||||
const source = fs.readFileSync(path.join(frontendRoot, "src/js/irc.js"), "utf8");
|
||||
const context = {
|
||||
window: {
|
||||
NexusTerminal: {
|
||||
AlchementaryLink: {
|
||||
createElement: () => {
|
||||
throw new Error("formatting tests must not create DOM elements");
|
||||
},
|
||||
@@ -18,7 +18,7 @@ const context = {
|
||||
vm.createContext(context);
|
||||
vm.runInContext(source, context, { filename: "src/js/irc.js" });
|
||||
|
||||
const formatting = context.window.NexusTerminal.IRCFormatting;
|
||||
const formatting = context.window.AlchementaryLink.IRCFormatting;
|
||||
assert.ok(formatting, "IRC formatting helpers should be exported");
|
||||
|
||||
assert.strictEqual(
|
||||
|
||||
@@ -143,7 +143,7 @@ assertContains(
|
||||
assertContains(
|
||||
"frontend/index.html",
|
||||
"mode-leviathan",
|
||||
"Leviathan mode toggle must be present beside Nexus Terminal",
|
||||
"Leviathan mode toggle must be present beside Alchementary Link",
|
||||
);
|
||||
assertContains(
|
||||
"frontend/index.html",
|
||||
@@ -232,12 +232,12 @@ assertContains(
|
||||
);
|
||||
assertContains(
|
||||
"frontend/src/js/app.js",
|
||||
"window.NexusTerminal.VNCViewer?.destroyViewer(sessionId)",
|
||||
"window.AlchementaryLink.VNCViewer?.destroyViewer(sessionId)",
|
||||
"session teardown must disconnect and remove VNC viewers",
|
||||
);
|
||||
assertContains(
|
||||
"frontend/src/js/app.js",
|
||||
"window.NexusTerminal.IRCViewer?.destroyViewer(sessionId)",
|
||||
"window.AlchementaryLink.IRCViewer?.destroyViewer(sessionId)",
|
||||
"session teardown must dispose IRC terminals and resize observers",
|
||||
);
|
||||
assertContains(
|
||||
@@ -262,7 +262,7 @@ assertContains(
|
||||
);
|
||||
assertContains(
|
||||
"frontend/src/js/splitpane.js",
|
||||
"await window.NexusTerminal.wailsService.DisconnectSession(sessionInfo.id)",
|
||||
"await window.AlchementaryLink.wailsService.DisconnectSession(sessionInfo.id)",
|
||||
"late split-pane connection results must be disconnected after their pane closes",
|
||||
);
|
||||
assertContains(
|
||||
@@ -404,7 +404,7 @@ assertContains(
|
||||
assertContains(
|
||||
"internal/api/leviathan.go",
|
||||
"LeviathanRunTool",
|
||||
"Nexus API must expose the imported Leviathan engine",
|
||||
"Link API must expose the imported Leviathan engine",
|
||||
);
|
||||
assertNotContains(
|
||||
"internal/api/leviathan.go",
|
||||
@@ -528,12 +528,12 @@ assertNotContains(
|
||||
);
|
||||
assertContains(
|
||||
"build.sh",
|
||||
"NEXUS_COMPRESS",
|
||||
"LINK_COMPRESS",
|
||||
"local builds must expose an explicit compression switch",
|
||||
);
|
||||
assertContains(
|
||||
"scripts/build-common.sh",
|
||||
"nexus_use_upx_for_target",
|
||||
"link_use_upx_for_target",
|
||||
"build scripts must share one UPX target-selection helper",
|
||||
);
|
||||
assertContains(
|
||||
@@ -563,7 +563,7 @@ assertContains(
|
||||
);
|
||||
assertContains(
|
||||
"Makefile",
|
||||
"NEXUS_FORCE_MACOS_UPX",
|
||||
"LINK_FORCE_MACOS_UPX",
|
||||
"Makefile compressed builds must make macOS UPX opt-in",
|
||||
);
|
||||
assertTreeNotContains(
|
||||
|
||||
@@ -14,7 +14,7 @@ function loadScript(relativePath, additions = {}) {
|
||||
Set,
|
||||
setTimeout,
|
||||
clearTimeout,
|
||||
window: { NexusTerminal: {} },
|
||||
window: { AlchementaryLink: {} },
|
||||
document: {
|
||||
querySelector: () => null,
|
||||
addEventListener: () => {},
|
||||
@@ -32,7 +32,7 @@ function loadScript(relativePath, additions = {}) {
|
||||
|
||||
async function testRepeatedSplitCleanup() {
|
||||
const context = loadScript("src/js/splitpane.js");
|
||||
const SplitPaneManager = context.window.NexusTerminal.SplitPaneManager;
|
||||
const SplitPaneManager = context.window.AlchementaryLink.SplitPaneManager;
|
||||
const closed = [];
|
||||
const app = {
|
||||
closeSessionImmediate: async (sessionId, options) => {
|
||||
@@ -100,7 +100,7 @@ async function testRepeatedSplitCleanup() {
|
||||
|
||||
function testClosedPaneRejectsLateConnection() {
|
||||
const context = loadScript("src/js/splitpane.js");
|
||||
const SplitPaneManager = context.window.NexusTerminal.SplitPaneManager;
|
||||
const SplitPaneManager = context.window.AlchementaryLink.SplitPaneManager;
|
||||
const manager = new SplitPaneManager({ closeSessionImmediate: async () => {} });
|
||||
const pane = {
|
||||
dataset: { paneId: "pane-1" },
|
||||
@@ -128,7 +128,7 @@ function testClosedPaneRejectsLateConnection() {
|
||||
|
||||
function testPerSessionStoresReleaseIDs() {
|
||||
const stateContext = loadScript("src/js/state.js");
|
||||
const State = stateContext.window.NexusTerminal.State;
|
||||
const State = stateContext.window.AlchementaryLink.State;
|
||||
const state = new State();
|
||||
state.addSession({ id: "session-1" });
|
||||
state.broadcastTargets = new Set(["session-1"]);
|
||||
@@ -138,7 +138,7 @@ function testPerSessionStoresReleaseIDs() {
|
||||
|
||||
const recordingContext = loadScript("src/js/recordings.js");
|
||||
const RecordingManager =
|
||||
recordingContext.window.NexusTerminal.RecordingManager;
|
||||
recordingContext.window.AlchementaryLink.RecordingManager;
|
||||
const recordings = new RecordingManager({});
|
||||
recordings.activeSessions.add("session-1");
|
||||
recordings.cleanup("session-1");
|
||||
@@ -149,7 +149,7 @@ async function testCompleteAppTeardownIsImmediateAndIdempotent() {
|
||||
let finishBackendClose;
|
||||
let backendCloses = 0;
|
||||
const context = loadScript("src/js/app.js");
|
||||
const App = context.window.NexusTerminal.App;
|
||||
const App = context.window.AlchementaryLink.App;
|
||||
const app = Object.create(App.prototype);
|
||||
const resources = {
|
||||
sftp: new Set(["session-1"]),
|
||||
@@ -195,10 +195,10 @@ async function testCompleteAppTeardownIsImmediateAndIdempotent() {
|
||||
};
|
||||
app.updateStatusBar = () => {};
|
||||
|
||||
context.window.NexusTerminal.GitHubViewer = drop("github");
|
||||
context.window.NexusTerminal.VNCViewer = drop("vnc");
|
||||
context.window.NexusTerminal.IRCViewer = drop("irc");
|
||||
context.window.NexusTerminal.wailsService = {
|
||||
context.window.AlchementaryLink.GitHubViewer = drop("github");
|
||||
context.window.AlchementaryLink.VNCViewer = drop("vnc");
|
||||
context.window.AlchementaryLink.IRCViewer = drop("irc");
|
||||
context.window.AlchementaryLink.wailsService = {
|
||||
DisconnectSession: () => {
|
||||
backendCloses++;
|
||||
return new Promise((resolve) => {
|
||||
@@ -240,7 +240,7 @@ async function testLatencyPingIsSingleFlightAndIgnoresStaleResults() {
|
||||
addEventListener: () => {},
|
||||
},
|
||||
});
|
||||
const App = context.window.NexusTerminal.App;
|
||||
const App = context.window.AlchementaryLink.App;
|
||||
const app = Object.create(App.prototype);
|
||||
app._latencySessionID = null;
|
||||
app._latencyTimer = null;
|
||||
@@ -248,7 +248,7 @@ async function testLatencyPingIsSingleFlightAndIgnoresStaleResults() {
|
||||
app._latencyPingInFlight = false;
|
||||
|
||||
const pending = [];
|
||||
context.window.NexusTerminal.wailsService = {
|
||||
context.window.AlchementaryLink.wailsService = {
|
||||
PingSession: (sessionID) =>
|
||||
new Promise((resolve, reject) => {
|
||||
pending.push({ sessionID, resolve, reject });
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* ==========================================================================
|
||||
Nexus Terminal — Base / Reset
|
||||
Alchementary Link — Base / Reset
|
||||
Modern CSS reset and foundational element styles.
|
||||
========================================================================== */
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* ==========================================================================
|
||||
Nexus Terminal — Reusable Components
|
||||
Alchementary Link — Reusable Components
|
||||
Buttons, form controls, badges, dropdowns, modals, notifications, etc.
|
||||
========================================================================== */
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* ==========================================================================
|
||||
Nexus Terminal — Profile Editor & Form Polish
|
||||
Alchementary Link — Profile Editor & Form Polish
|
||||
Section headers, grouped fields, protocol-specific visibility,
|
||||
inline validation, tag input, jump host list, key file selector.
|
||||
========================================================================== */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* ==========================================================================
|
||||
Nexus Terminal - IRC viewer
|
||||
Alchementary Link - IRC viewer
|
||||
========================================================================== */
|
||||
|
||||
.irc-viewer-panel {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* ==========================================================================
|
||||
Nexus Terminal — Application Layout
|
||||
Alchementary Link — Application Layout
|
||||
Grid shell, sidebar, tab bar, workspace, and status bar structure.
|
||||
========================================================================== */
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Leviathan mode inside the Nexus shell */
|
||||
/* Leviathan mode inside the Link shell */
|
||||
|
||||
.leviathan-actions {
|
||||
display: grid;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* ==========================================================================
|
||||
Nexus Terminal — Modals & Drawers
|
||||
Alchementary Link — Modals & Drawers
|
||||
Profile editor drawer, settings modal, known hosts viewer,
|
||||
confirmation dialogs, host key verification dialog.
|
||||
========================================================================== */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* ==========================================================================
|
||||
Nexus Terminal — SFTP File Browser
|
||||
Alchementary Link — SFTP File Browser
|
||||
Right-side panel for browsing remote files alongside the terminal.
|
||||
========================================================================== */
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* ==========================================================================
|
||||
Nexus Terminal — Sidebar
|
||||
Alchementary Link — Sidebar
|
||||
Connection manager: logo, search, sections, profile list, footer.
|
||||
========================================================================== */
|
||||
|
||||
@@ -74,6 +74,10 @@
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.sidebar-brand-gold {
|
||||
color: var(--sidebar-group-title);
|
||||
}
|
||||
|
||||
.sidebar-mode-toggle:hover {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* ==========================================================================
|
||||
Nexus Terminal — Split Pane Layout
|
||||
Alchementary Link — Split Pane Layout
|
||||
Draggable split panes within session panels for multi-terminal views.
|
||||
========================================================================== */
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* ==========================================================================
|
||||
Nexus Terminal — Tab Bar & Tabs
|
||||
Alchementary Link — Tab Bar & Tabs
|
||||
Compact session tabs with status indicators, protocol badges, and
|
||||
close / pin controls.
|
||||
========================================================================== */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* ==========================================================================
|
||||
Nexus Terminal — Terminal Integration
|
||||
Alchementary Link — Terminal Integration
|
||||
xterm.js container, header info strip, RAW mode views, and send bar.
|
||||
========================================================================== */
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* ==========================================================================
|
||||
Nexus Terminal — Design Tokens
|
||||
Alchementary Link — Design Tokens
|
||||
Central palette, spacing, typography, and layout constants.
|
||||
All values consumed via var(--token-name) throughout the application.
|
||||
========================================================================== */
|
||||
|
||||
+56
-56
@@ -1,6 +1,6 @@
|
||||
(function () {
|
||||
/**
|
||||
* Nexus Terminal — main application entry point.
|
||||
* Alchementary Link — main application entry point.
|
||||
* Initialises the application shell, loads profiles, and sets up event listeners.
|
||||
*/
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
TriggerEngine,
|
||||
LeviathanWorkbench,
|
||||
stateLabel,
|
||||
} = window.NexusTerminal;
|
||||
} = window.AlchementaryLink;
|
||||
|
||||
class App {
|
||||
constructor() {
|
||||
@@ -35,7 +35,7 @@
|
||||
this.terminals = new TerminalManager(this);
|
||||
this.profileEditor = new ProfileEditor(this);
|
||||
this.rawViewer = new RawViewer(this);
|
||||
window.NexusTerminal.IRCViewer?.setApp(this);
|
||||
window.AlchementaryLink.IRCViewer?.setApp(this);
|
||||
this.settings = new Settings(this);
|
||||
this.commandPalette = new CommandPalette(this);
|
||||
this.secretPrompt = new SecretPrompt(this);
|
||||
@@ -54,7 +54,7 @@
|
||||
this._latencyGeneration = 0;
|
||||
this._latencyPingInFlight = false;
|
||||
this.updateInfo = null;
|
||||
this.mode = "nexus";
|
||||
this.mode = "link";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,10 +92,10 @@
|
||||
const start = Date.now();
|
||||
const check = () => {
|
||||
const hasBridge =
|
||||
typeof window.NexusTerminal?.hasWailsService === "function"
|
||||
? window.NexusTerminal.hasWailsService()
|
||||
: Boolean(window.NexusTerminal?.wailsService && window.runtime);
|
||||
if (window.NexusTerminal?.wailsService && hasBridge) {
|
||||
typeof window.AlchementaryLink?.hasWailsService === "function"
|
||||
? window.AlchementaryLink.hasWailsService()
|
||||
: Boolean(window.AlchementaryLink?.wailsService && window.runtime);
|
||||
if (window.AlchementaryLink?.wailsService && hasBridge) {
|
||||
resolve();
|
||||
} else if (Date.now() - start < maxWait) {
|
||||
setTimeout(check, 50);
|
||||
@@ -108,7 +108,7 @@
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
Promise.resolve(window.NexusTerminal?.wailsServiceReady)
|
||||
Promise.resolve(window.AlchementaryLink?.wailsServiceReady)
|
||||
.catch(() => null)
|
||||
.finally(check);
|
||||
});
|
||||
@@ -121,7 +121,7 @@
|
||||
*/
|
||||
async loadProfiles() {
|
||||
try {
|
||||
const profiles = await window.NexusTerminal.wailsService.ListProfiles();
|
||||
const profiles = await window.AlchementaryLink.wailsService.ListProfiles();
|
||||
this.state.setProfiles(profiles || []);
|
||||
this.sidebar.render();
|
||||
} catch (err) {
|
||||
@@ -134,7 +134,7 @@
|
||||
*/
|
||||
async loadPreferences() {
|
||||
try {
|
||||
const prefs = await window.NexusTerminal.wailsService.GetPreferences();
|
||||
const prefs = await window.AlchementaryLink.wailsService.GetPreferences();
|
||||
this.state.setPreferences(prefs || {});
|
||||
this._applyPreferences();
|
||||
} catch (err) {
|
||||
@@ -149,10 +149,10 @@
|
||||
async checkSecretStore() {
|
||||
try {
|
||||
const initialised =
|
||||
await window.NexusTerminal.wailsService.IsSecretStoreInitialised();
|
||||
await window.AlchementaryLink.wailsService.IsSecretStoreInitialised();
|
||||
this.state.setSecretStoreInitialised(!!initialised);
|
||||
if (initialised) {
|
||||
const unlocked = await window.NexusTerminal.wailsService.IsSecretStoreUnlocked();
|
||||
const unlocked = await window.AlchementaryLink.wailsService.IsSecretStoreUnlocked();
|
||||
this.state.setSecretStoreState(!!unlocked);
|
||||
} else {
|
||||
this.state.setSecretStoreState(false);
|
||||
@@ -240,7 +240,7 @@
|
||||
if (entry) {
|
||||
const { cols, rows } = entry.term;
|
||||
try {
|
||||
window.NexusTerminal.wailsService.ResizeSession(
|
||||
window.AlchementaryLink.wailsService.ResizeSession(
|
||||
payload.sessionID,
|
||||
cols,
|
||||
rows,
|
||||
@@ -256,7 +256,7 @@
|
||||
setTimeout(fit, 50);
|
||||
setTimeout(fit, 300);
|
||||
setTimeout(fit, 800);
|
||||
setTimeout(() => window.NexusTerminal.IRCViewer?.fit(payload.sessionID), 50);
|
||||
setTimeout(() => window.AlchementaryLink.IRCViewer?.fit(payload.sessionID), 50);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -275,7 +275,7 @@
|
||||
|
||||
// GitHub repository data
|
||||
window.runtime.EventsOn("session:github:repos", (payload) => {
|
||||
window.NexusTerminal.GitHubViewer.updateRepos(
|
||||
window.AlchementaryLink.GitHubViewer.updateRepos(
|
||||
payload.sessionID,
|
||||
payload.login,
|
||||
payload.repos,
|
||||
@@ -284,7 +284,7 @@
|
||||
|
||||
// GitHub refresh errors
|
||||
window.runtime.EventsOn("session:github:error", (payload) => {
|
||||
window.NexusTerminal.GitHubViewer.showError(
|
||||
window.AlchementaryLink.GitHubViewer.showError(
|
||||
payload.sessionID,
|
||||
payload.error,
|
||||
);
|
||||
@@ -292,7 +292,7 @@
|
||||
|
||||
// GitHub status updates
|
||||
window.runtime.EventsOn("session:github:status", (payload) => {
|
||||
window.NexusTerminal.GitHubViewer.setStatus(
|
||||
window.AlchementaryLink.GitHubViewer.setStatus(
|
||||
payload.sessionID,
|
||||
payload.status,
|
||||
);
|
||||
@@ -300,7 +300,7 @@
|
||||
|
||||
// VNC WebSocket proxy ready — connect the viewer
|
||||
window.runtime.EventsOn("session:vnc:ready", (payload) => {
|
||||
window.NexusTerminal.VNCViewer.connect(
|
||||
window.AlchementaryLink.VNCViewer.connect(
|
||||
payload.sessionID,
|
||||
payload.wsURL,
|
||||
);
|
||||
@@ -308,7 +308,7 @@
|
||||
|
||||
// IRC events
|
||||
window.runtime.EventsOn("session:irc:event", (payload) => {
|
||||
window.NexusTerminal.IRCViewer.handleEvent(payload);
|
||||
window.AlchementaryLink.IRCViewer.handleEvent(payload);
|
||||
this.tabs.markActivity(payload.sessionID);
|
||||
});
|
||||
|
||||
@@ -462,9 +462,9 @@
|
||||
() => this.recordings.cleanup(sessionId),
|
||||
() => this.terminals.destroyTerminal(sessionId),
|
||||
() => this.rawViewer.destroyViewer(sessionId),
|
||||
() => window.NexusTerminal.GitHubViewer?.destroyViewer(sessionId),
|
||||
() => window.NexusTerminal.VNCViewer?.destroyViewer(sessionId),
|
||||
() => window.NexusTerminal.IRCViewer?.destroyViewer(sessionId),
|
||||
() => window.AlchementaryLink.GitHubViewer?.destroyViewer(sessionId),
|
||||
() => window.AlchementaryLink.VNCViewer?.destroyViewer(sessionId),
|
||||
() => window.AlchementaryLink.IRCViewer?.destroyViewer(sessionId),
|
||||
() => this.leviathan?.destroyPanel(sessionId),
|
||||
];
|
||||
for (const cleanup of localCleanups) {
|
||||
@@ -486,7 +486,7 @@
|
||||
let backendCleanup = Promise.resolve();
|
||||
try {
|
||||
backendCleanup = Promise.resolve(
|
||||
window.NexusTerminal.wailsService.DisconnectSession(sessionId),
|
||||
window.AlchementaryLink.wailsService.DisconnectSession(sessionId),
|
||||
);
|
||||
} catch (_) {
|
||||
// Ignore failures; all local resources have already been released.
|
||||
@@ -646,7 +646,7 @@
|
||||
this._bindClick("btn-recordings", () => this.recordings.openBrowser());
|
||||
this._bindClick("btn-new-tab", () => this.profileEditor.openNew());
|
||||
this._bindClick("btn-broadcast", () => this.toggleBroadcast());
|
||||
this._bindClick("mode-nexus", () => this.setMode("nexus"));
|
||||
this._bindClick("mode-link", () => this.setMode("link"));
|
||||
this._bindClick("mode-leviathan", () => this.setMode("leviathan"));
|
||||
|
||||
// Wire the "Local Machine" sidebar button
|
||||
@@ -679,7 +679,7 @@
|
||||
}
|
||||
|
||||
// Window resize → re-fit active terminal (debounced)
|
||||
const { debounce } = window.NexusTerminal;
|
||||
const { debounce } = window.AlchementaryLink;
|
||||
window.addEventListener(
|
||||
"resize",
|
||||
debounce(() => {
|
||||
@@ -698,21 +698,21 @@
|
||||
// ── App mode switching ─────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Switches the sidebar between Nexus profiles and Leviathan tools.
|
||||
* @param {"nexus"|"leviathan"} mode
|
||||
* Switches the sidebar between Link profiles and Leviathan tools.
|
||||
* @param {"link"|"leviathan"} mode
|
||||
*/
|
||||
setMode(mode) {
|
||||
const leviathanEnabled =
|
||||
this.state.preferences?.leviathanEnabled === true;
|
||||
this.mode =
|
||||
mode === "leviathan" && leviathanEnabled ? "leviathan" : "nexus";
|
||||
mode === "leviathan" && leviathanEnabled ? "leviathan" : "link";
|
||||
const root = document.getElementById("app");
|
||||
if (root) {
|
||||
root.classList.toggle("mode-leviathan", this.mode === "leviathan");
|
||||
}
|
||||
document
|
||||
.getElementById("mode-nexus")
|
||||
?.classList.toggle("is-active", this.mode === "nexus");
|
||||
.getElementById("mode-link")
|
||||
?.classList.toggle("is-active", this.mode === "link");
|
||||
document
|
||||
.getElementById("mode-leviathan")
|
||||
?.classList.toggle("is-active", this.mode === "leviathan");
|
||||
@@ -769,7 +769,7 @@
|
||||
* Opens a modal to select which sessions receive broadcast input.
|
||||
*/
|
||||
selectBroadcastTargets() {
|
||||
const { createElement, escapeHtml } = window.NexusTerminal;
|
||||
const { createElement, escapeHtml } = window.AlchementaryLink;
|
||||
const sessions = this.state
|
||||
.getAllSessions()
|
||||
.filter(
|
||||
@@ -925,7 +925,7 @@
|
||||
|
||||
try {
|
||||
const sessionInfo =
|
||||
await window.NexusTerminal.wailsService.ConnectSession(profileId);
|
||||
await window.AlchementaryLink.wailsService.ConnectSession(profileId);
|
||||
this.state.addSession(sessionInfo);
|
||||
this.tabs.addTab(sessionInfo);
|
||||
|
||||
@@ -936,7 +936,7 @@
|
||||
} else if (profile.protocol === "github") {
|
||||
const container = document.getElementById(panelId);
|
||||
if (container) {
|
||||
window.NexusTerminal.GitHubViewer.createViewer(
|
||||
window.AlchementaryLink.GitHubViewer.createViewer(
|
||||
sessionInfo.id,
|
||||
container,
|
||||
profile.displayName || profile.host,
|
||||
@@ -945,7 +945,7 @@
|
||||
} else if (profile.protocol === "vnc") {
|
||||
const container = document.getElementById(panelId);
|
||||
if (container) {
|
||||
window.NexusTerminal.VNCViewer.createViewer(
|
||||
window.AlchementaryLink.VNCViewer.createViewer(
|
||||
sessionInfo.id,
|
||||
container,
|
||||
profile,
|
||||
@@ -954,7 +954,7 @@
|
||||
} else if (profile.protocol === "irc") {
|
||||
const container = document.getElementById(panelId);
|
||||
if (container) {
|
||||
window.NexusTerminal.IRCViewer.createViewer(
|
||||
window.AlchementaryLink.IRCViewer.createViewer(
|
||||
sessionInfo.id,
|
||||
container,
|
||||
profile,
|
||||
@@ -977,7 +977,7 @@
|
||||
*/
|
||||
async connectLocal() {
|
||||
try {
|
||||
const sessionInfo = await window.NexusTerminal.wailsService.ConnectLocal();
|
||||
const sessionInfo = await window.AlchementaryLink.wailsService.ConnectLocal();
|
||||
this.state.addSession(sessionInfo);
|
||||
this.tabs.addTab(sessionInfo);
|
||||
|
||||
@@ -1000,7 +1000,7 @@
|
||||
* Opens a quick connect dialog for ad-hoc connections without saving a profile.
|
||||
*/
|
||||
async quickConnect() {
|
||||
const createElement = window.NexusTerminal.createElement;
|
||||
const createElement = window.AlchementaryLink.createElement;
|
||||
const result = await new Promise((resolve) => {
|
||||
const backdrop = createElement("div", "modal-backdrop");
|
||||
const modal = createElement("div", "modal modal-sm");
|
||||
@@ -1033,7 +1033,7 @@
|
||||
</div>
|
||||
<div class="form-group qc-nick-group" style="display:none">
|
||||
<label class="form-label">Nick</label>
|
||||
<input class="form-input qc-nick" type="text" value="nexus" />
|
||||
<input class="form-input qc-nick" type="text" value="link" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
@@ -1115,12 +1115,12 @@
|
||||
};
|
||||
if (result.protocol === "irc") {
|
||||
profile.irc = {
|
||||
nick: result.nick || "nexus",
|
||||
realName: "Nexus Terminal",
|
||||
nick: result.nick || "link",
|
||||
realName: "Alchementary Link",
|
||||
autoJoinRooms: [],
|
||||
};
|
||||
}
|
||||
const sessionInfo = await window.NexusTerminal.wailsService.ConnectQuick(profile);
|
||||
const sessionInfo = await window.AlchementaryLink.wailsService.ConnectQuick(profile);
|
||||
this.state.addSession(sessionInfo);
|
||||
this.tabs.addTab(sessionInfo);
|
||||
|
||||
@@ -1133,7 +1133,7 @@
|
||||
} else if (result.protocol === "irc") {
|
||||
const container = document.getElementById(panelId);
|
||||
if (container) {
|
||||
window.NexusTerminal.IRCViewer.createViewer(
|
||||
window.AlchementaryLink.IRCViewer.createViewer(
|
||||
sessionInfo.id,
|
||||
container,
|
||||
profile,
|
||||
@@ -1159,8 +1159,8 @@
|
||||
* @private
|
||||
*/
|
||||
_confirmReuseSession(profile, session) {
|
||||
const createElement = window.NexusTerminal.createElement;
|
||||
const label = window.NexusTerminal.escapeHtml(
|
||||
const createElement = window.AlchementaryLink.createElement;
|
||||
const label = window.AlchementaryLink.escapeHtml(
|
||||
(profile.displayName || profile.host || "Untitled").trim(),
|
||||
);
|
||||
const stateLabel =
|
||||
@@ -1240,7 +1240,7 @@
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
confirmDirectVNCProfile(profile, action) {
|
||||
const { createElement, createModalShell } = window.NexusTerminal;
|
||||
const { createElement, createModalShell } = window.AlchementaryLink;
|
||||
const label = (
|
||||
profile.displayName ||
|
||||
profile.vnc?.vncHost ||
|
||||
@@ -1391,7 +1391,7 @@
|
||||
|
||||
const doPing = async () => {
|
||||
if (!isCurrent()) return;
|
||||
if (!window.NexusTerminal?.wailsService) {
|
||||
if (!window.AlchementaryLink?.wailsService) {
|
||||
scheduleNext();
|
||||
return;
|
||||
}
|
||||
@@ -1399,7 +1399,7 @@
|
||||
this._latencyPingInFlight = true;
|
||||
let succeeded = false;
|
||||
try {
|
||||
const ms = await window.NexusTerminal.wailsService.PingSession(sessionID);
|
||||
const ms = await window.AlchementaryLink.wailsService.PingSession(sessionID);
|
||||
if (!isCurrent()) return;
|
||||
succeeded = true;
|
||||
const el = document.getElementById("status-latency");
|
||||
@@ -1443,12 +1443,12 @@
|
||||
}
|
||||
|
||||
async checkForUpdates(silent) {
|
||||
if (!window.NexusTerminal?.wailsService) {
|
||||
if (!window.AlchementaryLink?.wailsService) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const info = await window.NexusTerminal.wailsService.CheckForUpdates();
|
||||
const info = await window.AlchementaryLink.wailsService.CheckForUpdates();
|
||||
this.setUpdateInfo(info);
|
||||
if (!silent && info && info.available) {
|
||||
this.notifications.info(
|
||||
@@ -1508,7 +1508,7 @@
|
||||
leviathanButton.hidden = !leviathanEnabled;
|
||||
}
|
||||
if (!leviathanEnabled && this.mode === "leviathan") {
|
||||
this.setMode("nexus");
|
||||
this.setMode("link");
|
||||
}
|
||||
|
||||
// Refit any visible terminal after geometry changes.
|
||||
@@ -1600,10 +1600,10 @@
|
||||
* @private
|
||||
*/
|
||||
_showGuidedErrorModal(title, label, message, onAction, actions) {
|
||||
const createElement = window.NexusTerminal.createElement;
|
||||
const createElement = window.AlchementaryLink.createElement;
|
||||
const backdrop = createElement("div", "modal-backdrop");
|
||||
const modal = createElement("div", "modal modal-sm");
|
||||
const safeLabel = window.NexusTerminal.escapeHtml(label || "Session");
|
||||
const safeLabel = window.AlchementaryLink.escapeHtml(label || "Session");
|
||||
|
||||
modal.innerHTML = `
|
||||
<div class="modal-header">
|
||||
@@ -1615,7 +1615,7 @@
|
||||
<strong>${safeLabel}</strong>
|
||||
</p>
|
||||
<p style="font-size:var(--text-sm);color:var(--text-secondary);white-space:pre-line;">
|
||||
${window.NexusTerminal.escapeHtml(message)}
|
||||
${window.AlchementaryLink.escapeHtml(message)}
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer"></div>
|
||||
@@ -1888,7 +1888,7 @@
|
||||
* @private
|
||||
*/
|
||||
_inputPrompt(title, label, placeholder) {
|
||||
const createElement = window.NexusTerminal.createElement;
|
||||
const createElement = window.AlchementaryLink.createElement;
|
||||
return new Promise((resolve) => {
|
||||
const backdrop = createElement("div", "modal-backdrop");
|
||||
const modal = createElement("div", "modal modal-sm");
|
||||
@@ -1943,7 +1943,7 @@
|
||||
|
||||
// ── Bootstrap ────────────────────────────────────────────────────────
|
||||
|
||||
window.NexusTerminal.App = App;
|
||||
window.AlchementaryLink.App = App;
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
try {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
(function () {
|
||||
/**
|
||||
* Nexus Terminal — command palette (Ctrl+K).
|
||||
* Alchementary Link — command palette (Ctrl+K).
|
||||
* Provides a searchable overlay for quick access to application commands,
|
||||
* profile connections, and utility actions.
|
||||
*/
|
||||
|
||||
const { createElement, escapeHtml, fuzzyMatch, $ } = window.NexusTerminal;
|
||||
const { createElement, escapeHtml, fuzzyMatch, $ } = window.AlchementaryLink;
|
||||
|
||||
class CommandPalette {
|
||||
/**
|
||||
@@ -172,7 +172,7 @@
|
||||
section: "Security",
|
||||
action: async () => {
|
||||
try {
|
||||
await window.NexusTerminal.wailsService.LockSecretStore();
|
||||
await window.AlchementaryLink.wailsService.LockSecretStore();
|
||||
this.app.state.setSecretStoreState(false);
|
||||
this.app.notifications.info("Secret store locked");
|
||||
} catch (err) {
|
||||
@@ -189,7 +189,7 @@
|
||||
try {
|
||||
const pw =
|
||||
await this.app.secretPrompt.promptMasterPassword(false);
|
||||
await window.NexusTerminal.wailsService.UnlockSecretStore(pw);
|
||||
await window.AlchementaryLink.wailsService.UnlockSecretStore(pw);
|
||||
this.app.state.setSecretStoreState(true);
|
||||
this.app.notifications.success("Secret store unlocked");
|
||||
} catch (err) {
|
||||
@@ -441,6 +441,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
window.NexusTerminal = window.NexusTerminal || {};
|
||||
window.NexusTerminal.CommandPalette = CommandPalette;
|
||||
window.AlchementaryLink = window.AlchementaryLink || {};
|
||||
window.AlchementaryLink.CommandPalette = CommandPalette;
|
||||
})();
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
(function () {
|
||||
/**
|
||||
* Nexus Terminal — GitHub repository viewer.
|
||||
* Alchementary Link — GitHub repository viewer.
|
||||
* Renders a scrollable list of repositories grouped by owner/organisation,
|
||||
* sorted by last commit date (newest first). Automatically refreshes via
|
||||
* Wails events emitted by the Go backend.
|
||||
*/
|
||||
|
||||
const { createElement, escapeHtml, relativeTime, $ } = window.NexusTerminal;
|
||||
const { createElement, escapeHtml, relativeTime, $ } = window.AlchementaryLink;
|
||||
|
||||
class GitHubViewer {
|
||||
constructor() {
|
||||
@@ -425,7 +425,7 @@
|
||||
this._statusEl.textContent = `Downloading ${repo.fullName}...`;
|
||||
|
||||
try {
|
||||
await window.NexusTerminal.wailsService.DownloadGitHubRepoArchive(
|
||||
await window.AlchementaryLink.wailsService.DownloadGitHubRepoArchive(
|
||||
this.sessionID,
|
||||
repo.fullName,
|
||||
ref,
|
||||
@@ -441,7 +441,7 @@
|
||||
if (!this._statusEl) return;
|
||||
this._statusEl.textContent = "Refreshing…";
|
||||
try {
|
||||
await window.NexusTerminal.wailsService.RefreshGitHubRepos(this.sessionID);
|
||||
await window.AlchementaryLink.wailsService.RefreshGitHubRepos(this.sessionID);
|
||||
} catch (err) {
|
||||
this.showError(err.message || "Refresh failed");
|
||||
}
|
||||
@@ -492,6 +492,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
window.NexusTerminal = window.NexusTerminal || {};
|
||||
window.NexusTerminal.GitHubViewer = new GitHubViewer();
|
||||
window.AlchementaryLink = window.AlchementaryLink || {};
|
||||
window.AlchementaryLink.GitHubViewer = new GitHubViewer();
|
||||
})();
|
||||
|
||||
+11
-11
@@ -1,13 +1,13 @@
|
||||
(function () {
|
||||
/**
|
||||
* Nexus Terminal - IRC viewer.
|
||||
* Alchementary Link - IRC viewer.
|
||||
* Uses xterm.js as the chat stream, with room subtabs, a composer, and
|
||||
* room/user sidebars.
|
||||
*/
|
||||
|
||||
/* global Terminal, FitAddon, SearchAddon, WebLinksAddon */
|
||||
|
||||
const { createElement } = window.NexusTerminal;
|
||||
const { createElement } = window.AlchementaryLink;
|
||||
const SERVER_TARGET = "server";
|
||||
const MAX_LINES_PER_TARGET = 2000;
|
||||
const MAX_LISTED_ROOMS = 200;
|
||||
@@ -415,7 +415,7 @@
|
||||
const room = joinInput.value.trim();
|
||||
if (!room) return;
|
||||
try {
|
||||
await window.NexusTerminal.wailsService.IRCJoin(this.sessionID, room);
|
||||
await window.AlchementaryLink.wailsService.IRCJoin(this.sessionID, room);
|
||||
joinInput.value = "";
|
||||
} catch (err) {
|
||||
this._notifyError("Join failed: " + err);
|
||||
@@ -439,7 +439,7 @@
|
||||
this.roomList = [];
|
||||
this._renderRoomList("Loading rooms...");
|
||||
try {
|
||||
await window.NexusTerminal.wailsService.IRCListRooms(this.sessionID);
|
||||
await window.AlchementaryLink.wailsService.IRCListRooms(this.sessionID);
|
||||
} catch (err) {
|
||||
this._renderRoomList("Room list failed");
|
||||
this._notifyError("Room list failed: " + err);
|
||||
@@ -476,7 +476,7 @@
|
||||
const themeName =
|
||||
(this.app?.state?.preferences || {}).terminalTheme || "charcoal";
|
||||
this.term = new Terminal({
|
||||
theme: window.NexusTerminal.getTerminalTheme(themeName),
|
||||
theme: window.AlchementaryLink.getTerminalTheme(themeName),
|
||||
fontFamily:
|
||||
this.profile.terminalFontFamily ||
|
||||
'JetBrains Mono, Menlo, Monaco, Consolas, "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", monospace',
|
||||
@@ -668,7 +668,7 @@
|
||||
if (!text.trim()) return;
|
||||
this.inputEl.value = "";
|
||||
try {
|
||||
await window.NexusTerminal.wailsService.IRCHandleInput(
|
||||
await window.AlchementaryLink.wailsService.IRCHandleInput(
|
||||
this.sessionID,
|
||||
this.activeTarget,
|
||||
text,
|
||||
@@ -686,7 +686,7 @@
|
||||
async _joinRoom(room) {
|
||||
if (!room) return;
|
||||
try {
|
||||
await window.NexusTerminal.wailsService.IRCJoin(this.sessionID, room);
|
||||
await window.AlchementaryLink.wailsService.IRCJoin(this.sessionID, room);
|
||||
} catch (err) {
|
||||
this._notifyError("Join failed: " + err);
|
||||
}
|
||||
@@ -694,7 +694,7 @@
|
||||
|
||||
async _refreshJoinedRooms() {
|
||||
try {
|
||||
const rooms = await window.NexusTerminal.wailsService.IRCRooms(this.sessionID);
|
||||
const rooms = await window.AlchementaryLink.wailsService.IRCRooms(this.sessionID);
|
||||
if (this.destroyed) return;
|
||||
this.handleEvent({ type: "rooms", rooms: Array.isArray(rooms) ? rooms : [] });
|
||||
} catch (_) {
|
||||
@@ -785,11 +785,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
window.NexusTerminal = window.NexusTerminal || {};
|
||||
window.NexusTerminal.IRCFormatting = Object.freeze({
|
||||
window.AlchementaryLink = window.AlchementaryLink || {};
|
||||
window.AlchementaryLink.IRCFormatting = Object.freeze({
|
||||
formatEvent: formatIRCEvent,
|
||||
formatText: formatIRCText,
|
||||
plainText: plainIRCText,
|
||||
});
|
||||
window.NexusTerminal.IRCViewer = new IRCViewer();
|
||||
window.AlchementaryLink.IRCViewer = new IRCViewer();
|
||||
})();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
(function () {
|
||||
const { createElement, escapeHtml, formatBytes, generateId } =
|
||||
window.NexusTerminal;
|
||||
window.AlchementaryLink;
|
||||
|
||||
const tools = [
|
||||
{
|
||||
@@ -116,7 +116,7 @@
|
||||
|
||||
async loadFile() {
|
||||
try {
|
||||
const payload = await window.NexusTerminal.wailsService.LeviathanOpenFile();
|
||||
const payload = await window.AlchementaryLink.wailsService.LeviathanOpenFile();
|
||||
this.applyState(payload);
|
||||
this.refreshOpenHexTabs();
|
||||
} catch (err) {
|
||||
@@ -127,9 +127,9 @@
|
||||
|
||||
async saveFile() {
|
||||
try {
|
||||
const path = await window.NexusTerminal.wailsService.LeviathanSaveWorking();
|
||||
const path = await window.AlchementaryLink.wailsService.LeviathanSaveWorking();
|
||||
if (path) this.app.notifications.success("Saved working copy");
|
||||
const state = await window.NexusTerminal.wailsService.LeviathanState();
|
||||
const state = await window.AlchementaryLink.wailsService.LeviathanState();
|
||||
this.applyState(state);
|
||||
} catch (err) {
|
||||
this.app.notifications.error("Save failed: " + err);
|
||||
@@ -231,7 +231,7 @@
|
||||
const load = async () => {
|
||||
try {
|
||||
status.textContent = "Loading hex...";
|
||||
const result = await window.NexusTerminal.wailsService.LeviathanRunTool("hex", {
|
||||
const result = await window.AlchementaryLink.wailsService.LeviathanRunTool("hex", {
|
||||
start: Number(start.input.value || 0),
|
||||
bytes: Number(bytes.input.value || 2048),
|
||||
width: Number(width.input.value || 16),
|
||||
@@ -288,7 +288,7 @@
|
||||
length: Number(length.input.value || -1),
|
||||
max: Number(max.input.value || 1000),
|
||||
};
|
||||
const out = await window.NexusTerminal.wailsService.LeviathanRunTool("search", payload);
|
||||
const out = await window.AlchementaryLink.wailsService.LeviathanRunTool("search", payload);
|
||||
this.lastResults.set(sessionId, out);
|
||||
this._renderResult(summary, results, out, "search");
|
||||
} catch (err) {
|
||||
@@ -304,7 +304,7 @@
|
||||
const out = this.lastResults.get(sessionId);
|
||||
if (!out) return;
|
||||
try {
|
||||
await window.NexusTerminal.wailsService.LeviathanExportJSON("search.json", out);
|
||||
await window.AlchementaryLink.wailsService.LeviathanExportJSON("search.json", out);
|
||||
this.app.notifications.success("Exported JSON");
|
||||
} catch (err) {
|
||||
this.app.notifications.error("Export failed: " + err);
|
||||
@@ -339,7 +339,7 @@
|
||||
let selectedImagePath = "";
|
||||
chooseImage.addEventListener("click", async () => {
|
||||
try {
|
||||
const path = await window.NexusTerminal.wailsService.SelectFile("Select Image to Insert", "");
|
||||
const path = await window.AlchementaryLink.wailsService.SelectFile("Select Image to Insert", "");
|
||||
if (!path) return;
|
||||
selectedImagePath = path;
|
||||
imagePath.textContent = path;
|
||||
@@ -369,7 +369,7 @@
|
||||
if (!selectedImagePath) throw new Error("Select an image to insert first.");
|
||||
payload.imagePath = selectedImagePath;
|
||||
}
|
||||
const out = await window.NexusTerminal.wailsService.LeviathanRunTool(toolId, payload);
|
||||
const out = await window.AlchementaryLink.wailsService.LeviathanRunTool(toolId, payload);
|
||||
this.lastResults.set(sessionId, out);
|
||||
summary.innerHTML = "";
|
||||
results.innerHTML = "";
|
||||
@@ -416,7 +416,7 @@
|
||||
results.appendChild(createElement("div", "leviathan-empty", { textContent: "Rendering spectrogram..." }));
|
||||
const payload = {};
|
||||
for (const field of fields) payload[field.key] = this._fieldValue(field);
|
||||
const out = await window.NexusTerminal.wailsService.LeviathanRunTool("spectrogram-view", payload);
|
||||
const out = await window.AlchementaryLink.wailsService.LeviathanRunTool("spectrogram-view", payload);
|
||||
this.lastResults.set(sessionId, out);
|
||||
summary.innerHTML = "";
|
||||
results.innerHTML = "";
|
||||
@@ -442,7 +442,7 @@
|
||||
const out = this.lastResults.get(sessionId);
|
||||
if (!out) return;
|
||||
try {
|
||||
await window.NexusTerminal.wailsService.LeviathanExportJSON("spectrogram-view.json", out);
|
||||
await window.AlchementaryLink.wailsService.LeviathanExportJSON("spectrogram-view.json", out);
|
||||
this.app.notifications.success("Exported JSON");
|
||||
} catch (err) {
|
||||
this.app.notifications.error("Export failed: " + err);
|
||||
@@ -480,7 +480,7 @@
|
||||
running = true;
|
||||
try {
|
||||
if (tool.id === "diff" && !this.state?.compare) {
|
||||
const compare = await window.NexusTerminal.wailsService.LeviathanOpenCompare();
|
||||
const compare = await window.AlchementaryLink.wailsService.LeviathanOpenCompare();
|
||||
this.applyState(compare);
|
||||
}
|
||||
results.innerHTML = "";
|
||||
@@ -489,7 +489,7 @@
|
||||
for (const field of fields) {
|
||||
payload[field.key] = this._fieldValue(field);
|
||||
}
|
||||
const out = await window.NexusTerminal.wailsService.LeviathanRunTool(tool.id, payload);
|
||||
const out = await window.AlchementaryLink.wailsService.LeviathanRunTool(tool.id, payload);
|
||||
this.lastResults.set(sessionId, out);
|
||||
this._renderResult(summary, results, out, tool.id);
|
||||
if (out && out.state) this.applyState(out.state);
|
||||
@@ -505,7 +505,7 @@
|
||||
const out = this.lastResults.get(sessionId);
|
||||
if (!out) return;
|
||||
try {
|
||||
await window.NexusTerminal.wailsService.LeviathanExportJSON(`${tool.id}.json`, out);
|
||||
await window.AlchementaryLink.wailsService.LeviathanExportJSON(`${tool.id}.json`, out);
|
||||
this.app.notifications.success("Exported JSON");
|
||||
} catch (err) {
|
||||
this.app.notifications.error("Export failed: " + err);
|
||||
@@ -697,7 +697,7 @@
|
||||
const offset = Number(cell.dataset.offset || 0);
|
||||
cell.dataset.pendingHex = hex;
|
||||
cell.classList.add("is-saving");
|
||||
window.NexusTerminal.wailsService
|
||||
window.AlchementaryLink.wailsService
|
||||
.LeviathanSetByte(offset, hex)
|
||||
.then((payload) => {
|
||||
if (cell.dataset.pendingHex === hex) {
|
||||
@@ -1125,7 +1125,7 @@
|
||||
}
|
||||
const name = typeof nameBuilder === "function" ? nameBuilder(row) : this._foundFileName(row, "found");
|
||||
try {
|
||||
const path = await window.NexusTerminal.wailsService.LeviathanExportRange(name, range.offset, range.length);
|
||||
const path = await window.AlchementaryLink.wailsService.LeviathanExportRange(name, range.offset, range.length);
|
||||
if (path) this.app.notifications.success("Exported " + path);
|
||||
} catch (err) {
|
||||
this.app.notifications.error("Export failed: " + err);
|
||||
@@ -1164,7 +1164,7 @@
|
||||
}
|
||||
|
||||
_stateAction(method, sessionId) {
|
||||
return window.NexusTerminal.wailsService[method]()
|
||||
return window.AlchementaryLink.wailsService[method]()
|
||||
.then((state) => {
|
||||
this.applyState(state);
|
||||
const session = this.app.state.getSession(sessionId);
|
||||
@@ -1299,7 +1299,7 @@
|
||||
const search = async () => {
|
||||
try {
|
||||
status.textContent = "Searching...";
|
||||
const out = await window.NexusTerminal.wailsService.LeviathanRunTool("search", {
|
||||
const out = await window.AlchementaryLink.wailsService.LeviathanRunTool("search", {
|
||||
mode: mode.input.value,
|
||||
query: query.input.value,
|
||||
start: 0,
|
||||
@@ -1435,7 +1435,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
window.NexusTerminal = window.NexusTerminal || {};
|
||||
window.NexusTerminal.LeviathanWorkbench = LeviathanWorkbench;
|
||||
window.NexusTerminal.LeviathanTools = tools;
|
||||
window.AlchementaryLink = window.AlchementaryLink || {};
|
||||
window.AlchementaryLink.LeviathanWorkbench = LeviathanWorkbench;
|
||||
window.AlchementaryLink.LeviathanTools = tools;
|
||||
})();
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
(function() {
|
||||
/**
|
||||
* Nexus Terminal — toast notification system.
|
||||
* Alchementary Link — toast notification system.
|
||||
* Provides non-blocking, auto-dismissing notifications that stack
|
||||
* in the top-right corner of the viewport.
|
||||
*/
|
||||
|
||||
const { createElement, escapeHtml } = window.NexusTerminal;
|
||||
const { createElement, escapeHtml } = window.AlchementaryLink;
|
||||
|
||||
/** Small SVG icons for each notification type (16×16 viewBox). */
|
||||
const ICONS = {
|
||||
@@ -95,7 +95,7 @@ class Notifications {
|
||||
}
|
||||
}
|
||||
|
||||
window.NexusTerminal = window.NexusTerminal || {};
|
||||
window.NexusTerminal.Notifications = Notifications;
|
||||
window.AlchementaryLink = window.AlchementaryLink || {};
|
||||
window.AlchementaryLink.Notifications = Notifications;
|
||||
|
||||
})();
|
||||
|
||||
+33
-33
@@ -1,11 +1,11 @@
|
||||
(function () {
|
||||
/**
|
||||
* Nexus Terminal — connection profile editor.
|
||||
* Alchementary Link — connection profile editor.
|
||||
* Renders a slide-out drawer for creating and editing connection profiles.
|
||||
* Dynamically switches form sections based on the selected protocol.
|
||||
*/
|
||||
|
||||
const { createElement, escapeHtml, generateId, $ } = window.NexusTerminal;
|
||||
const { createElement, escapeHtml, generateId, $ } = window.AlchementaryLink;
|
||||
|
||||
/**
|
||||
* Default values mirroring the Go backend defaults.
|
||||
@@ -90,8 +90,8 @@
|
||||
port: 6667,
|
||||
protocol: "irc",
|
||||
irc: {
|
||||
nick: "nexus",
|
||||
realName: "Nexus Terminal",
|
||||
nick: "link",
|
||||
realName: "Alchementary Link",
|
||||
useTLS: false,
|
||||
serverPasswordRequired: false,
|
||||
serverPasswordMode: "ask-every-time",
|
||||
@@ -467,10 +467,10 @@
|
||||
|
||||
_loadSSHKeyOptions() {
|
||||
if (this._sshKeyOptionsLoading || this._sshKeyOptionsLoaded) return;
|
||||
if (!window.NexusTerminal?.wailsService) return;
|
||||
if (!window.AlchementaryLink?.wailsService) return;
|
||||
|
||||
this._sshKeyOptionsLoading = true;
|
||||
window.NexusTerminal.wailsService.ListSSHKeys()
|
||||
window.AlchementaryLink.wailsService.ListSSHKeys()
|
||||
.then((keys) => {
|
||||
this._sshKeyOptions = Array.isArray(keys) ? keys : [];
|
||||
this._sshKeyOptionsLoaded = true;
|
||||
@@ -1424,7 +1424,7 @@
|
||||
const profileId = profile.id;
|
||||
if (profileId) {
|
||||
try {
|
||||
const code = await window.NexusTerminal.wailsService.GetTOTPCode(profileId);
|
||||
const code = await window.AlchementaryLink.wailsService.GetTOTPCode(profileId);
|
||||
testBtn.textContent = code;
|
||||
setTimeout(() => {
|
||||
testBtn.textContent = "Test";
|
||||
@@ -1454,7 +1454,7 @@
|
||||
const profileId = profile.id;
|
||||
const secret = secretInput.value.trim();
|
||||
if (!profileId) {
|
||||
NexusTerminal.Notifications.show(
|
||||
AlchementaryLink.Notifications.show(
|
||||
"Save the profile first, then set the TOTP secret.",
|
||||
"warning",
|
||||
);
|
||||
@@ -1463,10 +1463,10 @@
|
||||
if (!secret) {
|
||||
// Delete existing secret
|
||||
try {
|
||||
await window.NexusTerminal.wailsService.DeleteTOTPSecret(profileId);
|
||||
NexusTerminal.Notifications.show("TOTP secret removed.", "info");
|
||||
await window.AlchementaryLink.wailsService.DeleteTOTPSecret(profileId);
|
||||
AlchementaryLink.Notifications.show("TOTP secret removed.", "info");
|
||||
} catch (err) {
|
||||
NexusTerminal.Notifications.show(
|
||||
AlchementaryLink.Notifications.show(
|
||||
"Failed to remove TOTP secret.",
|
||||
"error",
|
||||
);
|
||||
@@ -1474,15 +1474,15 @@
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await window.NexusTerminal.wailsService.SetTOTPSecret(profileId, secret);
|
||||
NexusTerminal.Notifications.show(
|
||||
await window.AlchementaryLink.wailsService.SetTOTPSecret(profileId, secret);
|
||||
AlchementaryLink.Notifications.show(
|
||||
"TOTP secret saved securely.",
|
||||
"success",
|
||||
);
|
||||
secretInput.value = "";
|
||||
secretInput.placeholder = "••••••••• (secret saved)";
|
||||
} catch (err) {
|
||||
NexusTerminal.Notifications.show(
|
||||
AlchementaryLink.Notifications.show(
|
||||
"Failed to save TOTP secret: " + err,
|
||||
"error",
|
||||
);
|
||||
@@ -1504,12 +1504,12 @@
|
||||
const profileId = profile.id;
|
||||
if (!profileId) return;
|
||||
try {
|
||||
await window.NexusTerminal.wailsService.DeleteTOTPSecret(profileId);
|
||||
NexusTerminal.Notifications.show("TOTP secret removed.", "info");
|
||||
await window.AlchementaryLink.wailsService.DeleteTOTPSecret(profileId);
|
||||
AlchementaryLink.Notifications.show("TOTP secret removed.", "info");
|
||||
secretInput.placeholder =
|
||||
"Base32-encoded secret (e.g. from authenticator app setup)";
|
||||
} catch (err) {
|
||||
NexusTerminal.Notifications.show(
|
||||
AlchementaryLink.Notifications.show(
|
||||
"Failed to remove TOTP secret.",
|
||||
"error",
|
||||
);
|
||||
@@ -1528,7 +1528,7 @@
|
||||
if (profile.id) {
|
||||
(async () => {
|
||||
try {
|
||||
const has = await window.NexusTerminal.wailsService.HasTOTPSecret(profile.id);
|
||||
const has = await window.AlchementaryLink.wailsService.HasTOTPSecret(profile.id);
|
||||
if (has) {
|
||||
secretInput.placeholder = "••••••••• (secret saved)";
|
||||
}
|
||||
@@ -1972,8 +1972,8 @@
|
||||
});
|
||||
browseBtn.addEventListener("mousedown", (e) => e.preventDefault());
|
||||
browseBtn.addEventListener("click", () => {
|
||||
if (!window.NexusTerminal?.wailsService) return;
|
||||
window.NexusTerminal.wailsService.SelectFile("Select SSH Private Key", "*")
|
||||
if (!window.AlchementaryLink?.wailsService) return;
|
||||
window.AlchementaryLink.wailsService.SelectFile("Select SSH Private Key", "*")
|
||||
.then((path) => {
|
||||
if (path) input.value = path;
|
||||
})
|
||||
@@ -2040,8 +2040,8 @@
|
||||
|
||||
browseBtn.addEventListener("mousedown", (e) => e.preventDefault());
|
||||
browseBtn.addEventListener("click", () => {
|
||||
if (!window.NexusTerminal?.wailsService) return;
|
||||
window.NexusTerminal.wailsService.SelectFile("Select SSH Private Key", "*")
|
||||
if (!window.AlchementaryLink?.wailsService) return;
|
||||
window.AlchementaryLink.wailsService.SelectFile("Select SSH Private Key", "*")
|
||||
.then((path) => {
|
||||
if (path) keyInput.value = path;
|
||||
this._validateKeyPath(keyInput, error);
|
||||
@@ -2117,10 +2117,10 @@
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!window.NexusTerminal?.wailsService) {
|
||||
if (!window.AlchementaryLink?.wailsService) {
|
||||
return;
|
||||
}
|
||||
window.NexusTerminal.wailsService.FileExists(value)
|
||||
window.AlchementaryLink.wailsService.FileExists(value)
|
||||
.then((exists) => {
|
||||
if (!exists) {
|
||||
errorEl.textContent = "Key file not found.";
|
||||
@@ -2131,7 +2131,7 @@
|
||||
}
|
||||
return;
|
||||
}
|
||||
return window.NexusTerminal.wailsService.FileIsPrivateKey(value).then((isKey) => {
|
||||
return window.AlchementaryLink.wailsService.FileIsPrivateKey(value).then((isKey) => {
|
||||
if (!isKey) {
|
||||
errorEl.textContent = "File is not a recognised private key.";
|
||||
errorEl.style.display = "block";
|
||||
@@ -2488,8 +2488,8 @@
|
||||
// IRC-specific
|
||||
if (p.protocol === "irc") {
|
||||
if (!p.irc) p.irc = {};
|
||||
p.irc.nick = val("irc.nick") || "nexus";
|
||||
p.irc.realName = val("irc.realName") || "Nexus Terminal";
|
||||
p.irc.nick = val("irc.nick") || "link";
|
||||
p.irc.realName = val("irc.realName") || "Alchementary Link";
|
||||
p.irc.useTLS = val("irc.useTLS") || false;
|
||||
p.irc.serverPasswordRequired =
|
||||
val("irc.serverPasswordRequired") || false;
|
||||
@@ -2584,11 +2584,11 @@
|
||||
try {
|
||||
let saved;
|
||||
if (this._isNew) {
|
||||
saved = await window.NexusTerminal.wailsService.CreateProfile(this._profile);
|
||||
saved = await window.AlchementaryLink.wailsService.CreateProfile(this._profile);
|
||||
this.app.state.addProfile(saved);
|
||||
this.app.notifications.success("Profile created");
|
||||
} else {
|
||||
saved = await window.NexusTerminal.wailsService.UpdateProfile(this._profile);
|
||||
saved = await window.AlchementaryLink.wailsService.UpdateProfile(this._profile);
|
||||
this.app.state.updateProfile(saved);
|
||||
this.app.notifications.success("Profile updated");
|
||||
}
|
||||
@@ -2609,7 +2609,7 @@
|
||||
if (!ok) return;
|
||||
|
||||
try {
|
||||
await window.NexusTerminal.wailsService.DeleteProfile(this._profile.id);
|
||||
await window.AlchementaryLink.wailsService.DeleteProfile(this._profile.id);
|
||||
this.app.state.removeProfile(this._profile.id);
|
||||
this.app.sidebar.render();
|
||||
this.app.notifications.success("Profile deleted");
|
||||
@@ -2633,7 +2633,7 @@
|
||||
this.app.notifications.info(`Testing connection to ${host}:${port}…`);
|
||||
|
||||
try {
|
||||
const result = await window.NexusTerminal.wailsService.TestConnection(host, port);
|
||||
const result = await window.AlchementaryLink.wailsService.TestConnection(host, port);
|
||||
this.app.notifications.success(
|
||||
`Reachable — ${result.latencyMs}ms latency`,
|
||||
);
|
||||
@@ -2764,6 +2764,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
window.NexusTerminal = window.NexusTerminal || {};
|
||||
window.NexusTerminal.ProfileEditor = ProfileEditor;
|
||||
window.AlchementaryLink = window.AlchementaryLink || {};
|
||||
window.AlchementaryLink.ProfileEditor = ProfileEditor;
|
||||
})();
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
(function () {
|
||||
/**
|
||||
* Nexus Terminal — RAW TCP data viewer.
|
||||
* Alchementary Link — RAW TCP data viewer.
|
||||
* Provides text, hex, and split display modes for raw socket data,
|
||||
* along with a send bar, pause/resume, and capture saving.
|
||||
*/
|
||||
|
||||
const { createElement, escapeHtml, formatTimestamp, formatBytes } =
|
||||
window.NexusTerminal;
|
||||
window.AlchementaryLink;
|
||||
|
||||
/**
|
||||
* Maximum number of data entries retained in the scrollback buffer.
|
||||
@@ -214,7 +214,7 @@
|
||||
return `[${ts}] [${dir}] ${e.data}`;
|
||||
});
|
||||
const content = lines.join("\n");
|
||||
await window.NexusTerminal.wailsService.SaveSessionCapture(sessionId, content);
|
||||
await window.AlchementaryLink.wailsService.SaveSessionCapture(sessionId, content);
|
||||
this.app.notifications.success("Capture saved");
|
||||
} catch (err) {
|
||||
this.app.notifications.error("Save failed: " + err);
|
||||
@@ -256,8 +256,8 @@
|
||||
const b64 =
|
||||
format === "hex" || format === "escaped"
|
||||
? btoa(dataToSend)
|
||||
: window.NexusTerminal.utf8ToBase64(dataToSend);
|
||||
await window.NexusTerminal.wailsService.WriteSession(sessionId, b64);
|
||||
: window.AlchementaryLink.utf8ToBase64(dataToSend);
|
||||
await window.AlchementaryLink.wailsService.WriteSession(sessionId, b64);
|
||||
input.value = "";
|
||||
|
||||
// Also show in viewer as sent data
|
||||
@@ -620,6 +620,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
window.NexusTerminal = window.NexusTerminal || {};
|
||||
window.NexusTerminal.RawViewer = RawViewer;
|
||||
window.AlchementaryLink = window.AlchementaryLink || {};
|
||||
window.AlchementaryLink.RawViewer = RawViewer;
|
||||
})();
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
(function() {
|
||||
/**
|
||||
* Nexus Terminal — Session Recording & Playback.
|
||||
* Alchementary Link — Session Recording & Playback.
|
||||
* Provides controls for starting/stopping recordings on active sessions,
|
||||
* listing saved recordings, and a terminal-based playback viewer with
|
||||
* speed controls and a progress scrubber.
|
||||
*/
|
||||
|
||||
const { createElement, formatDuration } = window.NexusTerminal;
|
||||
const { createElement, formatDuration } = window.AlchementaryLink;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Recording Manager
|
||||
@@ -41,7 +41,7 @@ class RecordingManager {
|
||||
const cols = entry ? entry.term.cols : 80;
|
||||
const rows = entry ? entry.term.rows : 24;
|
||||
|
||||
await window.NexusTerminal.wailsService.StartRecording(sessionId, cols, rows);
|
||||
await window.AlchementaryLink.wailsService.StartRecording(sessionId, cols, rows);
|
||||
this.activeSessions.add(sessionId);
|
||||
this._updateRecordingIndicator(sessionId, true);
|
||||
this.app.notifications.success('Recording started');
|
||||
@@ -56,7 +56,7 @@ class RecordingManager {
|
||||
*/
|
||||
async stop(sessionId) {
|
||||
try {
|
||||
await window.NexusTerminal.wailsService.StopRecording(sessionId);
|
||||
await window.AlchementaryLink.wailsService.StopRecording(sessionId);
|
||||
this.activeSessions.delete(sessionId);
|
||||
this._updateRecordingIndicator(sessionId, false);
|
||||
this.app.notifications.success('Recording saved');
|
||||
@@ -81,8 +81,8 @@ class RecordingManager {
|
||||
async openBrowser() {
|
||||
try {
|
||||
const [recordings, storageInfo] = await Promise.all([
|
||||
window.NexusTerminal.wailsService.ListRecordings(),
|
||||
window.NexusTerminal.wailsService.GetRecordingStorageInfo(),
|
||||
window.AlchementaryLink.wailsService.ListRecordings(),
|
||||
window.AlchementaryLink.wailsService.GetRecordingStorageInfo(),
|
||||
]);
|
||||
this._showBrowserModal(recordings || [], storageInfo || {});
|
||||
} catch (err) {
|
||||
@@ -96,7 +96,7 @@ class RecordingManager {
|
||||
*/
|
||||
async playback(id) {
|
||||
try {
|
||||
const data = await window.NexusTerminal.wailsService.GetRecording(id);
|
||||
const data = await window.AlchementaryLink.wailsService.GetRecording(id);
|
||||
this._showPlaybackModal(data);
|
||||
} catch (err) {
|
||||
this.app.notifications.error('Failed to load recording: ' + err);
|
||||
@@ -109,7 +109,7 @@ class RecordingManager {
|
||||
*/
|
||||
async deleteRecording(id) {
|
||||
try {
|
||||
await window.NexusTerminal.wailsService.DeleteRecording(id);
|
||||
await window.AlchementaryLink.wailsService.DeleteRecording(id);
|
||||
this.app.notifications.success('Recording deleted');
|
||||
} catch (err) {
|
||||
this.app.notifications.error('Failed to delete recording: ' + err);
|
||||
@@ -235,11 +235,11 @@ class RecordingManager {
|
||||
status.textContent = '';
|
||||
btn.disabled = true;
|
||||
try {
|
||||
const result = await window.NexusTerminal.wailsService.CleanupRecordings(ageDays, maxBytes);
|
||||
const result = await window.AlchementaryLink.wailsService.CleanupRecordings(ageDays, maxBytes);
|
||||
status.textContent = `Deleted ${result.deleted || 0}; freed ${_formatBytes(result.freedBytes || 0)}.`;
|
||||
const fresh = await window.NexusTerminal.wailsService.ListRecordings();
|
||||
const fresh = await window.AlchementaryLink.wailsService.ListRecordings();
|
||||
close();
|
||||
this._showBrowserModal(fresh || [], await window.NexusTerminal.wailsService.GetRecordingStorageInfo());
|
||||
this._showBrowserModal(fresh || [], await window.AlchementaryLink.wailsService.GetRecordingStorageInfo());
|
||||
} catch (err) {
|
||||
status.textContent = 'Cleanup failed: ' + err;
|
||||
} finally {
|
||||
@@ -314,7 +314,7 @@ class RecordingManager {
|
||||
|
||||
// Create xterm instance for playback.
|
||||
const termContainer = modal.querySelector('#playback-terminal');
|
||||
const { getTerminalTheme } = window.NexusTerminal;
|
||||
const { getTerminalTheme } = window.AlchementaryLink;
|
||||
const themeName = (this.app.state.preferences && this.app.state.preferences.terminalTheme) || 'charcoal';
|
||||
const theme = getTerminalTheme ? getTerminalTheme(themeName) : {};
|
||||
|
||||
@@ -566,7 +566,7 @@ class ExportImportUI {
|
||||
}
|
||||
|
||||
try {
|
||||
const path = await window.NexusTerminal.wailsService.ExportProfilesEncrypted(selectedIds, password);
|
||||
const path = await window.AlchementaryLink.wailsService.ExportProfilesEncrypted(selectedIds, password);
|
||||
this.app.notifications.success(`Exported ${selectedIds.length} profile(s) to ${path}`);
|
||||
close();
|
||||
} catch (err) {
|
||||
@@ -638,7 +638,7 @@ class ExportImportUI {
|
||||
try {
|
||||
confirmBtn.disabled = true;
|
||||
confirmBtn.textContent = 'Importing...';
|
||||
const count = await window.NexusTerminal.wailsService.ImportProfilesEncrypted(password, strategy);
|
||||
const count = await window.AlchementaryLink.wailsService.ImportProfilesEncrypted(password, strategy);
|
||||
this.app.notifications.success(`Imported ${count} profile(s)`);
|
||||
await this.app.loadProfiles();
|
||||
close();
|
||||
@@ -694,8 +694,8 @@ function _formatBytes(bytes) {
|
||||
// Export to global namespace
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
window.NexusTerminal = window.NexusTerminal || {};
|
||||
window.NexusTerminal.RecordingManager = RecordingManager;
|
||||
window.NexusTerminal.ExportImportUI = ExportImportUI;
|
||||
window.AlchementaryLink = window.AlchementaryLink || {};
|
||||
window.AlchementaryLink.RecordingManager = RecordingManager;
|
||||
window.AlchementaryLink.ExportImportUI = ExportImportUI;
|
||||
|
||||
})();
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
(function() {
|
||||
/**
|
||||
* Nexus Terminal — secret/passphrase prompt dialogs.
|
||||
* Alchementary Link — secret/passphrase prompt dialogs.
|
||||
* Provides modal prompts for collecting passwords, master-vault passphrases,
|
||||
* and SSH host-key verification confirmations.
|
||||
*/
|
||||
|
||||
const { createElement, escapeHtml } = window.NexusTerminal;
|
||||
const { createElement, escapeHtml } = window.AlchementaryLink;
|
||||
|
||||
class SecretPrompt {
|
||||
constructor(app) {
|
||||
@@ -367,7 +367,7 @@ function _measureStrength(pw) {
|
||||
return { percent: score, level: 'strong', label: 'Strong' };
|
||||
}
|
||||
|
||||
window.NexusTerminal = window.NexusTerminal || {};
|
||||
window.NexusTerminal.SecretPrompt = SecretPrompt;
|
||||
window.AlchementaryLink = window.AlchementaryLink || {};
|
||||
window.AlchementaryLink.SecretPrompt = SecretPrompt;
|
||||
|
||||
})();
|
||||
|
||||
+39
-39
@@ -1,12 +1,12 @@
|
||||
(function () {
|
||||
/**
|
||||
* Nexus Terminal — settings modal.
|
||||
* Alchementary Link — settings modal.
|
||||
* Renders a tabbed settings dialog for application-wide preferences,
|
||||
* terminal defaults, security, known-hosts management, and about info.
|
||||
*/
|
||||
|
||||
const { createElement, escapeHtml, formatTimestamp, $ } =
|
||||
window.NexusTerminal;
|
||||
window.AlchementaryLink;
|
||||
|
||||
/** Settings tab definitions. */
|
||||
const TABS = [
|
||||
@@ -65,13 +65,13 @@
|
||||
this._activeTab = tabKey || this._activeTab || "general";
|
||||
|
||||
try {
|
||||
this._knownHosts = (await window.NexusTerminal.wailsService.ListKnownHosts()) || [];
|
||||
this._knownHosts = (await window.AlchementaryLink.wailsService.ListKnownHosts()) || [];
|
||||
} catch (_) {
|
||||
this._knownHosts = [];
|
||||
}
|
||||
|
||||
try {
|
||||
this._defaultShell = await window.NexusTerminal.wailsService.GetDefaultShell();
|
||||
this._defaultShell = await window.AlchementaryLink.wailsService.GetDefaultShell();
|
||||
} catch (_) {
|
||||
this._defaultShell = "";
|
||||
}
|
||||
@@ -106,7 +106,7 @@
|
||||
this._collectValues();
|
||||
|
||||
try {
|
||||
await window.NexusTerminal.wailsService.SavePreferences(this._prefs);
|
||||
await window.AlchementaryLink.wailsService.SavePreferences(this._prefs);
|
||||
this.app.state.setPreferences(this._prefs);
|
||||
|
||||
// Apply terminal colour theme change immediately.
|
||||
@@ -331,7 +331,7 @@
|
||||
});
|
||||
browseBtn.addEventListener("click", async () => {
|
||||
try {
|
||||
const path = await window.NexusTerminal.wailsService.BrowseForShell();
|
||||
const path = await window.AlchementaryLink.wailsService.BrowseForShell();
|
||||
if (path) {
|
||||
shellInput.value = path;
|
||||
}
|
||||
@@ -356,7 +356,7 @@
|
||||
const p = this._prefs;
|
||||
|
||||
// Terminal colour theme selector
|
||||
const themes = window.NexusTerminal.listTerminalThemes();
|
||||
const themes = window.AlchementaryLink.listTerminalThemes();
|
||||
body.appendChild(
|
||||
this._settingField(
|
||||
"select",
|
||||
@@ -448,7 +448,7 @@
|
||||
initBtn.addEventListener("click", async () => {
|
||||
try {
|
||||
const pw = await this.app.secretPrompt.promptMasterPassword(true);
|
||||
await window.NexusTerminal.wailsService.InitialiseSecretStore(pw);
|
||||
await window.AlchementaryLink.wailsService.InitialiseSecretStore(pw);
|
||||
this.app.state.setSecretStoreInitialised(true);
|
||||
this.app.state.setSecretStoreState(true);
|
||||
this.app.notifications.success("Secret store created");
|
||||
@@ -467,7 +467,7 @@
|
||||
unlockBtn.addEventListener("click", async () => {
|
||||
try {
|
||||
const pw = await this.app.secretPrompt.promptMasterPassword(false);
|
||||
await window.NexusTerminal.wailsService.UnlockSecretStore(pw);
|
||||
await window.AlchementaryLink.wailsService.UnlockSecretStore(pw);
|
||||
this.app.state.setSecretStoreState(true);
|
||||
this.app.notifications.success("Secret store unlocked");
|
||||
this._renderTabContent(body);
|
||||
@@ -484,7 +484,7 @@
|
||||
});
|
||||
lockBtn.addEventListener("click", async () => {
|
||||
try {
|
||||
await window.NexusTerminal.wailsService.LockSecretStore();
|
||||
await window.AlchementaryLink.wailsService.LockSecretStore();
|
||||
this.app.state.setSecretStoreState(false);
|
||||
this.app.notifications.info("Secret store locked");
|
||||
this._renderTabContent(body);
|
||||
@@ -541,7 +541,7 @@
|
||||
});
|
||||
deleteBtn.addEventListener("click", async () => {
|
||||
try {
|
||||
await window.NexusTerminal.wailsService.DeleteKnownHost(kh.id);
|
||||
await window.AlchementaryLink.wailsService.DeleteKnownHost(kh.id);
|
||||
this._knownHosts = this._knownHosts.filter((h) => h.id !== kh.id);
|
||||
this._renderTabContent(body);
|
||||
this.app.notifications.success("Known host removed");
|
||||
@@ -568,7 +568,7 @@
|
||||
id: "getting-started",
|
||||
title: "Getting Started",
|
||||
content: `
|
||||
<p>Welcome to <strong>Nexus Terminal</strong> — a desktop application for SSH, Telnet, RAW TCP, VNC, GitHub, local terminal, and Leviathan binary-analysis workflows.</p>
|
||||
<p>Welcome to <strong>Alchementary Link</strong> — a desktop application for SSH, Telnet, RAW TCP, VNC, GitHub, local terminal, and Leviathan binary-analysis workflows.</p>
|
||||
<h4>Creating Your First Connection</h4>
|
||||
<ol>
|
||||
<li>Click the <strong>+</strong> button at the top of the sidebar, or press <kbd>⌘N</kbd> (Mac) / <kbd>Ctrl+N</kbd> (Windows/Linux).</li>
|
||||
@@ -578,7 +578,7 @@
|
||||
<li>Click <strong>Save</strong>, then double-click the entry in the sidebar to connect.</li>
|
||||
</ol>
|
||||
<h4>Leviathan Tools</h4>
|
||||
<p>Click <strong>Leviathan</strong> beside <strong>Nexus Terminal</strong> in the sidebar header to switch from connection profiles to binary-analysis tools. Use <strong>Load</strong> to choose a local file, then open tools such as Hex Editor, Overview, Entropy, Carve, BinSnoop, Structures, and Spectrogram View.</p>
|
||||
<p>Click <strong>Leviathan</strong> beside <strong>Alchementary Link</strong> in the sidebar header to switch from connection profiles to binary-analysis tools. Use <strong>Load</strong> to choose a local file, then open tools such as Hex Editor, Overview, Entropy, Carve, BinSnoop, Structures, and Spectrogram View.</p>
|
||||
<h4>Quick Connect</h4>
|
||||
<p>Need to connect quickly without saving a profile? Press <kbd>⌘⇧Q</kbd> / <kbd>Ctrl+Shift+Q</kbd> to open the quick connect dialog. Choose SSH, Telnet, or RAW, enter the host and port, and you're connected immediately.</p>
|
||||
<h4>Managing Connections</h4>
|
||||
@@ -589,7 +589,7 @@
|
||||
id: "ssh",
|
||||
title: "SSH Connections",
|
||||
content: `
|
||||
<p>SSH (Secure Shell) is the most common way to connect to remote servers. Nexus Terminal supports:</p>
|
||||
<p>SSH (Secure Shell) is the most common way to connect to remote servers. Alchementary Link supports:</p>
|
||||
<ul>
|
||||
<li><strong>Password authentication</strong> — enter your password when prompted, or save it in the encrypted vault.</li>
|
||||
<li><strong>Public key authentication</strong> — point to your private key file (e.g. <code>~/.ssh/id_rsa</code>). If the key has a passphrase, you will be prompted.</li>
|
||||
@@ -597,13 +597,13 @@
|
||||
<li><strong>SSH agent</strong> — tick "Use SSH Agent" to authenticate via your system's SSH agent (e.g. <code>ssh-agent</code> or 1Password).</li>
|
||||
</ul>
|
||||
<h4>TOTP / 2FA Auto-Fill</h4>
|
||||
<p>If your server requires a time-based one-time password (TOTP) during login, you can store the TOTP secret in the profile editor under the <strong>TOTP / 2FA</strong> section. When connecting, Nexus Terminal will detect 2FA prompts (such as "Verification code" or "OTP") and automatically generate and submit a 6-digit code. Use the <strong>Test</strong> button to verify your secret generates valid codes before connecting.</p>
|
||||
<p>If your server requires a time-based one-time password (TOTP) during login, you can store the TOTP secret in the profile editor under the <strong>TOTP / 2FA</strong> section. When connecting, Alchementary Link will detect 2FA prompts (such as "Verification code" or "OTP") and automatically generate and submit a 6-digit code. Use the <strong>Test</strong> button to verify your secret generates valid codes before connecting.</p>
|
||||
<h4>Jump Hosts (Bastions)</h4>
|
||||
<p>If you need to reach a server through an intermediate host, add one or more jump hosts in the connection editor. Nexus Terminal will tunnel through each hop automatically.</p>
|
||||
<p>If you need to reach a server through an intermediate host, add one or more jump hosts in the connection editor. Alchementary Link will tunnel through each hop automatically.</p>
|
||||
<h4>Port Forwarding</h4>
|
||||
<p>You can set up <strong>local</strong>, <strong>remote</strong>, and <strong>dynamic</strong> (SOCKS proxy) port forwards in the connection editor. These start automatically when the session connects.</p>
|
||||
<h4>Proxy Support</h4>
|
||||
<p>If you need to connect through a proxy, open the <strong>Proxy</strong> section in the profile editor. Nexus Terminal supports <strong>SOCKS5</strong> and <strong>HTTP CONNECT</strong> proxies, with optional username/password authentication. The proxy is used to reach the SSH server (or the first jump host if jump hosts are configured).</p>
|
||||
<p>If you need to connect through a proxy, open the <strong>Proxy</strong> section in the profile editor. Alchementary Link supports <strong>SOCKS5</strong> and <strong>HTTP CONNECT</strong> proxies, with optional username/password authentication. The proxy is used to reach the SSH server (or the first jump host if jump hosts are configured).</p>
|
||||
<h4>SFTP File Browser</h4>
|
||||
<p>Once connected to an SSH server, click the <strong>📁</strong> icon on the tab to open the SFTP file browser. You can browse directories, upload, download, rename, and delete files from a side panel.</p>
|
||||
<h4>SSH Agent Forwarding</h4>
|
||||
@@ -627,7 +627,7 @@
|
||||
content: `
|
||||
<p>Telnet is an older, unencrypted protocol still used for network equipment, legacy systems, and some fun internet services.</p>
|
||||
<ul>
|
||||
<li><strong>Option negotiation</strong> — Nexus Terminal handles standard Telnet options (echo, terminal type, window size) automatically.</li>
|
||||
<li><strong>Option negotiation</strong> — Alchementary Link handles standard Telnet options (echo, terminal type, window size) automatically.</li>
|
||||
<li><strong>Character encoding</strong> — choose UTF-8, ASCII, or Latin-1 for the connection.</li>
|
||||
<li><strong>Local echo</strong> — enable this if the remote host does not echo back what you type.</li>
|
||||
<li><strong>Line mode</strong> — send data line-by-line (press Enter to send) rather than character-by-character.</li>
|
||||
@@ -656,7 +656,7 @@
|
||||
id: "vnc",
|
||||
title: "VNC Connections",
|
||||
content: `
|
||||
<p>VNC profiles open a remote desktop viewer inside the Nexus workspace.</p>
|
||||
<p>VNC profiles open a remote desktop viewer inside the Link workspace.</p>
|
||||
<ul>
|
||||
<li><strong>Password handling</strong> — ask each time, use the session only, or save the password in the encrypted secret store.</li>
|
||||
<li><strong>View-only mode</strong> — observe a desktop without sending mouse or keyboard input.</li>
|
||||
@@ -672,7 +672,7 @@
|
||||
id: "github",
|
||||
title: "GitHub Connections",
|
||||
content: `
|
||||
<p>The GitHub connection type lets you browse your repositories without leaving Nexus Terminal.</p>
|
||||
<p>The GitHub connection type lets you browse your repositories without leaving Alchementary Link.</p>
|
||||
<h4>Setting Up</h4>
|
||||
<ol>
|
||||
<li>Create a new connection and choose <strong>GitHub</strong> as the protocol.</li>
|
||||
@@ -775,20 +775,20 @@
|
||||
title: "Security & Credentials",
|
||||
content: `
|
||||
<h4>How Your Passwords Are Stored</h4>
|
||||
<p>Nexus Terminal uses a <strong>two-tier</strong> approach to keep your credentials safe:</p>
|
||||
<p>Alchementary Link uses a <strong>two-tier</strong> approach to keep your credentials safe:</p>
|
||||
<ol>
|
||||
<li><strong>OS keychain</strong> (preferred) — your operating system's built-in credential store (macOS Keychain, Windows Credential Manager, or Linux Secret Service). If available, this is used automatically.</li>
|
||||
<li><strong>Encrypted vault</strong> (fallback) — if the OS keychain is not available, Nexus Terminal creates a local vault encrypted with AES-256-GCM. You set a master password to unlock it.</li>
|
||||
<li><strong>Encrypted vault</strong> (fallback) — if the OS keychain is not available, Alchementary Link creates a local vault encrypted with AES-256-GCM. You set a master password to unlock it.</li>
|
||||
</ol>
|
||||
<h4>Encryption Details</h4>
|
||||
<p>The vault uses <strong>Argon2id</strong> to derive the encryption key from your master password (128 MiB memory, making brute-force attacks very expensive). Each secret is individually encrypted and never stored in plain text.</p>
|
||||
<h4>TOTP / 2FA Secrets</h4>
|
||||
<p>TOTP secrets for SSH two-factor authentication are stored alongside your other credentials — encrypted in the OS keychain or the file vault. They are never saved in the profile database in plain text.</p>
|
||||
<h4>Known Hosts</h4>
|
||||
<p>When you connect to an SSH server for the first time, Nexus Terminal records the server's fingerprint. If the fingerprint changes on a subsequent connection, you'll be warned — this protects against man-in-the-middle attacks.</p>
|
||||
<p>When you connect to an SSH server for the first time, Alchementary Link records the server's fingerprint. If the fingerprint changes on a subsequent connection, you'll be warned — this protects against man-in-the-middle attacks.</p>
|
||||
<p>You can manage known hosts and change the verification policy in <strong>Settings → Known Hosts</strong>.</p>
|
||||
<h4>Encrypted Export</h4>
|
||||
<p>When exporting profiles, you can choose encrypted export (.nexus file) which protects the file with a password. This uses the same strong AES-256-GCM encryption as the vault.</p>
|
||||
<p>When exporting profiles, you can choose encrypted export (.link file) which protects the file with a password. This uses the same strong AES-256-GCM encryption as the vault.</p>
|
||||
`,
|
||||
},
|
||||
{
|
||||
@@ -822,11 +822,11 @@
|
||||
title: "Import & Export",
|
||||
content: `
|
||||
<h4>Exporting Profiles</h4>
|
||||
<p>Click the <strong>↗</strong> export button in the sidebar footer. You can choose which profiles to include, and set a password to encrypt the file. Exported files use the <code>.nexus</code> extension.</p>
|
||||
<p>Click the <strong>↗</strong> export button in the sidebar footer. You can choose which profiles to include, and set a password to encrypt the file. Exported files use the <code>.link</code> extension.</p>
|
||||
<h4>Importing Profiles</h4>
|
||||
<p>Click the <strong>↙</strong> import button. Select a <code>.nexus</code> file and enter the password used when exporting. Imported profiles will appear in your sidebar immediately.</p>
|
||||
<p>Click the <strong>↙</strong> import button. Select a <code>.link</code> file and enter the password used when exporting. Imported profiles will appear in your sidebar immediately.</p>
|
||||
<h4>Importing from OpenSSH</h4>
|
||||
<p>If you have an existing <code>~/.ssh/config</code> file, go to <strong>Settings → About</strong> and click <strong>Import ~/.ssh/config</strong>. Nexus Terminal will create profiles from your SSH Host blocks, including hostnames, usernames, ports, identity files, and jump hosts.</p>
|
||||
<p>If you have an existing <code>~/.ssh/config</code> file, go to <strong>Settings → About</strong> and click <strong>Import ~/.ssh/config</strong>. Alchementary Link will create profiles from your SSH Host blocks, including hostnames, usernames, ports, identity files, and jump hosts.</p>
|
||||
`,
|
||||
},
|
||||
{
|
||||
@@ -892,16 +892,16 @@
|
||||
<h4>SFTP panel doesn't open</h4>
|
||||
<p>SFTP is only available for <strong>SSH</strong> connections (not Telnet, RAW, or GitHub). Make sure you are connected to an SSH session first, then click the <strong>📁</strong> icon on the tab.</p>
|
||||
<h4>The application looks blurry on my display</h4>
|
||||
<p>Make sure your system display scaling is set correctly. On macOS, Nexus Terminal is fully Retina-aware. On Windows, check your DPI settings in Display preferences.</p>
|
||||
<p>Make sure your system display scaling is set correctly. On macOS, Alchementary Link is fully Retina-aware. On Windows, check your DPI settings in Display preferences.</p>
|
||||
<h4>Export/import isn't working</h4>
|
||||
<p>Ensure you're using the same password to import that was used to export. The encrypted <code>.nexus</code> files cannot be opened with the wrong password.</p>
|
||||
<p>Ensure you're using the same password to import that was used to export. The encrypted <code>.link</code> files cannot be opened with the wrong password.</p>
|
||||
`,
|
||||
},
|
||||
{
|
||||
id: "fun",
|
||||
title: "Fun Things to Try",
|
||||
content: `
|
||||
<p>Nexus Terminal isn't just for work! Here are some entertaining things you can connect to:</p>
|
||||
<p>Alchementary Link isn't just for work! Here are some entertaining things you can connect to:</p>
|
||||
<h4>🌟 Star Wars in ASCII Art</h4>
|
||||
<p>Create a <strong>Telnet</strong> connection to <code>towel.blinkenlights.nl</code> on port <strong>23</strong>. Sit back and enjoy the original Star Wars film retold entirely in ASCII art.</p>
|
||||
<h4>🗺️ MapSCII — World Map in Your Terminal</h4>
|
||||
@@ -974,9 +974,9 @@
|
||||
_renderAboutTab(body) {
|
||||
body.innerHTML = `
|
||||
<div class="about-section">
|
||||
<h3 class="about-title">Nexus Terminal</h3>
|
||||
<h3 class="about-title">Alchementary Link</h3>
|
||||
<p class="about-version" data-role="app-version">Version …</p>
|
||||
<p class="about-desc">Developed by Andy Dixon - www.dixon.cx/nexus</p>
|
||||
<p class="about-desc">Developed by Andy Dixon - alchementary.co.uk</p>
|
||||
<div class="about-links">
|
||||
<p>AES-256-GCM encrypted credential vault</p>
|
||||
<p>Platform-native keychain integration</p>
|
||||
@@ -993,8 +993,8 @@
|
||||
|
||||
const versionEl = body.querySelector("[data-role='app-version']");
|
||||
if (versionEl) {
|
||||
if (window.NexusTerminal?.wailsService) {
|
||||
window.NexusTerminal.wailsService.GetAppInfo()
|
||||
if (window.AlchementaryLink?.wailsService) {
|
||||
window.AlchementaryLink.wailsService.GetAppInfo()
|
||||
.then((info) => {
|
||||
const version = (info && info.version) || "dev";
|
||||
versionEl.textContent = `Version ${version}`;
|
||||
@@ -1013,7 +1013,7 @@
|
||||
updateBtn.disabled = true;
|
||||
updateBtn.textContent = "Checking…";
|
||||
try {
|
||||
const info = await window.NexusTerminal.wailsService.CheckForUpdates();
|
||||
const info = await window.AlchementaryLink.wailsService.CheckForUpdates();
|
||||
const resultEl = body.querySelector("#settings-update-result");
|
||||
if (this.app && typeof this.app.setUpdateInfo === "function") {
|
||||
this.app.setUpdateInfo(info);
|
||||
@@ -1061,7 +1061,7 @@
|
||||
importBtn.disabled = true;
|
||||
importBtn.textContent = "Importing…";
|
||||
try {
|
||||
const count = await window.NexusTerminal.wailsService.ImportSSHConfig(
|
||||
const count = await window.AlchementaryLink.wailsService.ImportSSHConfig(
|
||||
"",
|
||||
"SSH Config",
|
||||
);
|
||||
@@ -1129,7 +1129,7 @@
|
||||
};
|
||||
|
||||
try {
|
||||
await window.NexusTerminal.wailsService.SavePreferences(prefs);
|
||||
await window.AlchementaryLink.wailsService.SavePreferences(prefs);
|
||||
this._prefs.leviathanEnabled = enabled;
|
||||
this.app.state.setPreferences(prefs);
|
||||
this.app._applyPreferences?.();
|
||||
@@ -1271,6 +1271,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
window.NexusTerminal = window.NexusTerminal || {};
|
||||
window.NexusTerminal.Settings = Settings;
|
||||
window.AlchementaryLink = window.AlchementaryLink || {};
|
||||
window.AlchementaryLink.Settings = Settings;
|
||||
})();
|
||||
|
||||
+13
-13
@@ -1,6 +1,6 @@
|
||||
(function () {
|
||||
/**
|
||||
* Nexus Terminal — SFTP File Browser
|
||||
* Alchementary Link — SFTP File Browser
|
||||
*
|
||||
* Provides a right-side panel for browsing, uploading, downloading,
|
||||
* renaming and deleting remote files alongside the terminal.
|
||||
@@ -12,7 +12,7 @@
|
||||
formatBytes,
|
||||
relativeTime,
|
||||
dismissContextMenus,
|
||||
} = window.NexusTerminal;
|
||||
} = window.AlchementaryLink;
|
||||
|
||||
/* ── Helpers ──────────────────────────────────────────────────────── */
|
||||
|
||||
@@ -282,9 +282,9 @@
|
||||
|
||||
try {
|
||||
if (path === null) {
|
||||
path = await window.NexusTerminal.wailsService.SFTPPwd(sessionId);
|
||||
path = await window.AlchementaryLink.wailsService.SFTPPwd(sessionId);
|
||||
}
|
||||
var files = await window.NexusTerminal.wailsService.SFTPList(sessionId, path);
|
||||
var files = await window.AlchementaryLink.wailsService.SFTPList(sessionId, path);
|
||||
state.cwd = path;
|
||||
state.files = this._sortFiles(files || []);
|
||||
state.loading = false;
|
||||
@@ -604,7 +604,7 @@
|
||||
|
||||
async _download(sessionId, filePath) {
|
||||
try {
|
||||
await window.NexusTerminal.wailsService.SFTPDownload(sessionId, filePath);
|
||||
await window.AlchementaryLink.wailsService.SFTPDownload(sessionId, filePath);
|
||||
this.app.notifications.success("Download started");
|
||||
} catch (err) {
|
||||
this.app.notifications.error(
|
||||
@@ -617,7 +617,7 @@
|
||||
var state = this.panels.get(sessionId);
|
||||
if (!state) return;
|
||||
try {
|
||||
await window.NexusTerminal.wailsService.SFTPUpload(sessionId, state.cwd);
|
||||
await window.AlchementaryLink.wailsService.SFTPUpload(sessionId, state.cwd);
|
||||
this.app.notifications.success("Upload complete");
|
||||
await this._refresh(sessionId);
|
||||
} catch (err) {
|
||||
@@ -634,7 +634,7 @@
|
||||
if (!state) return;
|
||||
var fullPath = pathJoin(state.cwd, name);
|
||||
try {
|
||||
await window.NexusTerminal.wailsService.SFTPMkdir(sessionId, fullPath);
|
||||
await window.AlchementaryLink.wailsService.SFTPMkdir(sessionId, fullPath);
|
||||
this.app.notifications.success("Directory created");
|
||||
await this._refresh(sessionId);
|
||||
} catch (err) {
|
||||
@@ -647,7 +647,7 @@
|
||||
if (!state) return;
|
||||
var newPath = pathJoin(parentPath(oldPath), newName);
|
||||
try {
|
||||
await window.NexusTerminal.wailsService.SFTPRename(sessionId, oldPath, newPath);
|
||||
await window.AlchementaryLink.wailsService.SFTPRename(sessionId, oldPath, newPath);
|
||||
this.app.notifications.success("Renamed");
|
||||
await this._refresh(sessionId);
|
||||
} catch (err) {
|
||||
@@ -657,7 +657,7 @@
|
||||
|
||||
async _delete(sessionId, entry) {
|
||||
try {
|
||||
await window.NexusTerminal.wailsService.SFTPRemove(sessionId, entry.path);
|
||||
await window.AlchementaryLink.wailsService.SFTPRemove(sessionId, entry.path);
|
||||
this.app.notifications.success(
|
||||
(entry.isDir ? "Directory" : "File") + " deleted",
|
||||
);
|
||||
@@ -672,7 +672,7 @@
|
||||
var failed = 0;
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
try {
|
||||
await window.NexusTerminal.wailsService.SFTPDownload(sessionId, files[i].path);
|
||||
await window.AlchementaryLink.wailsService.SFTPDownload(sessionId, files[i].path);
|
||||
} catch (err) {
|
||||
failed++;
|
||||
this.app.notifications.error(
|
||||
@@ -706,7 +706,7 @@
|
||||
var failed = 0;
|
||||
for (var i = 0; i < entries.length; i++) {
|
||||
try {
|
||||
await window.NexusTerminal.wailsService.SFTPRemove(sessionId, entries[i].path);
|
||||
await window.AlchementaryLink.wailsService.SFTPRemove(sessionId, entries[i].path);
|
||||
} catch (err) {
|
||||
failed++;
|
||||
this.app.notifications.error(
|
||||
@@ -1242,6 +1242,6 @@
|
||||
}
|
||||
|
||||
/* ── Export ────────────────────────────────────────────────────────── */
|
||||
window.NexusTerminal = window.NexusTerminal || {};
|
||||
window.NexusTerminal.SFTPBrowser = SFTPBrowser;
|
||||
window.AlchementaryLink = window.AlchementaryLink || {};
|
||||
window.AlchementaryLink.SFTPBrowser = SFTPBrowser;
|
||||
})();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
(function () {
|
||||
/**
|
||||
* Nexus Terminal — sidebar module.
|
||||
* Alchementary Link — sidebar module.
|
||||
* Renders the profile list with search, sections (favourites, recent, all),
|
||||
* and context menus.
|
||||
*/
|
||||
@@ -12,7 +12,7 @@
|
||||
protocolColour,
|
||||
dismissContextMenus,
|
||||
truncate,
|
||||
} = window.NexusTerminal;
|
||||
} = window.AlchementaryLink;
|
||||
|
||||
/** Maximum number of profiles shown in the "Recent" section. */
|
||||
const RECENT_LIMIT = 5;
|
||||
@@ -441,7 +441,7 @@
|
||||
async _toggleFavourite(profile) {
|
||||
try {
|
||||
const newVal = !profile.favourite;
|
||||
await window.NexusTerminal.wailsService.SetFavourite(profile.id, newVal);
|
||||
await window.AlchementaryLink.wailsService.SetFavourite(profile.id, newVal);
|
||||
profile.favourite = newVal;
|
||||
this.app.state.updateProfile(profile);
|
||||
this.render();
|
||||
@@ -457,7 +457,7 @@
|
||||
*/
|
||||
async _duplicateProfile(profile) {
|
||||
try {
|
||||
const dup = await window.NexusTerminal.wailsService.DuplicateProfile(profile.id);
|
||||
const dup = await window.AlchementaryLink.wailsService.DuplicateProfile(profile.id);
|
||||
this.app.state.addProfile(dup);
|
||||
this.render();
|
||||
this.app.notifications.success(`Duplicated "${profile.displayName}"`);
|
||||
@@ -478,7 +478,7 @@
|
||||
if (!ok) return;
|
||||
|
||||
try {
|
||||
await window.NexusTerminal.wailsService.DeleteProfile(profile.id);
|
||||
await window.AlchementaryLink.wailsService.DeleteProfile(profile.id);
|
||||
this.app.state.removeProfile(profile.id);
|
||||
this.render();
|
||||
this.app.notifications.success("Profile deleted");
|
||||
@@ -687,7 +687,7 @@
|
||||
}
|
||||
|
||||
try {
|
||||
const saved = await window.NexusTerminal.wailsService.UpdateProfile(updated);
|
||||
const saved = await window.AlchementaryLink.wailsService.UpdateProfile(updated);
|
||||
this.app.state.updateProfile(saved);
|
||||
} catch (err) {
|
||||
this.app.notifications.error("Failed to update tags: " + err);
|
||||
@@ -708,7 +708,7 @@
|
||||
const updated = JSON.parse(JSON.stringify(p));
|
||||
updated.folder = folder;
|
||||
try {
|
||||
const saved = await window.NexusTerminal.wailsService.UpdateProfile(updated);
|
||||
const saved = await window.AlchementaryLink.wailsService.UpdateProfile(updated);
|
||||
this.app.state.updateProfile(saved);
|
||||
} catch (err) {
|
||||
this.app.notifications.error("Failed to move group: " + err);
|
||||
@@ -934,6 +934,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
window.NexusTerminal = window.NexusTerminal || {};
|
||||
window.NexusTerminal.Sidebar = Sidebar;
|
||||
window.AlchementaryLink = window.AlchementaryLink || {};
|
||||
window.AlchementaryLink.Sidebar = Sidebar;
|
||||
})();
|
||||
|
||||
+10
-10
@@ -1,11 +1,11 @@
|
||||
(function () {
|
||||
/**
|
||||
* Nexus Terminal — snippet / command library.
|
||||
* Alchementary Link — snippet / command library.
|
||||
* Provides a panel for managing and sending reusable commands to active sessions.
|
||||
*/
|
||||
|
||||
const { createElement, escapeHtml, dismissContextMenus } =
|
||||
window.NexusTerminal;
|
||||
window.AlchementaryLink;
|
||||
|
||||
class SnippetLibrary {
|
||||
/**
|
||||
@@ -91,8 +91,8 @@
|
||||
/** @private */
|
||||
_sendCommand(sessionId, command) {
|
||||
try {
|
||||
const b64 = window.NexusTerminal.utf8ToBase64(command + "\n");
|
||||
window.NexusTerminal.wailsService.WriteSession(sessionId, b64);
|
||||
const b64 = window.AlchementaryLink.utf8ToBase64(command + "\n");
|
||||
window.AlchementaryLink.wailsService.WriteSession(sessionId, b64);
|
||||
this.app.notifications.info("Snippet sent");
|
||||
} catch (err) {
|
||||
this.app.notifications.error("Failed to send snippet: " + err);
|
||||
@@ -171,7 +171,7 @@
|
||||
/** @private */
|
||||
async _loadSnippets() {
|
||||
try {
|
||||
this.snippets = (await window.NexusTerminal.wailsService.ListSnippets()) || [];
|
||||
this.snippets = (await window.AlchementaryLink.wailsService.ListSnippets()) || [];
|
||||
} catch (err) {
|
||||
this.snippets = [];
|
||||
console.error("Failed to load snippets:", err);
|
||||
@@ -334,7 +334,7 @@
|
||||
});
|
||||
deleteBtn.addEventListener("click", async () => {
|
||||
try {
|
||||
await window.NexusTerminal.wailsService.DeleteSnippet(snippet.id);
|
||||
await window.AlchementaryLink.wailsService.DeleteSnippet(snippet.id);
|
||||
this.snippets = this.snippets.filter((s) => s.id !== snippet.id);
|
||||
this._renderList(modal, closeFn);
|
||||
detailPanel.innerHTML =
|
||||
@@ -418,10 +418,10 @@
|
||||
|
||||
let saved;
|
||||
if (isNew) {
|
||||
saved = await window.NexusTerminal.wailsService.CreateSnippet(data);
|
||||
saved = await window.AlchementaryLink.wailsService.CreateSnippet(data);
|
||||
this.snippets.push(saved);
|
||||
} else {
|
||||
saved = await window.NexusTerminal.wailsService.UpdateSnippet(data);
|
||||
saved = await window.AlchementaryLink.wailsService.UpdateSnippet(data);
|
||||
const idx = this.snippets.findIndex((s) => s.id === saved.id);
|
||||
if (idx !== -1) this.snippets[idx] = saved;
|
||||
}
|
||||
@@ -449,6 +449,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
window.NexusTerminal = window.NexusTerminal || {};
|
||||
window.NexusTerminal.SnippetLibrary = SnippetLibrary;
|
||||
window.AlchementaryLink = window.AlchementaryLink || {};
|
||||
window.AlchementaryLink.SnippetLibrary = SnippetLibrary;
|
||||
})();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
(function() {
|
||||
/**
|
||||
* Nexus Terminal — Split Pane Manager.
|
||||
* Alchementary Link — Split Pane Manager.
|
||||
* Enables splitting session panels into multiple panes, each hosting
|
||||
* its own terminal session. Supports horizontal (top/bottom) and
|
||||
* vertical (left/right) splits with draggable resize dividers.
|
||||
@@ -522,10 +522,10 @@ class SplitPaneManager {
|
||||
if (placeholder) placeholder.remove();
|
||||
|
||||
// Connect via backend
|
||||
window.NexusTerminal.wailsService.ConnectSession(profile.id).then(async function(sessionInfo) {
|
||||
window.AlchementaryLink.wailsService.ConnectSession(profile.id).then(async function(sessionInfo) {
|
||||
if (!self._completePendingConnection(paneEl, ownerSessionId, token)) {
|
||||
try {
|
||||
await window.NexusTerminal.wailsService.DisconnectSession(sessionInfo.id);
|
||||
await window.AlchementaryLink.wailsService.DisconnectSession(sessionInfo.id);
|
||||
} catch (_) {
|
||||
// The backend may already have completed the close.
|
||||
}
|
||||
@@ -556,10 +556,10 @@ class SplitPaneManager {
|
||||
var placeholder = paneEl.querySelector('.split-pane-placeholder');
|
||||
if (placeholder) placeholder.remove();
|
||||
|
||||
window.NexusTerminal.wailsService.ConnectLocal().then(async function(sessionInfo) {
|
||||
window.AlchementaryLink.wailsService.ConnectLocal().then(async function(sessionInfo) {
|
||||
if (!self._completePendingConnection(paneEl, ownerSessionId, token)) {
|
||||
try {
|
||||
await window.NexusTerminal.wailsService.DisconnectSession(sessionInfo.id);
|
||||
await window.AlchementaryLink.wailsService.DisconnectSession(sessionInfo.id);
|
||||
} catch (_) {
|
||||
// The backend may already have completed the close.
|
||||
}
|
||||
@@ -791,7 +791,7 @@ class SplitPaneManager {
|
||||
}
|
||||
}
|
||||
|
||||
window.NexusTerminal = window.NexusTerminal || {};
|
||||
window.NexusTerminal.SplitPaneManager = SplitPaneManager;
|
||||
window.AlchementaryLink = window.AlchementaryLink || {};
|
||||
window.AlchementaryLink.SplitPaneManager = SplitPaneManager;
|
||||
|
||||
})();
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
(function () {
|
||||
/**
|
||||
* Nexus Terminal — SSH Key Manager.
|
||||
* Alchementary Link — SSH Key Manager.
|
||||
* Panel for listing, generating, copying, and deleting SSH key pairs.
|
||||
*/
|
||||
|
||||
const { createElement, dismissContextMenus } = window.NexusTerminal;
|
||||
const { createElement, dismissContextMenus } = window.AlchementaryLink;
|
||||
|
||||
class SSHKeyManager {
|
||||
constructor(app) {
|
||||
@@ -81,7 +81,7 @@
|
||||
list.innerHTML = '<div class="sshkey-loading">Loading keys…</div>';
|
||||
|
||||
try {
|
||||
const keys = await window.NexusTerminal.wailsService.ListSSHKeyPairs();
|
||||
const keys = await window.AlchementaryLink.wailsService.ListSSHKeyPairs();
|
||||
if (keys.length === 0) {
|
||||
list.innerHTML =
|
||||
'<div class="sshkey-empty">No SSH keys found in ~/.ssh/</div>';
|
||||
@@ -138,7 +138,7 @@
|
||||
|
||||
async _copyPublicKey(key) {
|
||||
try {
|
||||
const pubKey = await window.NexusTerminal.wailsService.GetSSHPublicKey(key.name);
|
||||
const pubKey = await window.AlchementaryLink.wailsService.GetSSHPublicKey(key.name);
|
||||
if (window.runtime && window.runtime.ClipboardSetText) {
|
||||
window.runtime.ClipboardSetText(pubKey);
|
||||
} else {
|
||||
@@ -163,7 +163,7 @@
|
||||
|
||||
async _deleteKey(key) {
|
||||
try {
|
||||
await window.NexusTerminal.wailsService.DeleteSSHKey(key.name);
|
||||
await window.AlchementaryLink.wailsService.DeleteSSHKey(key.name);
|
||||
this.app.notifications.success(`Key "${key.name}" deleted`);
|
||||
this._loadKeys();
|
||||
} catch (err) {
|
||||
@@ -181,7 +181,7 @@
|
||||
form.innerHTML = `
|
||||
<div class="form-group">
|
||||
<label class="form-label">Key Name</label>
|
||||
<input type="text" class="form-input sshkey-name-input" placeholder="id_ed25519_nexus" autocomplete="off" spellcheck="false" />
|
||||
<input type="text" class="form-input sshkey-name-input" placeholder="id_ed25519_link" autocomplete="off" spellcheck="false" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Type</label>
|
||||
@@ -242,7 +242,7 @@
|
||||
}
|
||||
|
||||
try {
|
||||
await window.NexusTerminal.wailsService.GenerateSSHKey(
|
||||
await window.AlchementaryLink.wailsService.GenerateSSHKey(
|
||||
name,
|
||||
keyType,
|
||||
bits,
|
||||
@@ -332,6 +332,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
window.NexusTerminal = window.NexusTerminal || {};
|
||||
window.NexusTerminal.SSHKeyManager = SSHKeyManager;
|
||||
window.AlchementaryLink = window.AlchementaryLink || {};
|
||||
window.AlchementaryLink.SSHKeyManager = SSHKeyManager;
|
||||
})();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
(function () {
|
||||
/**
|
||||
* Nexus Terminal — application state management.
|
||||
* Alchementary Link — application state management.
|
||||
* Provides a centralised store for profiles, sessions, and preferences,
|
||||
* emitting change events so the UI stays in sync.
|
||||
*/
|
||||
@@ -235,6 +235,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
window.NexusTerminal = window.NexusTerminal || {};
|
||||
window.NexusTerminal.State = State;
|
||||
window.AlchementaryLink = window.AlchementaryLink || {};
|
||||
window.AlchementaryLink.State = State;
|
||||
})();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
(function () {
|
||||
/**
|
||||
* Nexus Terminal — tab management.
|
||||
* Alchementary Link — tab management.
|
||||
* Handles the tab bar: adding, removing, reordering, renaming,
|
||||
* pinning tabs, and context menus.
|
||||
*/
|
||||
@@ -11,7 +11,7 @@
|
||||
protocolColour,
|
||||
stateLabel,
|
||||
dismissContextMenus,
|
||||
} = window.NexusTerminal;
|
||||
} = window.AlchementaryLink;
|
||||
|
||||
class TabManager {
|
||||
/**
|
||||
@@ -192,7 +192,7 @@
|
||||
* @private
|
||||
*/
|
||||
_confirmClose(sessionId) {
|
||||
const createElement = window.NexusTerminal.createElement;
|
||||
const createElement = window.AlchementaryLink.createElement;
|
||||
const label = escapeHtml(this._tabLabel(sessionId));
|
||||
return new Promise((resolve) => {
|
||||
const backdrop = createElement("div", "modal-backdrop");
|
||||
@@ -928,6 +928,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
window.NexusTerminal = window.NexusTerminal || {};
|
||||
window.NexusTerminal.TabManager = TabManager;
|
||||
window.AlchementaryLink = window.AlchementaryLink || {};
|
||||
window.AlchementaryLink.TabManager = TabManager;
|
||||
})();
|
||||
|
||||
+28
-28
@@ -1,6 +1,6 @@
|
||||
(function () {
|
||||
/**
|
||||
* Nexus Terminal — xterm.js terminal manager.
|
||||
* Alchementary Link — xterm.js terminal manager.
|
||||
* Creates, manages, and destroys xterm.js instances for SSH and Telnet sessions.
|
||||
* Handles data routing, resizing, and search.
|
||||
*/
|
||||
@@ -52,7 +52,7 @@
|
||||
const themeName =
|
||||
(this.app.state.preferences || {}).terminalTheme || "charcoal";
|
||||
const term = new Terminal({
|
||||
theme: window.NexusTerminal.getTerminalTheme(themeName),
|
||||
theme: window.AlchementaryLink.getTerminalTheme(themeName),
|
||||
fontFamily:
|
||||
profile.terminalFontFamily ||
|
||||
'JetBrains Mono, Menlo, Monaco, Consolas, "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", monospace',
|
||||
@@ -155,7 +155,7 @@
|
||||
const data = seq.repeat(lines);
|
||||
const b64 = utf8ToBase64(data);
|
||||
try {
|
||||
window.NexusTerminal.wailsService.WriteSession(sessionId, b64);
|
||||
window.AlchementaryLink.wailsService.WriteSession(sessionId, b64);
|
||||
} catch (_) {
|
||||
/* non-critical */
|
||||
}
|
||||
@@ -263,7 +263,7 @@
|
||||
|
||||
try {
|
||||
const b64 = utf8ToBase64(data);
|
||||
window.NexusTerminal.wailsService.WriteSession(sessionId, b64);
|
||||
window.AlchementaryLink.wailsService.WriteSession(sessionId, b64);
|
||||
|
||||
// Broadcast to other connected sessions if enabled
|
||||
if (this.app.state.broadcastMode) {
|
||||
@@ -275,7 +275,7 @@
|
||||
const otherSession = this.app.state.getSession(otherId);
|
||||
if (otherSession && otherSession.state === "connected") {
|
||||
try {
|
||||
window.NexusTerminal.wailsService.WriteSession(otherId, b64);
|
||||
window.AlchementaryLink.wailsService.WriteSession(otherId, b64);
|
||||
} catch (_) {
|
||||
/* non-critical */
|
||||
}
|
||||
@@ -570,7 +570,7 @@
|
||||
* @param {string} themeName
|
||||
*/
|
||||
applyTheme(themeName) {
|
||||
const themeObj = window.NexusTerminal.getTerminalTheme(themeName);
|
||||
const themeObj = window.AlchementaryLink.getTerminalTheme(themeName);
|
||||
for (const [, entry] of this.terminals) {
|
||||
entry.term.options.theme = themeObj;
|
||||
}
|
||||
@@ -584,7 +584,7 @@
|
||||
if (cols <= 0 || rows <= 0) return;
|
||||
|
||||
Promise.resolve(
|
||||
window.NexusTerminal.wailsService.ResizeSession(sessionId, cols, rows),
|
||||
window.AlchementaryLink.wailsService.ResizeSession(sessionId, cols, rows),
|
||||
).catch((err) => console.error("ResizeSession error:", err));
|
||||
}
|
||||
|
||||
@@ -593,7 +593,7 @@
|
||||
entry.localRestarting = true;
|
||||
|
||||
try {
|
||||
await window.NexusTerminal.wailsService.RestartLocalSession(
|
||||
await window.AlchementaryLink.wailsService.RestartLocalSession(
|
||||
sessionId,
|
||||
entry.term.cols,
|
||||
entry.term.rows,
|
||||
@@ -617,7 +617,7 @@
|
||||
let overlay = panel.querySelector(".terminal-disconnect-overlay");
|
||||
if (overlay) return overlay;
|
||||
|
||||
const { createElement } = window.NexusTerminal;
|
||||
const { createElement } = window.AlchementaryLink;
|
||||
if (!createElement) return null;
|
||||
|
||||
overlay = createElement("div", "terminal-disconnect-overlay");
|
||||
@@ -653,7 +653,7 @@
|
||||
* @private
|
||||
*/
|
||||
_showTerminalContextMenu(sessionId, term, x, y) {
|
||||
const { createElement, dismissContextMenus } = window.NexusTerminal;
|
||||
const { createElement, dismissContextMenus } = window.AlchementaryLink;
|
||||
dismissContextMenus();
|
||||
|
||||
const isMac = navigator.platform.indexOf("Mac") !== -1;
|
||||
@@ -813,7 +813,7 @@
|
||||
}
|
||||
|
||||
_promptFileDropAction(files, canUpload) {
|
||||
const { createElement, escapeHtml, formatBytes } = window.NexusTerminal;
|
||||
const { createElement, escapeHtml, formatBytes } = window.AlchementaryLink;
|
||||
return new Promise((resolve) => {
|
||||
const backdrop = createElement("div", "modal-backdrop");
|
||||
const modal = createElement("div", "modal modal-sm");
|
||||
@@ -951,7 +951,7 @@
|
||||
const totalBytes = typeof file.size === "number" ? file.size : 0;
|
||||
let uploadId = "";
|
||||
try {
|
||||
uploadId = await window.NexusTerminal.wailsService.SFTPUploadStart(
|
||||
uploadId = await window.AlchementaryLink.wailsService.SFTPUploadStart(
|
||||
sessionId,
|
||||
remoteDir,
|
||||
file.name,
|
||||
@@ -962,14 +962,14 @@
|
||||
const end = Math.min(offset + UPLOAD_CHUNK_BYTES, totalBytes);
|
||||
const buffer = await this._readFileSlice(file, offset, end);
|
||||
const base64Data = this._arrayBufferToBase64(buffer);
|
||||
await window.NexusTerminal.wailsService.SFTPUploadChunk(uploadId, base64Data);
|
||||
await window.AlchementaryLink.wailsService.SFTPUploadChunk(uploadId, base64Data);
|
||||
}
|
||||
|
||||
await window.NexusTerminal.wailsService.SFTPUploadFinish(uploadId);
|
||||
await window.AlchementaryLink.wailsService.SFTPUploadFinish(uploadId);
|
||||
} catch (err) {
|
||||
if (uploadId) {
|
||||
try {
|
||||
await window.NexusTerminal.wailsService.SFTPUploadCancel(uploadId);
|
||||
await window.AlchementaryLink.wailsService.SFTPUploadCancel(uploadId);
|
||||
} catch (_) {
|
||||
/* noop */
|
||||
}
|
||||
@@ -1001,7 +1001,7 @@
|
||||
}
|
||||
|
||||
_openUploadDirectoryPicker(sessionId) {
|
||||
const { createElement, escapeHtml } = window.NexusTerminal;
|
||||
const { createElement, escapeHtml } = window.AlchementaryLink;
|
||||
return new Promise((resolve) => {
|
||||
const backdrop = createElement("div", "modal-backdrop");
|
||||
const modal = createElement("div", "modal modal-lg");
|
||||
@@ -1223,12 +1223,12 @@
|
||||
renderLoading();
|
||||
try {
|
||||
const targetPath =
|
||||
path || (await window.NexusTerminal.wailsService.SFTPPwd(sessionId));
|
||||
const files = await window.NexusTerminal.wailsService.SFTPList(
|
||||
path || (await window.AlchementaryLink.wailsService.SFTPPwd(sessionId));
|
||||
const files = await window.AlchementaryLink.wailsService.SFTPList(
|
||||
sessionId,
|
||||
targetPath,
|
||||
);
|
||||
const stat = await window.NexusTerminal.wailsService.SFTPStat(
|
||||
const stat = await window.AlchementaryLink.wailsService.SFTPStat(
|
||||
sessionId,
|
||||
targetPath,
|
||||
);
|
||||
@@ -1320,7 +1320,7 @@
|
||||
}
|
||||
|
||||
_buildPickerRow(entry, index) {
|
||||
const { createElement } = window.NexusTerminal;
|
||||
const { createElement } = window.AlchementaryLink;
|
||||
const dataset = { path: entry.path || "" };
|
||||
if (typeof index === "number") {
|
||||
dataset.index = String(index);
|
||||
@@ -1395,7 +1395,7 @@
|
||||
* @private
|
||||
*/
|
||||
_exportTerminalOutput(sessionId, term) {
|
||||
const { createElement } = window.NexusTerminal;
|
||||
const { createElement } = window.AlchementaryLink;
|
||||
const buffer = term.buffer.active;
|
||||
const lines = [];
|
||||
for (let i = 0; i < buffer.length; i++) {
|
||||
@@ -1438,7 +1438,7 @@
|
||||
* @private
|
||||
*/
|
||||
_showPortForwardPanel(sessionId) {
|
||||
const { createElement, escapeHtml } = window.NexusTerminal;
|
||||
const { createElement, escapeHtml } = window.AlchementaryLink;
|
||||
const session = this.app.state.getSession(sessionId);
|
||||
if (!session || session.protocol !== "ssh") {
|
||||
this.app.notifications.error(
|
||||
@@ -1502,7 +1502,7 @@
|
||||
const renderForwards = async () => {
|
||||
try {
|
||||
const forwards =
|
||||
(await window.NexusTerminal.wailsService.ListPortForwards(sessionId)) || [];
|
||||
(await window.AlchementaryLink.wailsService.ListPortForwards(sessionId)) || [];
|
||||
listEl.innerHTML = "";
|
||||
if (forwards.length === 0) {
|
||||
listEl.innerHTML =
|
||||
@@ -1528,7 +1528,7 @@
|
||||
});
|
||||
stopBtn.addEventListener("click", async () => {
|
||||
try {
|
||||
await window.NexusTerminal.wailsService.StopPortForward(sessionId, fwd.id);
|
||||
await window.AlchementaryLink.wailsService.StopPortForward(sessionId, fwd.id);
|
||||
self.app.notifications.info("Forward stopped");
|
||||
renderForwards();
|
||||
} catch (err) {
|
||||
@@ -1573,7 +1573,7 @@
|
||||
}
|
||||
|
||||
try {
|
||||
await window.NexusTerminal.wailsService.StartPortForward(sessionId, {
|
||||
await window.AlchementaryLink.wailsService.StartPortForward(sessionId, {
|
||||
id: "",
|
||||
direction: direction,
|
||||
bindHost: bindHost,
|
||||
@@ -1604,10 +1604,10 @@
|
||||
_buildTheme() {
|
||||
const themeName =
|
||||
(this.app.state.preferences || {}).terminalTheme || "charcoal";
|
||||
return window.NexusTerminal.getTerminalTheme(themeName);
|
||||
return window.AlchementaryLink.getTerminalTheme(themeName);
|
||||
}
|
||||
}
|
||||
|
||||
window.NexusTerminal = window.NexusTerminal || {};
|
||||
window.NexusTerminal.TerminalManager = TerminalManager;
|
||||
window.AlchementaryLink = window.AlchementaryLink || {};
|
||||
window.AlchementaryLink.TerminalManager = TerminalManager;
|
||||
})();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
(function () {
|
||||
/**
|
||||
* Nexus Terminal — terminal colour themes.
|
||||
* Alchementary Link — terminal colour themes.
|
||||
* Provides a collection of preset xterm.js colour schemes and a manager
|
||||
* for applying them to terminal instances.
|
||||
*/
|
||||
@@ -341,7 +341,7 @@
|
||||
}));
|
||||
}
|
||||
|
||||
window.NexusTerminal = window.NexusTerminal || {};
|
||||
window.NexusTerminal.getTerminalTheme = getTerminalTheme;
|
||||
window.NexusTerminal.listTerminalThemes = listTerminalThemes;
|
||||
window.AlchementaryLink = window.AlchementaryLink || {};
|
||||
window.AlchementaryLink.getTerminalTheme = getTerminalTheme;
|
||||
window.AlchementaryLink.listTerminalThemes = listTerminalThemes;
|
||||
})();
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
(function () {
|
||||
/**
|
||||
* Nexus Terminal — Trigger Rules Engine.
|
||||
* Alchementary Link — Trigger Rules Engine.
|
||||
* Monitors terminal output and fires actions (highlight, notify, sound)
|
||||
* when patterns match. Rules are stored in localStorage.
|
||||
*/
|
||||
|
||||
const { createElement } = window.NexusTerminal;
|
||||
const { createElement } = window.AlchementaryLink;
|
||||
|
||||
const STORAGE_KEY = "nexus:trigger-rules";
|
||||
const STORAGE_KEY = "link:trigger-rules";
|
||||
|
||||
class TriggerEngine {
|
||||
constructor(app) {
|
||||
@@ -376,6 +376,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
window.NexusTerminal = window.NexusTerminal || {};
|
||||
window.NexusTerminal.TriggerEngine = TriggerEngine;
|
||||
window.AlchementaryLink = window.AlchementaryLink || {};
|
||||
window.AlchementaryLink.TriggerEngine = TriggerEngine;
|
||||
})();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
(function () {
|
||||
/**
|
||||
* Nexus Terminal — utility functions.
|
||||
* Alchementary Link — utility functions.
|
||||
* General-purpose helpers used throughout the application.
|
||||
*/
|
||||
|
||||
@@ -348,8 +348,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
window.NexusTerminal = window.NexusTerminal || {};
|
||||
Object.assign(window.NexusTerminal, {
|
||||
window.AlchementaryLink = window.AlchementaryLink || {};
|
||||
Object.assign(window.AlchementaryLink, {
|
||||
$,
|
||||
$$,
|
||||
createElement,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
(function () {
|
||||
/**
|
||||
* Nexus Terminal — VNC viewer.
|
||||
* Alchementary Link — VNC viewer.
|
||||
* Renders a noVNC canvas inside a session panel, connecting to the local
|
||||
* WebSocket proxy started by the Go backend.
|
||||
*/
|
||||
|
||||
const { createElement, $ } = window.NexusTerminal;
|
||||
const { createElement, $ } = window.AlchementaryLink;
|
||||
|
||||
class VNCViewer {
|
||||
constructor() {
|
||||
@@ -327,5 +327,5 @@
|
||||
}
|
||||
|
||||
// Export to global namespace.
|
||||
window.NexusTerminal.VNCViewer = new VNCViewer();
|
||||
window.AlchementaryLink.VNCViewer = new VNCViewer();
|
||||
})();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
(function () {
|
||||
const root = (window.NexusTerminal = window.NexusTerminal || {});
|
||||
const root = (window.AlchementaryLink = window.AlchementaryLink || {});
|
||||
|
||||
const directService = () => window.go?.api?.Service || null;
|
||||
const unavailable = (name) =>
|
||||
|
||||
Reference in New Issue
Block a user