Skip to content

Commit

Permalink
small optimisations
Browse files Browse the repository at this point in the history
  • Loading branch information
iambabyninja committed Jan 2, 2025
1 parent 5703820 commit 0d1b84c
Showing 1 changed file with 132 additions and 109 deletions.
241 changes: 132 additions & 109 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,7 @@
<title>Xray Config Validator</title>

<script src="wasm_exec.js"></script>
<script>
const isReady = new Promise((resolve) => {
window.onWasmInitialized = resolve;
});

const go = new Go();

(async () => {
const result = await WebAssembly.instantiateStreaming(fetch('main.wasm'), go.importObject);
go.run(result.instance);
await isReady;
document.getElementById("version-info").innerText = `Xray version ${XrayGetVersion()}`;
})();
</script>
<style>
* {
box-sizing: border-box;
Expand Down Expand Up @@ -131,6 +118,19 @@
}

@media (prefers-color-scheme: dark) {
body {
background-color: #121212;
color: #ffffff;
}

#editor {
border: 1px solid #555;
}

#output {
border: 1px solid #555;
}

button {
background-color: #333;
border: 1px solid #555;
Expand All @@ -144,30 +144,51 @@
button:active {
background-color: #555;
}
}

body {
background-color: #121212;
color: #ffffff;
}
#loading-indicator {
display: flex;
justify-content: center;
align-items: center;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1000;
}

#editor {
border: 1px solid #555;
}
#loading-spinner {
border: 8px solid #f3f3f3;
border-top: 8px solid #3498db;
border-radius: 50%;
width: 50px;
height: 50px;
animation: spin 2s linear infinite;
}

#output {
border: 1px solid #555;
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}


</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.33.0/min/vs/loader.min.js"></script>
</head>

<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.52.0/min/vs/loader.min.js"></script>
</head>
<body>

<div id="loading-indicator">
<div id="loading-spinner"></div>
</div>

<div class="container">
<nav id="nav">
<span id="version-info">Loading Xray&hellip;</span> |
<span id="version-info">Loading Xray</span> |
<a href="https://github.com/mmmray/xray-online">GitHub</a>
<h1>Xray Config Validator</h1>
</nav>
Expand All @@ -182,28 +203,48 @@ <h1>Xray Config Validator</h1>
</div>

<script>
const go = new Go();
const isReady = new Promise((resolve) => {
window.onWasmInitialized = resolve;
});

const output = document.getElementById("output");


(async () => {
try {
const result = await WebAssembly.instantiateStreaming(fetch('main.wasm'), go.importObject);
go.run(result.instance);
await isReady;
document.getElementById("version-info").innerText = `Xray version ${XrayGetVersion()}`;
} catch (error) {
console.error("Error loading WASM:", error);
document.getElementById("version-info").innerText = "Error loading Xray. Check the browser console.";
output.dataset.status = "error";
output.innerText = "Error loading Xray. Check the browser console.";
} finally {
document.getElementById("loading-indicator").style.display = "none";
}
})();


require.config({paths: {'vs': 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.52.0/min/vs'}});
require(['vs/editor/editor.main'], async function () {
const editorElement = document.getElementById('editor');
const output = document.getElementById("output");

function updateEditorTheme() {
const prefersDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
const theme = prefersDarkMode ? 'vs-dark' : 'vs-light';
monaco.editor.setTheme(theme);
}


const editor = monaco.editor.create(editorElement, {
language: 'json',
theme: 'vs-light',
automaticLayout: true
});


window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', updateEditorTheme);


updateEditorTheme();


Expand All @@ -229,7 +270,13 @@ <h1>Xray Config Validator</h1>
}]
});
})
.catch(error => console.error('Scheme load error:', error));
.catch(error => {
console.error('Scheme load error:', error);
output.dataset.status = "error";
output.innerText = "Error loading schema. Check browser console.";
});



function validateConfig() {
const jsonText = editor.getValue();
Expand All @@ -255,111 +302,66 @@ <h1>Xray Config Validator</h1>
}
}

