Files
hsa/code-server-like/templates/index.html
2025-12-13 22:52:16 +08:00

229 lines
9.9 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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="static/cdnback/css/editor/editor.main.css">
<script src="static/cdnback/socket.io.min.js"></script>
<link rel="stylesheet" href="static/cdnback/all.min.css">
<link rel="stylesheet" href="static/cdnback/css/xterm.css" />
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/xterm/5.5.0/xterm.js"></script> -->
<script src="static/cdnback/js/xterm.js"></script>
<script src="static/cdnback/js/addons/fit/fit.js"></script>
<script src="static/cdnback/js/addons/webLinks/webLinks.js"></script>
<script src="static/cdnback/js/addons/fullscreen/fullscreen.js"></script>
<script src="static/cdnback/js/addons/search/search.js"></script>
<link rel="stylesheet" href="static/cdnback/all.min.css">
<link rel="stylesheet" href="/vsc-like/static/css/index.css">
<link rel="stylesheet" href="/vsc-like/static/css/notification.css">
<script src="/vsc-like/static/js/notification.js"></script>
</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>
<div id="notificationsContainer"></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(/&#39;/g,'"'))
require.config({ paths: { vs: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.34.0/min/vs' } });
let saveTimeout=null;
// 语言映射表文件后缀与Monaco语言之间的映射
const languageMap = {
'.js': 'javascript',
'.ts': 'typescript',
'.html': 'html',
'.css': 'css',
'.json': 'json',
'.java': 'java',
'.py': 'python',
'.cpp': 'cpp',
'.c': 'c',
'.go': 'go',
'.ruby': 'ruby',
'.php': 'php',
'.xml': 'xml',
'.sql': 'sql',
'.sh': 'shell',
'.yaml': 'yaml',
'.md': 'markdown',
'.txt': 'plaintext',
};
// 缺省语言
const defaultLanguage = 'plaintext';
function getFileExtension(filename) {
return filename.slice(((filename.lastIndexOf(".") - 1) >>> 0) + 2);
}
// 根据文件名设置语言
function setEditorLanguageBasedOnFile(filename) {
const fileExtension = '.' + getFileExtension(filename).toLowerCase();
const language = languageMap[fileExtension] || defaultLanguage; // 如果没有匹配,使用默认语言
// 设置Monaco Editor语言
editor.setModel(monaco.editor.createModel(editor.getModel().getValue(), language));
console.log(`Editor language set to: ${language}`);
}
require(['vs/editor/editor.main'], function() {
editor = monaco.editor.create(document.getElementById('editorContainer'), {
value: '',
});
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 => {
if (!selectedItem){
showNotification('warning', '未选中文件,修改将不会保存');
return;
}
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 fullContent = editor.getValue();
debounceSendToServer({
type: 'fileEdit',
filePath: filePath,
content: fullContent,
config: code_like_config,
timestamp: new Date().getTime() // 添加时间戳以便追踪
}, 5000
);
// saveFile
if (saveTimeout) clearTimeout(saveTimeout);
saveTimeout = setTimeout(() => {
postSaveFile(selectedItem.path+'/'+selectedItem.name, editor.getValue());
}, 5000); // 5000ms = 5秒
});
// 监听粘贴事件
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';
}
}
// 按钮绑定事件
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>