init commit

This commit is contained in:
2026-03-31 14:40:03 +03:00
commit 41c8e186ef
37 changed files with 5420 additions and 0 deletions

152
frontend/index.html Normal file
View File

@@ -0,0 +1,152 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>kettuRay</title>
<link rel="stylesheet" href="/src/style.css" />
</head>
<body>
<div id="app">
<!-- Custom title bar -->
<div class="titlebar" data-wails-drag>
<span class="titlebar-title">kettuRay <span class="titlebar-sub">by @khton</span></span>
<div class="titlebar-actions">
<button class="icon-btn" id="minimizeBtn" title="Minimize to tray">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="15 3 21 3 21 9"/><polyline points="9 21 3 21 3 15"/>
<line x1="21" y1="3" x2="14" y2="10"/><line x1="3" y1="21" x2="10" y2="14"/>
</svg>
</button>
</div>
</div>
<!-- Main content column -->
<div id="mainCol">
<!-- Status card -->
<div class="status-card">
<div class="status-top">
<span id="stateDot" class="state-dot disconnected"></span>
<span id="stateText" class="state-text">Disconnected</span>
</div>
<div id="selectedName" class="status-name">not selected</div>
<div class="status-meta">
<span id="protoBadgeStatus" class="proto-badge" style="display:none"></span>
<span id="pingStatus" class="ping-status"></span>
<span id="latencyStatus" class="latency-status"></span>
</div>
</div>
<!-- Configs header -->
<div class="section-header" style="margin-top:18px">
<span class="section-title">Configurations</span>
<div class="header-actions">
<button class="icon-btn" id="pingAllBtn" title="Ping all">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="10" cy="12" r="2"/><path d="M13.4 10.6 L19 5"/><path d="M15.6 2.7a10 10 0 1 0 5.7 5.7"/>
</svg>
</button>
<button class="icon-btn" id="addLinkBtn" title="Add manually">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M15 7h2a5 5 0 0 1 0 10h-2m-6 0H7A5 5 0 0 1 7 7h2"/>
<line x1="8" y1="12" x2="16" y2="12"/>
</svg>
</button>
<button class="icon-btn" id="addClipboardBtn" title="Add from clipboard">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/>
<rect x="8" y="2" width="8" height="4" rx="1" ry="1"/>
</svg>
</button>
</div>
</div>
<!-- Link input panel (hidden by default) -->
<div id="linkInputPanel" class="link-input-panel hidden">
<input type="text" id="linkInputBox" placeholder="vless:// or trojan://..." spellcheck="false" autocomplete="off" />
<button id="submitLinkBtn" class="add-btn">+</button>
</div>
<!-- Config list -->
<div id="configList" class="config-list">
<div id="emptyState" class="empty-state">
No configurations.<br>Copy a link and add it from the clipboard.
</div>
</div>
<!-- Bottom controls -->
<div class="bottom-bar">
<div class="bottom-left">
<button class="icon-btn" id="settingsBtn" title="Settings">
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path id="settingsChevron" d="M2,1 L7,6 L2,11"/>
</svg>
</button>
<button id="connectBtn" class="solid-btn green" style="display:none">Start</button>
<button id="disconnectBtn" class="solid-btn red" style="display:none">Stop</button>
<button id="logBtn" class="solid-btn gray">Log</button>
</div>
<div class="bottom-right">
<button id="exitBtn" class="exit-btn">Exit</button>
</div>
</div>
<!-- Settings panel (hidden by default) -->
<div id="settingsPanel" class="settings-panel hidden">
<div class="settings-card">
<label class="toggle-row">
<span class="toggle-wrap">
<input type="checkbox" id="runOnStartup" />
<span class="toggle-track"><span class="toggle-knob"></span></span>
</span>
<span class="toggle-label">Run on startup</span>
</label>
<label class="toggle-row">
<span class="toggle-wrap">
<input type="checkbox" id="autoConnect" />
<span class="toggle-track"><span class="toggle-knob"></span></span>
</span>
<span class="toggle-label">Auto-connect VPN</span>
</label>
<label class="toggle-row" style="margin-bottom:0">
<span class="toggle-wrap">
<input type="checkbox" id="autoReconnect" />
<span class="toggle-track"><span class="toggle-knob"></span></span>
</span>
<span class="toggle-label">Auto-reconnect</span>
</label>
</div>
</div>
</div>
<!-- Log sidebar -->
<div id="logCol" class="log-col hidden">
<div class="log-header">
<span class="section-title">Logs</span>
<div class="header-actions">
<button class="icon-btn small" id="copyLogsBtn">Copy</button>
<button class="icon-btn small" id="clearLogsBtn">Clear</button>
</div>
</div>
<div id="logArea" class="log-area"></div>
</div>
<!-- Toast notification -->
<div id="toast" class="toast hidden"></div>
<!-- Context menu -->
<div id="ctxMenu" class="ctx-menu hidden">
<div class="ctx-item" id="ctxCopyLink">Copy Link</div>
<div class="ctx-separator"></div>
<div class="ctx-item danger" id="ctxDelete">Delete</div>
</div>
<!-- Notification stack -->
<div id="notifStack" class="notif-stack"></div>
</div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

1143
frontend/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

12
frontend/package.json Normal file
View File

@@ -0,0 +1,12 @@
{
"name": "ketturay-frontend",
"private": true,
"version": "0.0.1",
"scripts": {
"dev": "vite",
"build": "vite build"
},
"devDependencies": {
"vite": "^6.0.0"
}
}

View File

@@ -0,0 +1 @@
51c73c9dd7c8a498b3e4e7db2243affe

477
frontend/src/main.js Normal file
View File

