197 lines
9.1 KiB
HTML
197 lines
9.1 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Cloud IDE</title>
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.34.0/min/vs/editor/editor.main.css">
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.5.1/socket.io.min.js"></script>
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/xterm/5.5.0/xterm.css" />
|
||
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/xterm/5.5.0/xterm.js"></script> -->
|
||
<script src="https://unpkg.com/xterm@3.6.0/dist/xterm.js"></script>
|
||
<script src="https://unpkg.com/xterm@3.6.0/dist/addons/fit/fit.js"></script>
|
||
<script src="https://unpkg.com/xterm@3.6.0/dist/addons/webLinks/webLinks.js"></script>
|
||
<script src="https://unpkg.com/xterm@3.6.0/dist/addons/fullscreen/fullscreen.js"></script>
|
||
<script src="https://unpkg.com/xterm@3.6.0/dist/addons/search/search.js"></script>
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
|
||
|
||
<link rel="stylesheet" href="/vsc-like/static/css/index.css">
|
||
</head>
|
||
<body>
|
||
<div id="container">
|
||
<div id="fileTree" onclick="selectEmpty(this)">
|
||
<div id="topbar">
|
||
<button id="newFileBtn"><i class="fas fa-file"></i></button>
|
||
<button id="newFolderBtn"><i class="fas fa-folder"></i></button>
|
||
<button id="toggleSearchBtn"><i class="fas fa-search"></i></button>
|
||
<button id="refreshBtn"><i class="fas fa-sync-alt"></i></button>
|
||
<button id="saveBtn"><i class="fas fa-save"></i></button>
|
||
<button id="openTerminalBtn"><i class="fas fa-terminal"></i></button>
|
||
</div>
|
||
<div id="fileTreeContent"></div>
|
||
<div id="searchSidebar">
|
||
<h3>Global Search</h3>
|
||
<input type="text" id="searchInput" placeholder="Search..." oninput="searchFiles()">
|
||
<ul id="searchResults"></ul>
|
||
</div>
|
||
</div>
|
||
<!-- 右键菜单 -->
|
||
<div id="contextMenu" class="context-menu" style="display: none;">
|
||
<ul>
|
||
<li id="renameOption">Rename</li>
|
||
<li id="copyOption">Copy</li>
|
||
<li id="pasteOption">Paste</li>
|
||
<li id="deleteOption">Delete</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div id="contentArea">
|
||
<!-- 编辑器部分 -->
|
||
<div id="editorContainer">
|
||
</div>
|
||
|
||
<!-- 终端部分 -->
|
||
<span style="font-size: small;">status: <span style="font-size: small;" id="status">connecting...</span></span>
|
||
<div id="terminal">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.34.0/min/vs/loader.js"></script>
|
||
<script src="/vsc-like/static/js/code-like-extension.js"></script>
|
||
<script src="/vsc-like/static/js/file.js"></script>
|
||
<script src="/vsc-like/static/js/terminal.js"></script>
|
||
<script>
|
||
window.lesson_data = {
|
||
course_id:'{{course_id}}',
|
||
chapter_name:'{{chapter_name}}',
|
||
lesson_name:'{{lesson_name}}'
|
||
}
|
||
let code_like_config = JSON.parse('{{code_like_config}}'.replace(/'/g,'"'))
|
||
|
||
require.config({ paths: { vs: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.34.0/min/vs' } });
|
||
|
||
require(['vs/editor/editor.main'], function() {
|
||
editor = monaco.editor.create(document.getElementById('editorContainer'), {
|
||
value: '',
|
||
language: 'javascript'
|
||
});
|
||
|
||
const fileTreeDiv = document.getElementById('fileTreeContent');
|
||
const searchSidebar = document.getElementById('searchSidebar');
|
||
const searchInput = document.getElementById('searchInput');
|
||
const searchResults = document.getElementById('searchResults');
|
||
const saveBtn = document.getElementById('saveBtn');
|
||
const openTerminalBtn = document.getElementById('openTerminalBtn');
|
||
|
||
// 监听文本编辑事件
|
||
editor.onDidChangeModelContent(event => {
|
||
const filePath = selectedItem.path + '/' +selectedItem.name;
|
||
const changes = event.changes;
|
||
console.log("changes",changes)
|
||
// 检查是否有大段文本插入,判断为粘贴操作
|
||
changes.forEach(change => {
|
||
if (change.text.length > 20 && !change.rangeLength) { // 设置长度阈值为20字符,且非删除操作
|
||
console.log(`Detected paste action in file ${filePath}. Sending content...`);
|
||
|
||
sendToServer({
|
||
type: 'paste',
|
||
filePath: filePath,
|
||
content: change.text,
|
||
config: code_like_config,
|
||
});
|
||
}
|
||
});
|
||
|
||
// 防抖处理非粘贴的编辑操作
|
||
const content = changes.text;
|
||
debounceSendToServer({
|
||
type: 'fileEdit',
|
||
filePath: filePath,
|
||
content: content,
|
||
config: code_like_config,
|
||
}, 5000
|
||
);
|
||
});
|
||
|
||
// 监听粘贴事件
|
||
editor.onDidPaste(function(event) {
|
||
console.log('Paste event: ', event); // 这里你可以调用自定义的粘贴逻辑
|
||
});
|
||
// 切换全局搜索侧栏
|
||
function toggleSearchSidebar(event) {
|
||
event.stopPropagation();
|
||
searchSidebar.style.display = (searchSidebar.style.display === 'none' || searchSidebar.style.display === '') ? 'block' : 'none';
|
||
if (searchSidebar.style.display === 'block') {
|
||
fileTreeDiv.style.display = 'none';
|
||
}
|
||
else{
|
||
fileTreeDiv.style.display = 'block';
|
||
}
|
||
}
|
||
|
||
// 刷新文件树
|
||
function refreshFileTree(event) {
|
||
event.stopPropagation();
|
||
loadFileTree();
|
||
}
|
||
|
||
// 搜索文件
|
||
function searchFiles() {
|
||
const query = searchInput.value;
|
||
fetch(`/vsc-like/search-files?query=${query}`)
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
searchResults.innerHTML = '';
|
||
data.forEach(result => {
|
||
const li = document.createElement('li');
|
||
li.textContent = result.name;
|
||
li.onclick = () => loadFileContent(result);
|
||
searchResults.appendChild(li);
|
||
});
|
||
});
|
||
}
|
||
let socket = null;
|
||
function openTerminal(event) {
|
||
// document.getElementById('terminal').style.display = 'block';
|
||
if (document.getElementById('terminal').style.display === 'none' || document.getElementById('terminal').style.display === '') {
|
||
document.getElementById('terminal').style.display = 'block';
|
||
}else{
|
||
document.getElementById('terminal').style.display = 'none';
|
||
}
|
||
// event.stopPropagation();
|
||
// const terminalDiv = document.getElementById('terminal');
|
||
// terminalDiv.style.display = (terminalDiv.style.display === 'none' || terminalDiv.style.display === '') ? 'block' : 'none';
|
||
// if (socket == null) {
|
||
// socket = io.connect('http://' + document.domain + ':5200/vsc-like/terminal');
|
||
// }
|
||
// socket.on('connected', function(data) {
|
||
// console.log('Terminal connected');
|
||
// });
|
||
// socket.on('terminal_output', function(data) {
|
||
// const terminalDiv = document.getElementById('terminal');
|
||
// terminalDiv.textContent += data.data;
|
||
// });
|
||
|
||
// function sendTerminalInput(input) {
|
||
// socket.emit('send_input', { input: input });
|
||
// }
|
||
}
|
||
|
||
// 按钮绑定事件
|
||
document.getElementById('newFileBtn').addEventListener('click', (event) => createNewFile(event));
|
||
document.getElementById('newFolderBtn').addEventListener('click', (event) => createNewFolder(event));
|
||
document.getElementById('toggleSearchBtn').addEventListener('click', (event) => toggleSearchSidebar(event));
|
||
document.getElementById('refreshBtn').addEventListener('click', (event) => refreshFileTree(event));
|
||
document.getElementById('saveBtn').addEventListener('click', (event) => saveFile(event));
|
||
document.getElementById('openTerminalBtn').addEventListener('click', (event) => openTerminal(event));
|
||
|
||
loadFileTree();
|
||
});
|
||
|
||
</script>
|
||
</body>
|
||
</html>
|