tspace-nfs
Version:
tspace-nfs is a Network File System (NFS) and provides both server and client capabilities for accessing files over a network.
169 lines (133 loc) • 6.18 kB
HTML
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NFS-Studio</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.33.0/min/vs/loader.min.js"></script>
<style>
.vscode-bg {
background-color: #1e1e1e;
}
#editor {
height: calc(100vh - 64px);
}
</style>
</head>
<body class="bg-gray-100 flex flex-col h-screen">
<nav class="sticky top-0 bg-gray-300 border-gray-200 dark:bg-gray-900">
<div class="w-screen-xl flex items-center justify-between mx-auto p-4">
<div class="cursor-pointer flex items-center space-x-3 rtl:space-x-reverse" onclick="window.location.reload()">
<svg class="w-12 h-12 text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linejoin="round" stroke-width="2" d="M15 4v3a1 1 0 0 1-1 1h-3m2 10v1a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-7.13a1 1 0 0 1 .24-.65L6.7 8.35A1 1 0 0 1 7.46 8H9m-1 4H4m16-7v10a1 1 0 0 1-1 1h-7a1 1 0 0 1-1-1V7.87a1 1 0 0 1 .24-.65l2.46-2.87a1 1 0 0 1 .76-.35H19a1 1 0 0 1 1 1Z"/>
</svg>
<h1 class="self-center text-2xl whitespace-nowrap text-gray-800 dark:text-white">
NFS-Studio
</h1>
</div>
</div>
</nav>
<div id="notify-container" class="space-y-4 fixed right-0 top-0 m-2 items-end justify-end z-50 pointer-events-none"></div>
<div id="editor" class="flex-grow"></div>
<div class="bg-white dark:bg-[#1e1e1e] p-4 flex justify-end">
<button class="bg-gray-300 dark:bg-gray-800 text-gray-800 dark:text-white text-[1.25rem] px-6 py-3 rounded-full"
onclick="saveFile()">
Save File
</button>
</div>
</body>
<script>
document.addEventListener('DOMContentLoaded', () => {
const $ = (element) => document.querySelector(element)
async function notify({ type, message }) {
const notifyContainer = $('#notify-container')
const notify = document.createElement('div');
const capitalizedType = type.charAt(0).toUpperCase() + type.slice(1);
switch(type.toLowerCase()) {
case 'success':
notify.className = 'max-w-md text-center bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded relative';
break;
case 'error':
notify.className = 'max-w-md text-center bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative';
break;
case 'info':
notify.className = 'max-w-md text-center bg-blue-100 border border-blue-400 text-blue-700 px-4 py-3 rounded relative';
break;
case 'warning':
notify.className = 'max-w-md text-center bg-yellow-100 border border-yellow-400 text-yellow-700 px-4 py-3 rounded relative';
break;
default:
notify.className = 'max-w-md text-center bg-gray-100 border border-gray-400 text-gray-700 px-4 py-3 rounded relative';
}
notify.innerHTML = `
<strong class="font-bold">${capitalizedType}</strong>
<span class="block sm:inline">${message}</span>
`;
notifyContainer.appendChild(notify);
setTimeout(() => {
notify.remove()
}, 2500)
}
function themeToggleMode() {
const theme = (localStorage.getItem('theme') ?? (document.body?.classList?.contains('dark') ? 'dark' : 'light'))
const isDark = theme === 'dark'
const prefixToReplace = isDark ? 'light:' : 'dark:';
const newPrefix = isDark ? 'dark:' : 'light:';
const body = document.body;
body.className = body.className.split(' ').map(className => {
return className.startsWith(prefixToReplace)
? className.replace(prefixToReplace, newPrefix)
: className
}).join(' ');
const elements = document.body.getElementsByTagName('*');
for (let element of elements) {
if(!element.classList) continue
element.classList.forEach(className => {
if (className.startsWith(prefixToReplace)) {
element.classList.replace(className, className.replace(prefixToReplace, newPrefix));
}
})
}
}
themeToggleMode()
require.config({ paths: { 'vs': 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.33.0/min/vs' } });
require(['vs/editor/editor.main'], function() {
const theme = (localStorage.getItem('theme') ?? (document.body?.classList?.contains('dark') ? 'dark' : 'light'))
const editor = monaco.editor.create(document.getElementById('editor'), {
language: "{{language}}",
theme: `vs-${theme}`,
automaticLayout: true,
});
const url = window.location.href
const baseUrl = `${window.location.protocol}//${window.location.host}/`
const path = url.replace(baseUrl, '').replace('studio','studio/api')
const filename = path.split('/').pop()
fetch(`${baseUrl}${path}`)
.then(response => response.text())
.then(data => {
editor.setValue(data)
})
.catch(err => notify({ type : 'error', message : `The file '${filename}' encountered an error '${err.message}'` }));
window.saveFile = function() {
const content = editor.getValue();
fetch(`${baseUrl}${path}`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ content })
})
.then(response => response.text())
.then(data => {
notify({
type: 'success',
message : `The file '${filename}' has been updated`
})
})
.catch(err => notify({ type : 'error', message : `The file '${filename}' encountered an error '${err.message}'` }));
}
});
});
</script>
</html>