editor.onDidChangeModelContent(validateConfig);



window.addEventListener("resize", function () {
editor.layout();
});


const brotli = await import("https://unpkg.com/[email protected]/index.web.js?module").then(m => m.default);
const textEncoder = new TextEncoder();
const textDecoder = new TextDecoder('utf-8');



document.getElementById("share-config").addEventListener("click", async () => {

if (!editor) {
console.error('Editor is not loaded');
return;
}
const config = editor.getValue();

const compressed = await compressWithBrotli(config);
const base64String = uint8ArrayToBase64(compressed)

const config = editor.getValue();
const compressed = await brotli.compress(textEncoder.encode(config));
const base64String = btoa(String.fromCharCode(...compressed));
const baseUrl = window.location.origin + window.location.pathname;
const url = `${baseUrl}?c=${base64String}`;

navigator.clipboard.writeText(url).then(() => {
alert("Shareable link copied to clipboard!");
}).catch((err) => {
console.error("Failed to copy link: ", err);
});
});

function uint8ArrayToBase64(uint8Array) {
let binaryString = '';
for (let i = 0; i < uint8Array.length; i++) {
binaryString += String.fromCharCode(uint8Array[i]);
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(url).then(() => {
alert("Shareable link copied to clipboard!");
}).catch((err) => {
console.error("Failed to copy link: ", err);

output.dataset.status = "warn";
output.innerText = "Could not copy to clipboard. Here's the link:\n" + url;
});
} else {

output.dataset.status = "warn";
output.innerText = "Clipboard API not supported. Here's the link:\n" + url;
}
});

return btoa(binaryString);
}

function compressWithBrotli(input) {
const uncompressedData = textEncoder.encode(input);
return brotli.compress(uncompressedData)
function uint8ArrayToBase64(uint8Array) {
return btoa(String.fromCharCode(...uint8Array));
}



const urlParams = new URLSearchParams(window.location.search);
let encodedConfig = urlParams.get('c');

if (encodedConfig) {
decodeAndLoadConfig(encodedConfig)
} else {
setDefaultConfigValue()
}

function setDefaultConfigValue() {
editor.setValue(`{
"inbounds": [
{
"listen": "127.0.0.1",
"port": 10808,
"protocol": "socks",
"settings": {
"udp": true
},
"sniffing": {
"enabled": true,
"destOverride": [
"http",
"tls"
]
}
}
],
"outbounds": [
{
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "",
"port": 443,
"users": [
{
"id": "user",
"encryption": "none",
"flow": "xtls-rprx-vision"
}
]
}
]
},
"streamSettings": {
"network": "tcp",
"security": "tls",
"tlsSettings": {
"serverName": "",
"allowInsecure": false,
"fingerprint": "chrome"
}
},
"tag": "proxy"
}
]
}`,);
editor.setValue(`{}`);
}


function decodeAndLoadConfig(encodedConfig) {
try {
encodedConfig = encodedConfig.replace(/ /g, '+').trim();
Expand All @@ -379,15 +381,36 @@ <h1>Xray Config Validator</h1>
const configText = textDecoder.decode(decompressedConfig);

editor.setValue(configText);

} catch (error) {
console.error("Failed to decode and decompress configuration:", error);
output.dataset.status = "error";
output.innerText = "Failed to decode and load shared configuration.";
output.innerText = "Failed to load shared configuration. Make sure the link is valid.";

if (error instanceof DOMException && error.name === "InvalidCharacterError") {
output.innerText += " Invalid Base64 encoding.";
} else if (error.message && error.message.startsWith("Brotli")) {
output.innerText += " Brotli decompression error.";
}
}
}

});


isReady.then(() => {
editor.onDidChangeModelContent(validateConfig);
validateConfig();


if (encodedConfig) {
decodeAndLoadConfig(encodedConfig);
} else {
setDefaultConfigValue();
}
});


});
</script>

</body>
Expand Down

0 comments on commit 0d1b84c

Please sign in to comment.