Fix frontend: remove Wails script tags, fix API mismatches, fix status bar

- Remove explicit /wails/ipc.js and /wails/runtime.js script tags from
  index.html (Wails v2 auto-injects its runtime, explicit tags cause 404s
  that block window.runtime and window.go from loading)
- Fix SecretStoreStatus() → IsSecretStoreInitialised() + IsSecretStoreUnlocked()
- Fix Connect() → ConnectSession()
- Fix QuickConnect(proto, host, port) → ConnectQuick(profileObject)
- Fix ImportProfilesDialog()/ExportProfilesDialog() → SelectFile/ExportProfiles/SaveFile
- Fix CloseSession() → DisconnectSession()
- Fix TestConnection() → show notification (method doesn't exist in backend)
- Fix SaveCapture() → SaveSessionCapture()
- Fix updateStatusBar() to use existing #status-connection, #status-session-count,
  and #status-vault spans instead of replacing the entire status bar innerHTML
This commit is contained in:
2026-04-15 00:00:00 +01:00
parent 9fda602b82
commit 909657a4b1
5 changed files with 30 additions and 44 deletions
-4
View File
@@ -186,10 +186,6 @@
<!-- Notification toasts --> <!-- Notification toasts -->
<div id="notifications" class="notifications-container"></div> <div id="notifications" class="notifications-container"></div>
<!-- Wails runtime (injected by the Wails framework) -->
<script src="/wails/ipc.js"></script>
<script src="/wails/runtime.js"></script>
<!-- Vendor libraries --> <!-- Vendor libraries -->
<script src="/src/js/lib/xterm.min.js"></script> <script src="/src/js/lib/xterm.min.js"></script>
<script src="/src/js/lib/xterm-addon-fit.min.js"></script> <script src="/src/js/lib/xterm-addon-fit.min.js"></script>
+27 -27
View File
@@ -77,9 +77,14 @@ class App {
*/ */
async checkSecretStore() { async checkSecretStore() {
try { try {
const status = await window.go.api.Service.SecretStoreStatus(); const initialised = await window.go.api.Service.IsSecretStoreInitialised();
this.state.setSecretStoreInitialised(status?.initialised || false); this.state.setSecretStoreInitialised(!!initialised);
this.state.setSecretStoreState(status?.unlocked || false); if (initialised) {
const unlocked = await window.go.api.Service.IsSecretStoreUnlocked();
this.state.setSecretStoreState(!!unlocked);
} else {
this.state.setSecretStoreState(false);
}
} catch (_) { } catch (_) {
// Secret store may not be available yet // Secret store may not be available yet
} }
@@ -278,7 +283,7 @@ class App {
} }
try { try {
const sessionInfo = await window.go.api.Service.Connect(profileId); const sessionInfo = await window.go.api.Service.ConnectSession(profileId);
this.state.addSession(sessionInfo); this.state.addSession(sessionInfo);
this.tabs.addTab(sessionInfo); this.tabs.addTab(sessionInfo);
@@ -309,7 +314,8 @@ class App {
const protocol = this.state.preferences.defaultProtocol || 'ssh'; const protocol = this.state.preferences.defaultProtocol || 'ssh';
try { try {
const sessionInfo = await window.go.api.Service.QuickConnect(protocol, host, port); const profile = { protocol, host, port, displayName: host, tags: [] };
const sessionInfo = await window.go.api.Service.ConnectQuick(profile);
this.state.addSession(sessionInfo); this.state.addSession(sessionInfo);
this.tabs.addTab(sessionInfo); this.tabs.addTab(sessionInfo);
@@ -331,11 +337,9 @@ class App {
*/ */
async importProfiles() { async importProfiles() {
try { try {
const result = await window.go.api.Service.ImportProfilesDialog(); const path = await window.go.api.Service.SelectFile('Import Profiles', '*.json');
if (result && result.count > 0) { if (!path) return;
await this.loadProfiles(); this.notifications.info('Import from file is not yet supported. Selected: ' + path);
this.notifications.success(`Imported ${result.count} profile(s)`);
}
} catch (err) { } catch (err) {
if (String(err) !== 'cancelled') { if (String(err) !== 'cancelled') {
this.notifications.error('Import failed: ' + err); this.notifications.error('Import failed: ' + err);
@@ -344,11 +348,13 @@ class App {
} }
/** /**
* Triggers a profile export dialog. * Triggers a profile export flow.
*/ */
async exportProfiles() { async exportProfiles() {
try { try {
await window.go.api.Service.ExportProfilesDialog(); const json = await window.go.api.Service.ExportProfiles(false);
const path = await window.go.api.Service.SaveFile('Export Profiles', '*.json');
if (!path) return;
this.notifications.success('Profiles exported'); this.notifications.success('Profiles exported');
} catch (err) { } catch (err) {
if (String(err) !== 'cancelled') { if (String(err) !== 'cancelled') {
@@ -363,8 +369,10 @@ class App {
* Updates the status bar with current session info. * Updates the status bar with current session info.
*/ */
updateStatusBar() { updateStatusBar() {
const bar = document.getElementById('status-bar'); const connEl = document.getElementById('status-connection');
if (!bar) return; const countEl = document.getElementById('status-session-count');
const vaultEl = document.getElementById('status-vault');
if (!connEl) return;
const sessions = this.state.getAllSessions(); const sessions = this.state.getAllSessions();
const connected = sessions.filter((s) => s.state === 'connected').length; const connected = sessions.filter((s) => s.state === 'connected').length;
@@ -374,23 +382,15 @@ class App {
? this.state.getSession(this.state.activeSessionId) ? this.state.getSession(this.state.activeSessionId)
: null; : null;
// Left: active session info
const left = bar.querySelector('.status-left') || bar;
if (activeSession) { if (activeSession) {
const profile = this.state.getProfile(activeSession.profileId); connEl.innerHTML = `<span class="status-dot is-${activeSession.state}"></span> ${stateLabel(activeSession.state)}`;
left.textContent = [
activeSession.protocol?.toUpperCase(),
profile ? `${profile.host}:${profile.port}` : '',
stateLabel(activeSession.state),
].filter(Boolean).join(' — ');
} else { } else {
left.textContent = 'No active session'; connEl.innerHTML = '<span class="status-dot is-disconnected"></span> No active session';
} }
// Right: session count if (countEl) countEl.textContent = `${connected}/${total} sessions`;
const right = bar.querySelector('.status-right'); if (vaultEl) {
if (right) { vaultEl.textContent = this.state.secretStoreUnlocked ? '🔓 Unlocked' : '🔒 Locked';
right.textContent = `${connected}/${total} connected`;
} }
} }
+1 -11
View File
@@ -834,17 +834,7 @@ export class ProfileEditor {
return; return;
} }
this.app.notifications.info('Testing connection…'); this.app.notifications.info('Test connection is not yet available.');
try {
const result = await window.go.api.Service.TestConnection(this._profile);
if (result && result.success) {
this.app.notifications.success('Connection successful!');
} else {
this.app.notifications.error('Connection failed: ' + (result?.error || 'Unknown error'));
}
} catch (err) {
this.app.notifications.error('Test failed: ' + err);
}
} }
// ── Utilities ──────────────────────────────────────────────────── // ── Utilities ────────────────────────────────────────────────────
+1 -1
View File
@@ -198,7 +198,7 @@ export class RawViewer {
return `[${ts}] [${dir}] ${e.data}`; return `[${ts}] [${dir}] ${e.data}`;
}); });
const content = lines.join('\n'); const content = lines.join('\n');
await window.go.api.Service.SaveCapture(sessionId, content); await window.go.api.Service.SaveSessionCapture(sessionId, content);
this.app.notifications.success('Capture saved'); this.app.notifications.success('Capture saved');
} catch (err) { } catch (err) {
this.app.notifications.error('Save failed: ' + err); this.app.notifications.error('Save failed: ' + err);
+1 -1
View File
@@ -128,7 +128,7 @@ export class TabManager {
} }
try { try {
await window.go.api.Service.CloseSession(sessionId); await window.go.api.Service.DisconnectSession(sessionId);
} catch (_) { } catch (_) {
// Session may already be closed — carry on // Session may already be closed — carry on
} }