(function () {
window.AppMenu = window.AppMenu || [];
// Add InstaFile menu item
window.AppMenu.push({
label: '๐ InstaFile (Paste to File)',
action: () => {
if (window.AppOverlay) {
const instaFileSection = {
title: 'InstaFile - Paste to File',
html: `
<div style="padding: 1rem;">
<h3 style="margin-top: 0;">๐ Paste Content to Create File</h3>
<p style="color: #94a3b8; margin-bottom: 1rem;">
Paste text from clipboard and save it as a file on your SFTP server.
</p>
<div style="margin-bottom: 1rem;">
<label style="display: block; font-weight: 600; margin-bottom: 0.5rem;">
File Name:
</label>
<input
type="text"
id="instaFileName"
placeholder="example.txt"
style="width: 100%; padding: 0.6rem; background: #0f1725; border: 1px solid #2a3648; border-radius: 8px; color: #e6edf3;"
/>
</div>
<div style="margin-bottom: 1rem;">
<label style="display: block; font-weight: 600; margin-bottom: 0.5rem;">
Content (tap to paste):
</label>
<textarea
id="instaFileContent"
placeholder="Tap here and paste your content..."
style="width: 100%; min-height: 300px; padding: 0.75rem; background: #0f1725; border: 1px solid #2a3648; border-radius: 8px; color: #e6edf3; font-family: monospace; font-size: 14px; resize: vertical;"
></textarea>
</div>
<div style="display: flex; gap: 0.5rem;">
<button id="instaFileSave" style="flex: 1; padding: 0.75rem; background: linear-gradient(135deg, #3b82f6, #9333ea); border: none; border-radius: 8px; color: white; font-weight: 600; cursor: pointer;">
๐พ Save to SFTP
</button>
<button id="instaFileCancel" style="padding: 0.75rem 1.5rem; background: #2a3648; border: none; border-radius: 8px; color: #e6edf3; font-weight: 600; cursor: pointer;">
Cancel
</button>
</div>
<div id="instaFileMessage" style="margin-top: 1rem; padding: 0.75rem; border-radius: 8px; display: none;"></div>
</div>
`
};
AppOverlay.open([instaFileSection], 0);
// Wait for overlay to render
setTimeout(() => {
const saveBtn = document.getElementById('instaFileSave');
const cancelBtn = document.getElementById('instaFileCancel');
const fileNameInput = document.getElementById('instaFileName');
const contentArea = document.getElementById('instaFileContent');
const messageDiv = document.getElementById('instaFileMessage');
// Focus textarea for easy pasting
contentArea.focus();
// Cancel button
if (cancelBtn) {
cancelBtn.onclick = () => {
if (AppOverlay && AppOverlay.close) AppOverlay.close();
};
}
// Save button
if (saveBtn) {
saveBtn.onclick = async () => {
const fileName = fileNameInput.value.trim();
const content = contentArea.value;
if (!fileName) {
messageDiv.style.display = 'block';
messageDiv.style.background = 'rgba(239, 68, 68, 0.2)';
messageDiv.style.color = '#ef4444';
messageDiv.style.border = '1px solid rgba(239, 68, 68, 0.3)';
messageDiv.textContent = 'โ Please enter a file name';
return;
}
if (!content) {
messageDiv.style.display = 'block';
messageDiv.style.background = 'rgba(239, 68, 68, 0.2)';
messageDiv.style.color = '#ef4444';
messageDiv.style.border = '1px solid rgba(239, 68, 68, 0.3)';
messageDiv.textContent = 'โ Please paste some content';
return;
}
try {
saveBtn.disabled = true;
saveBtn.textContent = 'โณ Saving...';
// Use instafile.php to save content
const res = await fetch('instafile.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
path: '/' + fileName, // Save to root directory
content: content
})
});
const data = await res.json();
if (data.success) {
messageDiv.style.display = 'block';
messageDiv.style.background = 'rgba(16, 185, 129, 0.2)';
messageDiv.style.color = '#10b981';
messageDiv.style.border = '1px solid rgba(16, 185, 129, 0.3)';
messageDiv.textContent = 'โ
File saved successfully!';
// Clear form
setTimeout(() => {
fileNameInput.value = '';
contentArea.value = '';
if (AppOverlay && AppOverlay.close) {
AppOverlay.close();
}
}, 1500);
} else {
throw new Error(data.message || 'Upload failed');
}
} catch (err) {
messageDiv.style.display = 'block';
messageDiv.style.background = 'rgba(239, 68, 68, 0.2)';
messageDiv.style.color = '#ef4444';
messageDiv.style.border = '1px solid rgba(239, 68, 68, 0.3)';
messageDiv.textContent = 'โ Error: ' + err.message;
} finally {
saveBtn.disabled = false;
saveBtn.textContent = '๐พ Save to SFTP';
}
};
}
}, 100);
}
}
});
// Add Copy File to Clipboard menu item
window.AppMenu.push({
label: '๐ Copy File to Clipboard',
action: () => {
if (window.AppOverlay) {
const copyFileSection = {
title: 'Copy File to Clipboard',
html: `
<div style="padding: 1rem;">
<h3 style="margin-top: 0;">๐ Copy File Content</h3>
<p style="color: #94a3b8; margin-bottom: 1rem;">
Enter the file path to copy its entire content to your clipboard.
</p>
<div style="margin-bottom: 1rem;">
<label style="display: block; font-weight: 600; margin-bottom: 0.5rem;">
File Path:
</label>
<input
type="text"
id="copyFilePath"
placeholder="/path/to/file.txt"
style="width: 100%; padding: 0.6rem; background: #0f1725; border: 1px solid #2a3648; border-radius: 8px; color: #e6edf3;"
/>
</div>
<div style="display: flex; gap: 0.5rem;">
<button id="copyFileBtn" style="flex: 1; padding: 0.75rem; background: linear-gradient(135deg, #3b82f6, #9333ea); border: none; border-radius: 8px; color: white; font-weight: 600; cursor: pointer;">
๐ Copy to Clipboard
</button>
<button id="copyFileCancel" style="padding: 0.75rem 1.5rem; background: #2a3648; border: none; border-radius: 8px; color: #e6edf3; font-weight: 600; cursor: pointer;">
Cancel
</button>
</div>
<div id="copyFilePreview" style="margin-top: 1rem; padding: 0.75rem; background: #0f1725; border: 1px solid #2a3648; border-radius: 8px; max-height: 300px; overflow-y: auto; font-family: monospace; font-size: 13px; white-space: pre-wrap; display: none; color: #94a3b8;"></div>
<div id="copyFileMessage" style="margin-top: 1rem; padding: 0.75rem; border-radius: 8px; display: none;"></div>
</div>
`
};
AppOverlay.open([copyFileSection], 0);
// Wait for overlay to render
setTimeout(() => {
const copyBtn = document.getElementById('copyFileBtn');
const cancelBtn = document.getElementById('copyFileCancel');
const filePathInput = document.getElementById('copyFilePath');
const previewDiv = document.getElementById('copyFilePreview');
const messageDiv = document.getElementById('copyFileMessage');
// Cancel button
if (cancelBtn) {
cancelBtn.onclick = () => {
if (AppOverlay && AppOverlay.close) AppOverlay.close();
};
}
// Copy button
if (copyBtn) {
copyBtn.onclick = async () => {
const filePath = filePathInput.value.trim();
if (!filePath) {
messageDiv.style.display = 'block';
messageDiv.style.background = 'rgba(239, 68, 68, 0.2)';
messageDiv.style.color = '#ef4444';
messageDiv.style.border = '1px solid rgba(239, 68, 68, 0.3)';
messageDiv.textContent = 'โ Please enter a file path';
return;
}
try {
copyBtn.disabled = true;
copyBtn.textContent = 'โณ Reading file...';
// Download file content using SFTPdownload.php
const res = await fetch('SFTPdownload.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ path: filePath })
});
if (!res.ok) {
const errorText = await res.text();
throw new Error(errorText || 'File not found or cannot be read');
}
const content = await res.text();
// Show preview
previewDiv.style.display = 'block';
previewDiv.textContent = content.substring(0, 500) + (content.length > 500 ? '...\n\n(Preview truncated)' : '');
// Copy to clipboard
await navigator.clipboard.writeText(content);
messageDiv.style.display = 'block';
messageDiv.style.background = 'rgba(16, 185, 129, 0.2)';
messageDiv.style.color = '#10b981';
messageDiv.style.border = '1px solid rgba(16, 185, 129, 0.3)';
messageDiv.textContent = `โ
Copied ${content.length} characters to clipboard!`;
} catch (err) {
messageDiv.style.display = 'block';
messageDiv.style.background = 'rgba(239, 68, 68, 0.2)';
messageDiv.style.color = '#ef4444';
messageDiv.style.border = '1px solid rgba(239, 68, 68, 0.3)';
messageDiv.textContent = 'โ Error: ' + err.message;
} finally {
copyBtn.disabled = false;
copyBtn.textContent = '๐ Copy to Clipboard';
}
};
}
}, 100);
}
}
});
// Add menu items here
window.AppMenu.push({
label: '๐ Refresh Connection',
action: async () => {
try {
const res = await fetch('SFTPconnector.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'status' })
});
const data = await res.json();
if (data.success && data.data?.connected) {
alert('โ
Connection is active\n\n' +
`Host: ${data.data.config.host}\n` +
`User: ${data.data.config.username}\n` +
`Port: ${data.data.config.port}`);
} else {
alert('โ No active connection');
}
} catch (err) {
alert('Error checking connection: ' + err.message);
}
}
});
window.AppMenu.push({
label: '๐ Disconnect All',
action: async () => {
if (!confirm('Disconnect from SFTP server?')) return;
try {
const res = await fetch('SFTPconnector.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'disconnect' })
});
const data = await res.json();
if (data.success) {
// Update localStorage
const STORAGE_KEY = 'sftp_connections';
const connections = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
connections.forEach(c => c.active = false);
localStorage.setItem(STORAGE_KEY, JSON.stringify(connections));
alert('โ
Disconnected successfully');
// Trigger re-render if on Connections tab
const event = new CustomEvent('sftp-disconnected');
document.dispatchEvent(event);
} else {
alert('โ ' + (data.message || 'Disconnect failed'));
}
} catch (err) {
alert('Error: ' + err.message);
}
}
});
window.AppMenu.push({
label: '๐งช Test SFTP',
action: async () => {
try {
const res = await fetch('SFTPconnector.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'test' })
});
const data = await res.json();
if (data.success) {
const info = data.data;
alert('๐งช SFTP Test Results\n\n' +
`SSH2 Extension: ${info.ssh2_loaded ? 'โ
' : 'โ'}\n` +
`Exec Available: ${info.exec_available ? 'โ
' : 'โ'}\n` +
`Session Active: ${info.session_active ? 'โ
' : 'โ'}\n` +
`PHP Version: ${info.php_version}\n` +
`Preferred Method: ${info.preferred_method}\n` +
`Server Time: ${info.time}`);
} else {
alert('โ Test failed: ' + (data.message || 'Unknown error'));
}
} catch (err) {
alert('Error: ' + err.message);
}
}
});
window.AppMenu.push({
label: '๐๏ธ Clear Saved Connections',
action: () => {
if (!confirm('Delete all saved connections? This cannot be undone.')) return;
localStorage.removeItem('sftp_connections');
alert('โ
All saved connections deleted');
// Trigger re-render if on Connections tab
const event = new CustomEvent('connections-cleared');
document.dispatchEvent(event);
}
});
window.AppMenu.push({
label: 'โน๏ธ About',
action: () => {
alert('SFTP Manager v1.0\n\n' +
'Secure file transfer manager\n' +
'Built with PHP SSH2 Extension\n\n' +
'ยฉ 2025 DevBrewing');
}
});
// Listen for custom events to re-render connections
document.addEventListener('sftp-disconnected', () => {
const grid = document.getElementById('connectionsGrid');
if (grid && typeof renderConnections === 'function') {
renderConnections();
}
});
document.addEventListener('connections-cleared', () => {
const grid = document.getElementById('connectionsGrid');
if (grid && typeof renderConnections === 'function') {
renderConnections();
}
});
console.log('[menu.js] Loaded - 3-dot menu with', window.AppMenu.length, 'items');
})();