@@ -0,0 +1,477 @@
import {
GetConfigs, AddConfig, DeleteConfig,
SetSelectedConfig, GetSelectedConfigID,
Connect, Disconnect, GetState,
PingAll,
GetSettings, SaveSettings,
ResizeWindow, HideWindow,
} from '../wailsjs/go/main/App.js';
import { EventsOn, Quit } from '../wailsjs/runtime/runtime.js';
// ── DOM refs ──────────────────────────────────────────────────────────────────
const stateDot = document.getElementById('stateDot');
const stateText = document.getElementById('stateText');
const selectedName = document.getElementById('selectedName');
const protoBadge = document.getElementById('protoBadgeStatus');
const pingStatus = document.getElementById('pingStatus');
const latencyStatus = document.getElementById('latencyStatus');
const pingAllBtn = document.getElementById('pingAllBtn');
const addLinkBtn = document.getElementById('addLinkBtn');
const addClipboardBtn= document.getElementById('addClipboardBtn');
const linkInputPanel = document.getElementById('linkInputPanel');
const linkInputBox = document.getElementById('linkInputBox');
const submitLinkBtn = document.getElementById('submitLinkBtn');
const configList = document.getElementById('configList');
const emptyState = document.getElementById('emptyState');
const connectBtn = document.getElementById('connectBtn');
const disconnectBtn = document.getElementById('disconnectBtn');
const logBtn = document.getElementById('logBtn');
const settingsBtn = document.getElementById('settingsBtn');
const exitBtn = document.getElementById('exitBtn');
const minimizeBtn = document.getElementById('minimizeBtn');
const settingsPanel = document.getElementById('settingsPanel');
const settingsChevron= document.getElementById('settingsChevron');
const runOnStartup = document.getElementById('runOnStartup');
const autoConnect = document.getElementById('autoConnect');
const autoReconnect = document.getElementById('autoReconnect');
const logCol = document.getElementById('logCol');
const logArea = document.getElementById('logArea');
const copyLogsBtn = document.getElementById('copyLogsBtn');
const clearLogsBtn = document.getElementById('clearLogsBtn');
const toast = document.getElementById('toast');
const ctxMenu = document.getElementById('ctxMenu');
const ctxCopyLink = document.getElementById('ctxCopyLink');
const ctxDelete = document.getElementById('ctxDelete');
// ── State ─────────────────────────────────────────────────────────────────────
let currentState = 'Disconnected';
let selectedID = '';
let configs = [];
let logExpanded = false;
let settingsExpanded= false;
let linkPanelOpen = false;
let ctxTargetID = null;
let deleteConfirmTimeout = null;
// Window heights
const BASE_H = 500;
const SETTINGS_H = 80; // extra height for settings panel
const BASE_W = 360;
const LOG_W = 360;
// ── Helpers ───────────────────────────────────────────────────────────────────
function currentWindowSize() {
const h = BASE_H + (settingsExpanded ? SETTINGS_H : 0);
const w = BASE_W + (logExpanded ? LOG_W : 0);
return { w, h };
}
function applyWindowSize() {
const { w, h } = currentWindowSize();
ResizeWindow(w, h);
}
// ── State UI ──────────────────────────────────────────────────────────────────
function updateStateUI(state) {
currentState = state;
const s = state.toLowerCase();
stateDot.className = 'state-dot ' + s;
stateText.textContent = state;
connectBtn.style.display = 'none';
disconnectBtn.style.display = 'none';
switch (state) {
case 'Disconnected':
case 'Error':
connectBtn.style.display = 'inline-flex';
connectBtn.disabled = !selectedID;
break;
case 'Connecting':
disconnectBtn.style.display = 'inline-flex';
disconnectBtn.disabled = false;
break;
case 'Connected':
disconnectBtn.style.display = 'inline-flex';
disconnectBtn.disabled = false;
break;
case 'Disconnecting':
disconnectBtn.style.display = 'inline-flex';
disconnectBtn.disabled = true;
break;
}
}
// ── Config list rendering ─────────────────────────────────────────────────────
function renderConfigs() {
// Remove all config items but keep emptyState
configList.querySelectorAll('.config-item').forEach(el => el.remove());
emptyState.style.display = configs.length === 0 ? 'block' : 'none';
for (const cfg of configs) {
const item = document.createElement('div');
item.className = 'config-item' + (cfg.id === selectedID ? ' selected' : '');
item.dataset.id = cfg.id;
const name = document.createElement('span');
name.className = 'config-name';
name.textContent = cfg.name;
const proto = document.createElement('span');
proto.className = 'config-proto ' + cfg.protocolType;
proto.textContent = cfg.protocolType;
const menuBtn = document.createElement('button');
menuBtn.className = 'config-menu-btn';
menuBtn.textContent = '⋮';
menuBtn.addEventListener('click', e => {
e.stopPropagation();
openCtxMenu(cfg.id, menuBtn);
});
item.appendChild(name);
if (cfg.ping) {
const pingBadge = document.createElement('span');
pingBadge.className = 'config-ping';
pingBadge.textContent = cfg.ping;
pingBadge.style.borderColor = cfg.pingColor;
pingBadge.style.color = cfg.pingColor;
item.appendChild(pingBadge);
}
item.appendChild(proto);
item.appendChild(menuBtn);
// Single click → select
item.addEventListener('click', () => selectConfig(cfg.id));
// Double click → connect
item.addEventListener('dblclick', async () => {
selectConfig(cfg.id);
if (currentState === 'Connected' || currentState === 'Connecting') {
await Disconnect();
}
Connect(cfg.id);
});
configList.insertBefore(item, emptyState);
}
updateStatusCard();
}
function selectConfig(id) {
selectedID = id;
SetSelectedConfig(id);
configList.querySelectorAll('.config-item').forEach(el => {
el.classList.toggle('selected', el.dataset.id === id);
});
connectBtn.disabled = false;
updateStatusCard();
}
function updateStatusCard() {
const cfg = configs.find(c => c.id === selectedID);
if (!cfg) {
selectedName.textContent = 'not selected';
protoBadge.style.display = 'none';
pingStatus.textContent = 'timeout';
pingStatus.style.color = '#E53935';
latencyStatus.textContent = '';
return;
}
selectedName.textContent = cfg.name;
if (currentState === 'Connected') {
protoBadge.style.display = 'none';
pingStatus.textContent = 'connected';
pingStatus.style.color = '#4CAF50';
} else {
protoBadge.textContent = cfg.protocolType;
protoBadge.className = 'proto-badge ' + cfg.protocolType;
protoBadge.style.display = '';
pingStatus.textContent = cfg.protocolType;
pingStatus.style.color = '#666';
latencyStatus.textContent = '';
}
}
// Update just ping badge for one config (from ping-update event)
function updatePingBadge(id, pingText, pingColor) {
const cfg = configs.find(c => c.id === id);
if (cfg) {
cfg.ping = pingText;
cfg.pingColor = pingColor;
}
const item = configList.querySelector(`[data-id="${id}"]`);
if (!item) return;
let badge = item.querySelector('.config-ping');
if (!pingText) {
if (badge) badge.remove();
return;
}
if (!badge) {
badge = document.createElement('span');
badge.className = 'config-ping';
const proto = item.querySelector('.config-proto');
item.insertBefore(badge, proto);
}
badge.textContent = pingText;
badge.style.borderColor = pingColor;
badge.style.color = pingColor;
}
// ── Context menu ──────────────────────────────────────────────────────────────
function openCtxMenu(id, anchor) {
ctxTargetID = id;
ctxDelete.textContent = 'Delete';
ctxDelete.className = 'ctx-item danger';
if (deleteConfirmTimeout) { clearTimeout(deleteConfirmTimeout); deleteConfirmTimeout = null; }
ctxMenu.classList.remove('hidden');
const rect = anchor.getBoundingClientRect();
ctxMenu.style.top = rect.bottom + 4 + 'px';
ctxMenu.style.left = Math.min(rect.left, window.innerWidth - 150) + 'px';
}
function closeCtxMenu() {
ctxMenu.classList.add('hidden');
ctxTargetID = null;
}
ctxCopyLink.addEventListener('click', () => {
const cfg = configs.find(c => c.id === ctxTargetID);
if (cfg) {
navigator.clipboard.writeText(cfg.link);
showToast('Link copied!', false);
}
closeCtxMenu();
});
ctxDelete.addEventListener('click', () => {
if (ctxDelete.textContent !== 'Are you sure?') {
ctxDelete.textContent = 'Are you sure?';
ctxDelete.className = 'ctx-item danger';
deleteConfirmTimeout = setTimeout(() => {
ctxDelete.textContent = 'Delete';
deleteConfirmTimeout = null;
}, 3000);
return;
}
const id = ctxTargetID;
closeCtxMenu();
DeleteConfig(id);
configs = configs.filter(c => c.id !== id);
if (selectedID === id) {
selectedID = configs.length > 0 ? configs[0].id : '';
if (selectedID) SetSelectedConfig(selectedID);
}
renderConfigs();
});
document.addEventListener('click', e => {
if (!ctxMenu.contains(e.target)) closeCtxMenu();
});
// ── Link input panel ──────────────────────────────────────────────────────────
addLinkBtn.addEventListener('click', () => {
linkPanelOpen = !linkPanelOpen;
linkInputPanel.classList.toggle('hidden', !linkPanelOpen);
if (linkPanelOpen) linkInputBox.focus();
});
submitLinkBtn.addEventListener('click', submitLink);
linkInputBox.addEventListener('keydown', e => { if (e.key === 'Enter') submitLink(); });
async function submitLink() {
const link = linkInputBox.value.trim();
if (!link) { showToast('Enter a link', true); return; }
const err = await AddConfig(link);
if (err) { showToast(err, true); return; }
linkInputBox.value = '';
linkPanelOpen = false;
linkInputPanel.classList.add('hidden');
const fresh = await GetConfigs();
configs = fresh;
// Select newly added
if (fresh.length > 0) selectConfig(fresh[fresh.length - 1].id);
renderConfigs();
showToast('Configuration added', false);
}
addClipboardBtn.addEventListener('click', async () => {
try {
const text = await navigator.clipboard.readText();
if (!text) { showToast('Clipboard is empty', true); return; }
const err = await AddConfig(text.trim());
if (err) { showToast(err, true); return; }
const fresh = await GetConfigs();
configs = fresh;
if (fresh.length > 0) selectConfig(fresh[fresh.length - 1].id);
renderConfigs();
showToast('Configuration added', false);
} catch {
showToast('Clipboard access denied', true);
}
});
// ── Connect / Disconnect ──────────────────────────────────────────────────────
connectBtn.addEventListener('click', () => {
if (!selectedID) return;
Connect(selectedID);
});
disconnectBtn.addEventListener('click', () => {
Disconnect();
});
// ── Ping all ──────────────────────────────────────────────────────────────────
pingAllBtn.addEventListener('click', () => {
pingAllBtn.disabled = true;
PingAll();
setTimeout(() => { pingAllBtn.disabled = false; }, 5000);
});
// ── Log panel ─────────────────────────────────────────────────────────────────
logBtn.addEventListener('click', () => {
logExpanded = !logExpanded;
logCol.classList.toggle('hidden', !logExpanded);
applyWindowSize();
});
copyLogsBtn.addEventListener('click', () => {
navigator.clipboard.writeText(logArea.innerText);
copyLogsBtn.textContent = 'Copied!';
setTimeout(() => { copyLogsBtn.textContent = 'Copy'; }, 1500);
});
clearLogsBtn.addEventListener('click', () => { logArea.innerHTML = ''; });
function appendLog(source, message) {
const line = document.createElement('div');
line.className = 'log-line';
const isErr = /\bERROR\b|\bFATAL\b|\[ERR\]/.test(message) && !/\bINFO\b|\bWARN\b/.test(message);
if (isErr) line.classList.add('err');
const ts = new Date().toLocaleTimeString();
line.textContent = `[${ts}] [${source}] ${message}`;
logArea.appendChild(line);
logArea.scrollTop = logArea.scrollHeight;
}
// ── Settings panel ────────────────────────────────────────────────────────────
settingsBtn.addEventListener('click', () => {
settingsExpanded = !settingsExpanded;
settingsPanel.classList.toggle('hidden', !settingsExpanded);
settingsChevron.setAttribute('d', settingsExpanded ? 'M1,2 L6,7 L11,2' : 'M2,1 L7,6 L2,11');
applyWindowSize();
});
function saveSettings() {
SaveSettings(runOnStartup.checked, autoConnect.checked, autoReconnect.checked);
}
runOnStartup.addEventListener('change', saveSettings);
autoConnect.addEventListener('change', saveSettings);
autoReconnect.addEventListener('change', saveSettings);
// ── Window controls ───────────────────────────────────────────────────────────
minimizeBtn.addEventListener('click', () => HideWindow());
exitBtn.addEventListener('click', () => Quit());
// ── Toast ─────────────────────────────────────────────────────────────────────
let toastTimeout;
function showToast(msg, isError) {
clearTimeout(toastTimeout);
toast.textContent = msg;
toast.className = 'toast' + (isError ? '' : ' green');
// force reflow
toast.offsetHeight;
toast.classList.add('show');
toastTimeout = setTimeout(() => toast.classList.remove('show'), 3000);
}
// ── Wails events ──────────────────────────────────────────────────────────────
EventsOn('state-changed', state => {
updateStateUI(state);
updateStatusCard();
});
EventsOn('log', (source, message) => {
appendLog(source, message);
});
EventsOn('ping-update', (id, pingText, pingColor) => {
updatePingBadge(id, pingText, pingColor);
if (id === selectedID) updateStatusCard();
});
EventsOn('toast', (msg, isError) => showToast(msg, isError));
// ── Custom notifications ──────────────────────────────────────────────────────
const notifStack = document.getElementById('notifStack');
function showNotification(message, dotColor) {
const item = document.createElement('div');
item.className = 'notif-item';
const dot = document.createElement('span');
dot.className = 'notif-dot';
dot.style.background = dotColor;
const text = document.createElement('span');
text.className = 'notif-text';
text.textContent = message;
item.appendChild(dot);
item.appendChild(text);
notifStack.appendChild(item);
// Auto-dismiss after 4s
setTimeout(() => {
item.classList.add('out');
item.addEventListener('animationend', () => item.remove());
}, 4000);
}
EventsOn('notification', (msg, dotColor) => showNotification(msg, dotColor));
// ── Init ──────────────────────────────────────────────────────────────────────
async function init() {
try {
configs = await GetConfigs() || [];
const state = await GetState();
selectedID = await GetSelectedConfigID();
const settings = await GetSettings();
runOnStartup.checked = settings.RunOnStartup;
autoConnect.checked = settings.AutoConnect;
autoReconnect.checked = settings.AutoReconnect;
renderConfigs();
updateStateUI(state);
} catch (e) {
console.error('init error:', e);
// Retry after a short delay
setTimeout(init, 500);
}
}
// Wait for Wails runtime to be fully ready before calling Go bindings
document.addEventListener('wails:loaded', () => init());
// Fallback in case the event already fired
if (window?.go?.main?.App?.GetConfigs) {
init();
}

494
frontend/src/style.css Normal file
View File

@@ -0,0 +1,494 @@
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: "Segoe UI", Inter, Roboto, sans-serif;
background: #1e1e1e;
color: #dedede;
height: 100vh;
overflow: hidden;
user-select: none;
}
#app {
display: flex;
height: 100vh;
border: 1px solid #333;
border-radius: 8px;
overflow: hidden;
}
/* ── Titlebar ── */
.titlebar {
position: fixed;
top: 0; left: 0; right: 0;
height: 30px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 8px 0 15px;
z-index: 100;
background: transparent;
--wails-draggable: drag;
}
.titlebar-title {
font-size: 12px;
font-weight: 600;
color: #666;
pointer-events: none;
}
.titlebar-sub { font-weight: 400; }
.titlebar-actions { display: flex; gap: 2px; --wails-draggable: no-drag; }
/* ── Main column ── */
#mainCol {
width: 360px;
flex-shrink: 0;
display: flex;
flex-direction: column;
padding: 40px 15px 15px;
overflow: hidden;
}
/* ── Status card ── */
.status-card {
background: #2b2b2b;
border-radius: 8px;
padding: 14px 15px;
flex-shrink: 0;
}
.status-top {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
margin-bottom: 8px;
}
.state-dot {
width: 14px; height: 14px;
border-radius: 50%;
flex-shrink: 0;
transition: background 0.3s;
}
.state-dot.disconnected { background: #E53935; }
.state-dot.connecting { background: #FDD835; }
.state-dot.connected { background: #4CAF50; }
.state-dot.disconnecting{ background: #FDD835; }
.state-dot.error { background: #b71c1c; }
.state-text {
font-size: 18px;
font-weight: 700;
color: #fff;
}
.status-name {
text-align: center;
font-size: 12px;
color: #aaa;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
}
.status-meta {
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
margin-top: 4px;
min-height: 18px;
}
.ping-status {
font-size: 11px;
font-weight: 600;
color: #666;
}
.latency-status {
font-size: 11px;
font-weight: 600;
color: #4CAF50;
}
.proto-badge {
font-size: 10px;
font-weight: 700;
padding: 2px 6px;
border-radius: 4px;
border: 1px solid #888;
color: #888;
line-height: 1;
}
.proto-badge.VLESS { border-color: #00E676; color: #00E676; }
.proto-badge.TROJAN { border-color: #00BFFF; color: #00BFFF; }
/* ── Section header ── */
.section-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 5px;
flex-shrink: 0;
}
.section-title {
font-size: 12px;
font-weight: 700;
color: #555;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.header-actions { display: flex; gap: 2px; }
/* ── Link input panel ── */
.link-input-panel {
display: flex;
gap: 6px;
margin-bottom: 8px;
flex-shrink: 0;
}
.link-input-panel.hidden { display: none; }
.link-input-panel input {
flex: 1;
background: #222;
border: none;
border-radius: 4px;
padding: 6px 10px;
color: #ccc;
font-size: 12px;
font-family: "Consolas", monospace;
outline: none;
}
.add-btn {
background: #4CAF50;
color: #fff;
border: none;
border-radius: 4px;
width: 30px;
font-size: 18px;
font-weight: 700;
cursor: pointer;
transition: opacity 0.15s;
line-height: 1;
}
.add-btn:hover { opacity: 0.8; }
/* ── Config list ── */
.config-list {
flex: 1;
overflow-y: auto;
overflow-x: hidden;
scrollbar-width: thin;
scrollbar-color: #555 transparent;
min-height: 0;
}
.config-list::-webkit-scrollbar { width: 6px; }
.config-list::-webkit-scrollbar-thumb { background: #555; border-radius: 3px; }
.config-list::-webkit-scrollbar-track { background: transparent; }
.empty-state {
text-align: center;
color: #555;
font-size: 13px;
line-height: 1.6;
margin: 20px;
}
.config-item {
display: flex;
align-items: center;
gap: 6px;
padding: 8px;
border-radius: 4px;
cursor: pointer;
transition: background 0.12s;
}
.config-item:hover { background: #2a2a2a; }
.config-item.selected { background: #2f2f2f; }
.config-name {
flex: 1;
font-size: 14px;
font-weight: 600;
color: #eee;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.config-ping {
font-size: 10px;
font-weight: 700;
padding: 2px 5px;
border-radius: 4px;
border: 1px solid;
line-height: 1;
flex-shrink: 0;
white-space: nowrap;
}
.config-proto {
font-size: 10px;
font-weight: 700;
padding: 2px 6px;
border-radius: 4px;
border: 1px solid #888;
color: #888;
line-height: 1;
flex-shrink: 0;
}
.config-proto.VLESS { border-color: #00E676; color: #00E676; }
.config-proto.TROJAN { border-color: #00BFFF; color: #00BFFF; }
.config-menu-btn {
background: transparent;
border: none;
color: #888;
font-size: 16px;
font-weight: 700;
width: 24px;
height: 24px;
border-radius: 4px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
transition: background 0.12s, color 0.12s;
}
.config-menu-btn:hover { background: #333; color: #fff; }
/* ── Bottom bar ── */
.bottom-bar {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 15px;
flex-shrink: 0;
}
.bottom-left { display: flex; align-items: center; gap: 8px; }
.bottom-right { display: flex; align-items: center; }
/* ── Buttons ── */
.icon-btn {
background: transparent;
border: none;
color: #777;
padding: 5px;
border-radius: 4px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.12s, color 0.12s;
line-height: 1;
}
.icon-btn:hover { background: #333; color: #fff; }
.icon-btn.small { font-size: 11px; padding: 3px 7px; }
.solid-btn {
border: none;
color: #fff;
font-size: 13px;
font-weight: 500;
padding: 8px 15px;
border-radius: 6px;
cursor: pointer;
transition: opacity 0.15s;
}
.solid-btn:hover { opacity: 0.8; }
.solid-btn:disabled{ opacity: 0.4; cursor: not-allowed; }
.solid-btn.green { background: #4CAF50; }
.solid-btn.red { background: #E53935; }
.solid-btn.gray { background: #404040; }
.exit-btn {
background: transparent;
border: 1px solid #E53935;
color: #E53935;
font-size: 12px;
font-weight: 600;
padding: 4px 12px;
border-radius: 4px;
cursor: pointer;
transition: background 0.15s;
}
.exit-btn:hover { background: #E5393520; }
/* ── Settings panel ── */
.settings-panel { flex-shrink: 0; margin-top: 15px; }
.settings-panel.hidden { display: none; }
.settings-card {
background: #2b2b2b;
border-radius: 8px;
padding: 15px;
display: flex;
flex-direction: column;
gap: 12px;
}
.toggle-row {
display: flex;
align-items: center;
gap: 10px;
cursor: pointer;
}
.toggle-row input[type="checkbox"] { display: none; }
.toggle-wrap { flex-shrink: 0; }
.toggle-track {
display: block;
width: 40px; height: 20px;
background: #4d4d4d;
border-radius: 10px;
position: relative;
transition: background 0.2s;
}
.toggle-knob {
display: block;
width: 16px; height: 16px;
background: #fff;
border-radius: 50%;
position: absolute;
top: 2px; left: 2px;
transition: left 0.2s;
}
input[type="checkbox"]:checked ~ .toggle-track { background: #4CAF50; }
input[type="checkbox"]:checked ~ .toggle-track .toggle-knob { left: 22px; }
.toggle-label { font-size: 13px; color: #dedede; }
/* ── Log sidebar ── */
.log-col {
width: 360px;
flex-shrink: 0;
border-left: 1px solid #333;
background: #161616;
display: flex;
flex-direction: column;
padding: 40px 15px 15px;
}
.log-col.hidden { display: none; }
.log-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 10px;
flex-shrink: 0;
}
.log-area {
flex: 1;
overflow-y: auto;
font-family: "Consolas", "Courier New", monospace;
font-size: 11px;
line-height: 1.6;
color: #a1e7b2;
user-select: text;
scrollbar-width: thin;
scrollbar-color: #444 transparent;
word-break: break-all;
}
.log-area::-webkit-scrollbar { width: 6px; }
.log-area::-webkit-scrollbar-thumb { background: #444; border-radius: 3px; }
.log-line { white-space: pre-wrap; }
.log-line.err { color: #ef9a9a; }
/* ── Toast ── */
.toast {
position: fixed;
top: 50px;
left: 50%;
transform: translateX(-50%) translateY(-10px);
background: #b71c1c;
color: #fff;
font-size: 13px;
padding: 10px 16px;
border-radius: 8px;
z-index: 200;
pointer-events: none;
opacity: 0;
transition: opacity 0.2s, transform 0.3s;
white-space: nowrap;
max-width: 300px;
text-align: center;
}
.toast.show {
opacity: 1;
transform: translateX(-50%) translateY(0);
}
.toast.green { background: #2e7d32; }
/* ── Context menu ── */
.ctx-menu {
position: fixed;
background: #252525;
border: 1px solid #3a3a3a;
border-radius: 6px;
padding: 3px;
z-index: 300;
min-width: 130px;
}
.ctx-menu.hidden { display: none; }
.ctx-item {
padding: 6px 12px;
font-size: 13px;
color: #ddd;
border-radius: 4px;
cursor: pointer;
transition: background 0.1s;
}
.ctx-item:hover { background: #3a3a3a; color: #fff; }
.ctx-item.danger { color: #E53935; }
.ctx-item.danger:hover { background: #3a1a1a; }
.ctx-separator {
height: 1px;
background: #3a3a3a;
margin: 3px 5px;
}
/* ── Custom notifications ── */
.notif-stack {
position: fixed;
bottom: 16px;
left: 16px;
display: flex;
flex-direction: column-reverse;
gap: 8px;
z-index: 500;
pointer-events: none;
}
.notif-item {
display: flex;
align-items: center;
gap: 10px;
background: #161b2e;
border: 1px solid #252d45;
border-radius: 10px;
padding: 10px 18px;
min-width: 200px;
max-width: 320px;
pointer-events: auto;
opacity: 0;
transform: translateY(12px);
animation: notifIn 0.3s ease forwards;
}
.notif-item.out {
animation: notifOut 0.3s ease forwards;
}
.notif-dot {
width: 10px;
height: 10px;
border-radius: 50%;
flex-shrink: 0;
}
.notif-text {
font-size: 13px;
font-weight: 500;
color: #e0e0e0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
@keyframes notifIn {
from { opacity: 0; transform: translateY(12px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes notifOut {
from { opacity: 1; transform: translateY(0); }
to { opacity: 0; transform: translateY(-8px); }
}

11
frontend/vite.config.js Normal file
View File

@@ -0,0 +1,11 @@
import { defineConfig } from 'vite'
export default defineConfig({
build: {
outDir: 'dist',
emptyOutDir: true,
rollupOptions: {
input: 'index.html'
}
}
})

30
frontend/wailsjs/go/main/App.d.ts vendored Normal file
View File

@@ -0,0 +1,30 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
import {main} from '../models';
import {core} from '../models';
export function AddConfig(arg1:string):Promise<string>;
export function Connect(arg1:string):Promise<void>;
export function DeleteConfig(arg1:string):Promise<void>;
export function Disconnect():Promise<void>;
export function GetConfigs():Promise<Array<main.VpnConfigItem>>;
export function GetSelectedConfigID():Promise<string>;
export function GetSettings():Promise<core.AppSettings>;
export function GetState():Promise<string>;
export function HideWindow():Promise<void>;
export function PingAll():Promise<void>;
export function ResizeWindow(arg1:number,arg2:number):Promise<void>;
export function SaveSettings(arg1:boolean,arg2:boolean,arg3:boolean):Promise<void>;
export function SetSelectedConfig(arg1:string):Promise<void>;

View File

@@ -0,0 +1,55 @@
// @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
export function AddConfig(arg1) {
return window['go']['main']['App']['AddConfig'](arg1);
}
export function Connect(arg1) {
return window['go']['main']['App']['Connect'](arg1);
}
export function DeleteConfig(arg1) {
return window['go']['main']['App']['DeleteConfig'](arg1);
}
export function Disconnect() {
return window['go']['main']['App']['Disconnect']();
}
export function GetConfigs() {
return window['go']['main']['App']['GetConfigs']();
}
export function GetSelectedConfigID() {
return window['go']['main']['App']['GetSelectedConfigID']();
}
export function GetSettings() {
return window['go']['main']['App']['GetSettings']();
}
export function GetState() {
return window['go']['main']['App']['GetState']();
}
export function HideWindow() {
return window['go']['main']['App']['HideWindow']();
}
export function PingAll() {
return window['go']['main']['App']['PingAll']();
}
export function ResizeWindow(arg1, arg2) {
return window['go']['main']['App']['ResizeWindow'](arg1, arg2);
}
export function SaveSettings(arg1, arg2, arg3) {
return window['go']['main']['App']['SaveSettings'](arg1, arg2, arg3);
}
export function SetSelectedConfig(arg1) {
return window['go']['main']['App']['SetSelectedConfig'](arg1);
}

View File

@@ -0,0 +1,50 @@
export namespace core {
export class AppSettings {
RunOnStartup: boolean;
AutoConnect: boolean;
AutoReconnect: boolean;
LastConfigId?: string;
static createFrom(source: any = {}) {
return new AppSettings(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.RunOnStartup = source["RunOnStartup"];
this.AutoConnect = source["AutoConnect"];
this.AutoReconnect = source["AutoReconnect"];
this.LastConfigId = source["LastConfigId"];
}
}
}
export namespace main {
export class VpnConfigItem {
id: string;
name: string;
link: string;
protocolType: string;
ping: string;
pingColor: string;
static createFrom(source: any = {}) {
return new VpnConfigItem(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.id = source["id"];
this.name = source["name"];
this.link = source["link"];
this.protocolType = source["protocolType"];
this.ping = source["ping"];
this.pingColor = source["pingColor"];
}
}
}

View File

@@ -0,0 +1,24 @@
{
"name": "@wailsapp/runtime",
"version": "2.0.0",
"description": "Wails Javascript runtime library",
"main": "runtime.js",
"types": "runtime.d.ts",
"scripts": {
},
"repository": {
"type": "git",
"url": "git+https://github.com/wailsapp/wails.git"
},
"keywords": [
"Wails",
"Javascript",
"Go"
],
"author": "Lea Anthony <lea.anthony@gmail.com>",
"license": "MIT",
"bugs": {
"url": "https://github.com/wailsapp/wails/issues"
},
"homepage": "https://github.com/wailsapp/wails#readme"
}

330
frontend/wailsjs/runtime/runtime.d.ts vendored Normal file
View File

@@ -0,0 +1,330 @@
/*
_ __ _ __
| | / /___ _(_) /____
| | /| / / __ `/ / / ___/
| |/ |/ / /_/ / / (__ )
|__/|__/\__,_/_/_/____/
The electron alternative for Go
(c) Lea Anthony 2019-present
*/
export interface Position {
x: number;
y: number;
}
export interface Size {
w: number;
h: number;
}
export interface Screen {
isCurrent: boolean;
isPrimary: boolean;
width : number
height : number
}
// Environment information such as platform, buildtype, ...
export interface EnvironmentInfo {
buildType: string;
platform: string;
arch: string;
}
// [EventsEmit](https://wails.io/docs/reference/runtime/events#eventsemit)
// emits the given event. Optional data may be passed with the event.
// This will trigger any event listeners.
export function EventsEmit(eventName: string, ...data: any): void;
// [EventsOn](https://wails.io/docs/reference/runtime/events#eventson) sets up a listener for the given event name.
export function EventsOn(eventName: string, callback: (...data: any) => void): () => void;
// [EventsOnMultiple](https://wails.io/docs/reference/runtime/events#eventsonmultiple)
// sets up a listener for the given event name, but will only trigger a given number times.
export function EventsOnMultiple(eventName: string, callback: (...data: any) => void, maxCallbacks: number): () => void;
// [EventsOnce](https://wails.io/docs/reference/runtime/events#eventsonce)
// sets up a listener for the given event name, but will only trigger once.
export function EventsOnce(eventName: string, callback: (...data: any) => void): () => void;
// [EventsOff](https://wails.io/docs/reference/runtime/events#eventsoff)
// unregisters the listener for the given event name.
export function EventsOff(eventName: string, ...additionalEventNames: string[]): void;
// [EventsOffAll](https://wails.io/docs/reference/runtime/events#eventsoffall)
// unregisters all listeners.
export function EventsOffAll(): void;
// [LogPrint](https://wails.io/docs/reference/runtime/log#logprint)
// logs the given message as a raw message
export function LogPrint(message: string): void;
// [LogTrace](https://wails.io/docs/reference/runtime/log#logtrace)
// logs the given message at the `trace` log level.
export function LogTrace(message: string): void;
// [LogDebug](https://wails.io/docs/reference/runtime/log#logdebug)
// logs the given message at the `debug` log level.
export function LogDebug(message: string): void;
// [LogError](https://wails.io/docs/reference/runtime/log#logerror)
// logs the given message at the `error` log level.
export function LogError(message: string): void;
// [LogFatal](https://wails.io/docs/reference/runtime/log#logfatal)
// logs the given message at the `fatal` log level.
// The application will quit after calling this method.
export function LogFatal(message: string): void;
// [LogInfo](https://wails.io/docs/reference/runtime/log#loginfo)
// logs the given message at the `info` log level.
export function LogInfo(message: string): void;
// [LogWarning](https://wails.io/docs/reference/runtime/log#logwarning)
// logs the given message at the `warning` log level.
export function LogWarning(message: string): void;
// [WindowReload](https://wails.io/docs/reference/runtime/window#windowreload)
// Forces a reload by the main application as well as connected browsers.
export function WindowReload(): void;
// [WindowReloadApp](https://wails.io/docs/reference/runtime/window#windowreloadapp)
// Reloads the application frontend.
export function WindowReloadApp(): void;
// [WindowSetAlwaysOnTop](https://wails.io/docs/reference/runtime/window#windowsetalwaysontop)
// Sets the window AlwaysOnTop or not on top.
export function WindowSetAlwaysOnTop(b: boolean): void;
// [WindowSetSystemDefaultTheme](https://wails.io/docs/next/reference/runtime/window#windowsetsystemdefaulttheme)
// *Windows only*
// Sets window theme to system default (dark/light).
export function WindowSetSystemDefaultTheme(): void;
// [WindowSetLightTheme](https://wails.io/docs/next/reference/runtime/window#windowsetlighttheme)
// *Windows only*
// Sets window to light theme.
export function WindowSetLightTheme(): void;
// [WindowSetDarkTheme](https://wails.io/docs/next/reference/runtime/window#windowsetdarktheme)
// *Windows only*
// Sets window to dark theme.
export function WindowSetDarkTheme(): void;
// [WindowCenter](https://wails.io/docs/reference/runtime/window#windowcenter)
// Centers the window on the monitor the window is currently on.
export function WindowCenter(): void;
// [WindowSetTitle](https://wails.io/docs/reference/runtime/window#windowsettitle)
// Sets the text in the window title bar.
export function WindowSetTitle(title: string): void;
// [WindowFullscreen](https://wails.io/docs/reference/runtime/window#windowfullscreen)
// Makes the window full screen.
export function WindowFullscreen(): void;
// [WindowUnfullscreen](https://wails.io/docs/reference/runtime/window#windowunfullscreen)
// Restores the previous window dimensions and position prior to full screen.
export function WindowUnfullscreen(): void;
// [WindowIsFullscreen](https://wails.io/docs/reference/runtime/window#windowisfullscreen)
// Returns the state of the window, i.e. whether the window is in full screen mode or not.
export function WindowIsFullscreen(): Promise<boolean>;
// [WindowSetSize](https://wails.io/docs/reference/runtime/window#windowsetsize)
// Sets the width and height of the window.
export function WindowSetSize(width: number, height: number): void;
// [WindowGetSize](https://wails.io/docs/reference/runtime/window#windowgetsize)
// Gets the width and height of the window.
export function WindowGetSize(): Promise<Size>;
// [WindowSetMaxSize](https://wails.io/docs/reference/runtime/window#windowsetmaxsize)
// Sets the maximum window size. Will resize the window if the window is currently larger than the given dimensions.
// Setting a size of 0,0 will disable this constraint.
export function WindowSetMaxSize(width: number, height: number): void;
// [WindowSetMinSize](https://wails.io/docs/reference/runtime/window#windowsetminsize)
// Sets the minimum window size. Will resize the window if the window is currently smaller than the given dimensions.
// Setting a size of 0,0 will disable this constraint.
export function WindowSetMinSize(width: number, height: number): void;
// [WindowSetPosition](https://wails.io/docs/reference/runtime/window#windowsetposition)
// Sets the window position relative to the monitor the window is currently on.
export function WindowSetPosition(x: number, y: number): void;
// [WindowGetPosition](https://wails.io/docs/reference/runtime/window#windowgetposition)
// Gets the window position relative to the monitor the window is currently on.
export function WindowGetPosition(): Promise<Position>;
// [WindowHide](https://wails.io/docs/reference/runtime/window#windowhide)
// Hides the window.
export function WindowHide(): void;
// [WindowShow](https://wails.io/docs/reference/runtime/window#windowshow)
// Shows the window, if it is currently hidden.
export function WindowShow(): void;
// [WindowMaximise](https://wails.io/docs/reference/runtime/window#windowmaximise)
// Maximises the window to fill the screen.
export function WindowMaximise(): void;
// [WindowToggleMaximise](https://wails.io/docs/reference/runtime/window#windowtogglemaximise)
// Toggles between Maximised and UnMaximised.
export function WindowToggleMaximise(): void;
// [WindowUnmaximise](https://wails.io/docs/reference/runtime/window#windowunmaximise)
// Restores the window to the dimensions and position prior to maximising.
export function WindowUnmaximise(): void;
// [WindowIsMaximised](https://wails.io/docs/reference/runtime/window#windowismaximised)
// Returns the state of the window, i.e. whether the window is maximised or not.
export function WindowIsMaximised(): Promise<boolean>;
// [WindowMinimise](https://wails.io/docs/reference/runtime/window#windowminimise)
// Minimises the window.
export function WindowMinimise(): void;
// [WindowUnminimise](https://wails.io/docs/reference/runtime/window#windowunminimise)
// Restores the window to the dimensions and position prior to minimising.
export function WindowUnminimise(): void;
// [WindowIsMinimised](https://wails.io/docs/reference/runtime/window#windowisminimised)
// Returns the state of the window, i.e. whether the window is minimised or not.
export function WindowIsMinimised(): Promise<boolean>;
// [WindowIsNormal](https://wails.io/docs/reference/runtime/window#windowisnormal)
// Returns the state of the window, i.e. whether the window is normal or not.
export function WindowIsNormal(): Promise<boolean>;
// [WindowSetBackgroundColour](https://wails.io/docs/reference/runtime/window#windowsetbackgroundcolour)
// Sets the background colour of the window to the given RGBA colour definition. This colour will show through for all transparent pixels.
export function WindowSetBackgroundColour(R: number, G: number, B: number, A: number): void;
// [ScreenGetAll](https://wails.io/docs/reference/runtime/window#screengetall)
// Gets the all screens. Call this anew each time you want to refresh data from the underlying windowing system.
export function ScreenGetAll(): Promise<Screen[]>;
// [BrowserOpenURL](https://wails.io/docs/reference/runtime/browser#browseropenurl)
// Opens the given URL in the system browser.
export function BrowserOpenURL(url: string): void;
// [Environment](https://wails.io/docs/reference/runtime/intro#environment)
// Returns information about the environment
export function Environment(): Promise<EnvironmentInfo>;
// [Quit](https://wails.io/docs/reference/runtime/intro#quit)
// Quits the application.
export function Quit(): void;
// [Hide](https://wails.io/docs/reference/runtime/intro#hide)
// Hides the application.
export function Hide(): void;
// [Show](https://wails.io/docs/reference/runtime/intro#show)
// Shows the application.
export function Show(): void;
// [ClipboardGetText](https://wails.io/docs/reference/runtime/clipboard#clipboardgettext)
// Returns the current text stored on clipboard
export function ClipboardGetText(): Promise<string>;
// [ClipboardSetText](https://wails.io/docs/reference/runtime/clipboard#clipboardsettext)
// Sets a text on the clipboard
export function ClipboardSetText(text: string): Promise<boolean>;
// [OnFileDrop](https://wails.io/docs/reference/runtime/draganddrop#onfiledrop)
// OnFileDrop listens to drag and drop events and calls the callback with the coordinates of the drop and an array of path strings.
export function OnFileDrop(callback: (x: number, y: number ,paths: string[]) => void, useDropTarget: boolean) :void
// [OnFileDropOff](https://wails.io/docs/reference/runtime/draganddrop#dragandddropoff)
// OnFileDropOff removes the drag and drop listeners and handlers.
export function OnFileDropOff() :void
// Check if the file path resolver is available
export function CanResolveFilePaths(): boolean;
// Resolves file paths for an array of files
export function ResolveFilePaths(files: File[]): void
// Notification types
export interface NotificationOptions {
id: string;
title: string;
subtitle?: string; // macOS and Linux only
body?: string;
categoryId?: string;
data?: { [key: string]: any };
}
export interface NotificationAction {
id?: string;
title?: string;
destructive?: boolean; // macOS-specific
}
export interface NotificationCategory {
id?: string;
actions?: NotificationAction[];
hasReplyField?: boolean;
replyPlaceholder?: string;
replyButtonTitle?: string;
}
// [InitializeNotifications](https://wails.io/docs/reference/runtime/notification#initializenotifications)
// Initializes the notification service for the application.
// This must be called before sending any notifications.
export function InitializeNotifications(): Promise<void>;
// [CleanupNotifications](https://wails.io/docs/reference/runtime/notification#cleanupnotifications)
// Cleans up notification resources and releases any held connections.
export function CleanupNotifications(): Promise<void>;
// [IsNotificationAvailable](https://wails.io/docs/reference/runtime/notification#isnotificationavailable)
// Checks if notifications are available on the current platform.
export function IsNotificationAvailable(): Promise<boolean>;
// [RequestNotificationAuthorization](https://wails.io/docs/reference/runtime/notification#requestnotificationauthorization)
// Requests notification authorization from the user (macOS only).
export function RequestNotificationAuthorization(): Promise<boolean>;
// [CheckNotificationAuthorization](https://wails.io/docs/reference/runtime/notification#checknotificationauthorization)
// Checks the current notification authorization status (macOS only).
export function CheckNotificationAuthorization(): Promise<boolean>;
// [SendNotification](https://wails.io/docs/reference/runtime/notification#sendnotification)
// Sends a basic notification with the given options.
export function SendNotification(options: NotificationOptions): Promise<void>;
// [SendNotificationWithActions](https://wails.io/docs/reference/runtime/notification#sendnotificationwithactions)
// Sends a notification with action buttons. Requires a registered category.
export function SendNotificationWithActions(options: NotificationOptions): Promise<void>;
// [RegisterNotificationCategory](https://wails.io/docs/reference/runtime/notification#registernotificationcategory)
// Registers a notification category that can be used with SendNotificationWithActions.
export function RegisterNotificationCategory(category: NotificationCategory): Promise<void>;
// [RemoveNotificationCategory](https://wails.io/docs/reference/runtime/notification#removenotificationcategory)
// Removes a previously registered notification category.
export function RemoveNotificationCategory(categoryId: string): Promise<void>;
// [RemoveAllPendingNotifications](https://wails.io/docs/reference/runtime/notification#removeallpendingnotifications)
// Removes all pending notifications from the notification center.
export function RemoveAllPendingNotifications(): Promise<void>;
// [RemovePendingNotification](https://wails.io/docs/reference/runtime/notification#removependingnotification)
// Removes a specific pending notification by its identifier.
export function RemovePendingNotification(identifier: string): Promise<void>;
// [RemoveAllDeliveredNotifications](https://wails.io/docs/reference/runtime/notification#removealldeliverednotifications)
// Removes all delivered notifications from the notification center.
export function RemoveAllDeliveredNotifications(): Promise<void>;
// [RemoveDeliveredNotification](https://wails.io/docs/reference/runtime/notification#removedeliverednotification)
// Removes a specific delivered notification by its identifier.
export function RemoveDeliveredNotification(identifier: string): Promise<void>;
// [RemoveNotification](https://wails.io/docs/reference/runtime/notification#removenotification)
// Removes a notification by its identifier (cross-platform convenience function).
export function RemoveNotification(identifier: string): Promise<void>;

View File

@@ -0,0 +1,298 @@
/*
_ __ _ __
| | / /___ _(_) /____
| | /| / / __ `/ / / ___/
| |/ |/ / /_/ / / (__ )
|__/|__/\__,_/_/_/____/
The electron alternative for Go
(c) Lea Anthony 2019-present
*/
export function LogPrint(message) {
window.runtime.LogPrint(message);
}
export function LogTrace(message) {
window.runtime.LogTrace(message);
}
export function LogDebug(message) {
window.runtime.LogDebug(message);
}
export function LogInfo(message) {
window.runtime.LogInfo(message);
}
export function LogWarning(message) {
window.runtime.LogWarning(message);
}
export function LogError(message) {
window.runtime.LogError(message);
}
export function LogFatal(message) {
window.runtime.LogFatal(message);
}
export function EventsOnMultiple(eventName, callback, maxCallbacks) {
return window.runtime.EventsOnMultiple(eventName, callback, maxCallbacks);
}
export function EventsOn(eventName, callback) {
return EventsOnMultiple(eventName, callback, -1);
}
export function EventsOff(eventName, ...additionalEventNames) {
return window.runtime.EventsOff(eventName, ...additionalEventNames);
}
export function EventsOffAll() {
return window.runtime.EventsOffAll();
}
export function EventsOnce(eventName, callback) {
return EventsOnMultiple(eventName, callback, 1);
}
export function EventsEmit(eventName) {
let args = [eventName].slice.call(arguments);
return window.runtime.EventsEmit.apply(null, args);
}
export function WindowReload() {
window.runtime.WindowReload();
}
export function WindowReloadApp() {
window.runtime.WindowReloadApp();
}
export function WindowSetAlwaysOnTop(b) {
window.runtime.WindowSetAlwaysOnTop(b);
}
export function WindowSetSystemDefaultTheme() {
window.runtime.WindowSetSystemDefaultTheme();
}
export function WindowSetLightTheme() {
window.runtime.WindowSetLightTheme();
}
export function WindowSetDarkTheme() {
window.runtime.WindowSetDarkTheme();
}
export function WindowCenter() {
window.runtime.WindowCenter();
}
export function WindowSetTitle(title) {
window.runtime.WindowSetTitle(title);
}
export function WindowFullscreen() {
window.runtime.WindowFullscreen();
}
export function WindowUnfullscreen() {
window.runtime.WindowUnfullscreen();
}
export function WindowIsFullscreen() {
return window.runtime.WindowIsFullscreen();
}
export function WindowGetSize() {
return window.runtime.WindowGetSize();
}
export function WindowSetSize(width, height) {
window.runtime.WindowSetSize(width, height);
}
export function WindowSetMaxSize(width, height) {
window.runtime.WindowSetMaxSize(width, height);
}
export function WindowSetMinSize(width, height) {
window.runtime.WindowSetMinSize(width, height);
}
export function WindowSetPosition(x, y) {
window.runtime.WindowSetPosition(x, y);
}
export function WindowGetPosition() {
return window.runtime.WindowGetPosition();
}
export function WindowHide() {
window.runtime.WindowHide();
}
export function WindowShow() {
window.runtime.WindowShow();
}
export function WindowMaximise() {
window.runtime.WindowMaximise();
}
export function WindowToggleMaximise() {
window.runtime.WindowToggleMaximise();
}
export function WindowUnmaximise() {
window.runtime.WindowUnmaximise();
}
export function WindowIsMaximised() {
return window.runtime.WindowIsMaximised();
}
export function WindowMinimise() {
window.runtime.WindowMinimise();
}
export function WindowUnminimise() {
window.runtime.WindowUnminimise();
}
export function WindowSetBackgroundColour(R, G, B, A) {
window.runtime.WindowSetBackgroundColour(R, G, B, A);
}
export function ScreenGetAll() {
return window.runtime.ScreenGetAll();
}
export function WindowIsMinimised() {
return window.runtime.WindowIsMinimised();
}
export function WindowIsNormal() {
return window.runtime.WindowIsNormal();
}
export function BrowserOpenURL(url) {
window.runtime.BrowserOpenURL(url);
}
export function Environment() {
return window.runtime.Environment();
}
export function Quit() {
window.runtime.Quit();
}
export function Hide() {
window.runtime.Hide();
}
export function Show() {
window.runtime.Show();
}
export function ClipboardGetText() {
return window.runtime.ClipboardGetText();
}
export function ClipboardSetText(text) {
return window.runtime.ClipboardSetText(text);
}
/**
* Callback for OnFileDrop returns a slice of file path strings when a drop is finished.
*
* @export
* @callback OnFileDropCallback
* @param {number} x - x coordinate of the drop
* @param {number} y - y coordinate of the drop
* @param {string[]} paths - A list of file paths.
*/
/**
* OnFileDrop listens to drag and drop events and calls the callback with the coordinates of the drop and an array of path strings.
*
* @export
* @param {OnFileDropCallback} callback - Callback for OnFileDrop returns a slice of file path strings when a drop is finished.
* @param {boolean} [useDropTarget=true] - Only call the callback when the drop finished on an element that has the drop target style. (--wails-drop-target)
*/
export function OnFileDrop(callback, useDropTarget) {
return window.runtime.OnFileDrop(callback, useDropTarget);
}
/**
* OnFileDropOff removes the drag and drop listeners and handlers.
*/
export function OnFileDropOff() {
return window.runtime.OnFileDropOff();
}
export function CanResolveFilePaths() {
return window.runtime.CanResolveFilePaths();
}
export function ResolveFilePaths(files) {
return window.runtime.ResolveFilePaths(files);
}
export function InitializeNotifications() {
return window.runtime.InitializeNotifications();
}
export function CleanupNotifications() {
return window.runtime.CleanupNotifications();
}
export function IsNotificationAvailable() {
return window.runtime.IsNotificationAvailable();
}
export function RequestNotificationAuthorization() {
return window.runtime.RequestNotificationAuthorization();
}
export function CheckNotificationAuthorization() {
return window.runtime.CheckNotificationAuthorization();
}
export function SendNotification(options) {
return window.runtime.SendNotification(options);
}
export function SendNotificationWithActions(options) {
return window.runtime.SendNotificationWithActions(options);
}
export function RegisterNotificationCategory(category) {
return window.runtime.RegisterNotificationCategory(category);
}
export function RemoveNotificationCategory(categoryId) {
return window.runtime.RemoveNotificationCategory(categoryId);
}
export function RemoveAllPendingNotifications() {
return window.runtime.RemoveAllPendingNotifications();
}
export function RemovePendingNotification(identifier) {
return window.runtime.RemovePendingNotification(identifier);
}
export function RemoveAllDeliveredNotifications() {
return window.runtime.RemoveAllDeliveredNotifications();
}
export function RemoveDeliveredNotification(identifier) {
return window.runtime.RemoveDeliveredNotification(identifier);
}
export function RemoveNotification(identifier) {
return window.runtime.RemoveNotification(identifier);
}