This commit is contained in:
Evan
2026-09-07 09:55:36 +08:00
parent 9b6892f2ed
commit badbc11154
9098 changed files with 985321 additions and 0 deletions

View File

@@ -0,0 +1,87 @@
// PyodideWorkerPool.js
export default class PyodideWorkerPool {
constructor(poolSize = 2) {
this.poolSize = poolSize;
this.workers = [];
this.onMessageCallbacks = [];
this.initPool();
}
initPool() {
for (let i = 0; i < this.poolSize; i++) {
this.workers.push(this.createWorker());
}
}
createWorker() {
const workerInfo = {
worker: new Worker('js/pyodideWorker.js'),
busy: false,
loaded: false
};
// 监听 Worker 消息
workerInfo.worker.onmessage = (event) => {
const { type } = event.data;
if (type === "finish") {
workerInfo.loaded = true;
}
if (type === "executionComplete" || type === "error") {
workerInfo.busy = false;
}
// 让外部知道“是哪个 worker 发来的消息”
for (let cb of this.onMessageCallbacks) {
cb(event, workerInfo);
}
this.updateIsPythonLoadOK();
};
return workerInfo;
}
addOnMessageListener(callback) {
this.onMessageCallbacks.push(callback);
}
updateIsPythonLoadOK() {
// 只要至少一个 Worker loaded 就算可以执行
const anyLoaded = this.workers.some(w => w.loaded === true);
window.isPythonLoadOK = anyLoaded;
}
// 找到不忙、且已loaded的 Worker
getFreeWorker() {
return this.workers.find(w => w.busy === false && w.loaded === true) || null;
}
// 让指定 Worker 执行
runPython(workerInfo, code) {
if (workerInfo.busy) {
throw new Error("该 Worker 正在执行脚本,无法重复使用");
}
if (!workerInfo.loaded) {
throw new Error("该 Worker 尚未加载 Pyodide无法执行 Python");
}
workerInfo.busy = true;
workerInfo.worker.postMessage({ type: "runPython", code });
}
// 停止并替换
terminateAndReplace(workerInfo) {
workerInfo.worker.terminate();
const idx = this.workers.indexOf(workerInfo);
if (idx !== -1) {
const newW = this.createWorker();
this.workers[idx] = newW;
}
this.updateIsPythonLoadOK();
}
destroyPool() {
for (let wInfo of this.workers) {
wInfo.worker.terminate();
}
this.workers = [];
this.updateIsPythonLoadOK();
}
}

File diff suppressed because one or more lines are too long

2
scratch-gui/static/js/jquery.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,135 @@
// 这个文件主要用于加载Bootstrap的JavaScript功能
// 主要的排行榜逻辑在 src/playground/leaderboard.js 中实现
document.addEventListener('DOMContentLoaded', function() {
// 检测是否是移动设备
const isMobile = window.innerWidth <= 576 || /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
// 在移动设备上禁用特定功能
if (isMobile) {
const fullscreenBtn = document.getElementById('fullscreen-btn');
const autoRefreshSwitch = document.getElementById('auto-refresh-switch');
// 如果存在全屏按钮,禁用它
if (fullscreenBtn) {
fullscreenBtn.style.display = 'none';
}
// 如果存在自动刷新开关,禁用它
if (autoRefreshSwitch) {
autoRefreshSwitch.disabled = true;
const label = document.querySelector('label[for="auto-refresh-switch"]');
if (label) {
label.parentElement.style.display = 'none';
}
}
}
// 初始化所有Bootstrap组件
const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl);
});
// 添加平滑滚动效果
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const targetId = this.getAttribute('href');
if (targetId === '#') return;
const targetElement = document.querySelector(targetId);
if (targetElement) {
targetElement.scrollIntoView({
behavior: 'smooth'
});
}
});
});
// 检测是否是iPhone
const isIPhone = /iPhone/i.test(navigator.userAgent);
// 响应式调整
function adjustLayout() {
const windowWidth = window.innerWidth;
const windowHeight = window.innerHeight;
const tableContainer = document.querySelector('.leaderboard-table-container');
const isFullscreen = document.body.classList.contains('fullscreen-mode');
// 设置安全区域边距
if (isIPhone) {
document.body.style.paddingBottom = 'env(safe-area-inset-bottom)';
// 处理刘海屏和底部条
if (windowWidth < windowHeight) { // 竖屏
document.body.style.paddingTop = 'env(safe-area-inset-top)';
document.body.style.paddingLeft = 'env(safe-area-inset-left)';
document.body.style.paddingRight = 'env(safe-area-inset-right)';
}
}
if (windowWidth < 768 && !isFullscreen) {
// 小屏幕适配
if (tableContainer) {
tableContainer.classList.add('table-responsive-sm');
}
} else {
// 大屏幕或全屏模式适配
if (tableContainer) {
tableContainer.classList.remove('table-responsive-sm');
}
}
// 全屏模式下的额外调整
if (isFullscreen && !isMobile) {
const table = document.querySelector('.leaderboard-table');
if (table) {
// 确保表格足够大以填充屏幕
const viewportHeight = window.innerHeight;
const headerHeight = document.querySelector('.header').offsetHeight;
const infoHeight = document.querySelector('.leaderboard-header').offsetHeight;
const paginationHeight = document.querySelector('.pagination-container')?.offsetHeight || 0;
// 为iPhone底部栏预留空间
const bottomInset = isIPhone ? 34 : 0;
const availableHeight = viewportHeight - headerHeight - infoHeight - paginationHeight - 40 - bottomInset;
if (availableHeight > 300) {
tableContainer.style.maxHeight = `${availableHeight}px`;
tableContainer.style.overflowY = 'auto';
}
}
} else {
// 非全屏模式下重置样式
if (tableContainer) {
tableContainer.style.maxHeight = '';
tableContainer.style.overflowY = '';
}
}
}
// 初始调整和窗口大小变化时调整
adjustLayout();
window.addEventListener('resize', adjustLayout);
// 处理屏幕方向变化
window.addEventListener('orientationchange', function() {
setTimeout(adjustLayout, 300); // 延迟执行以确保浏览器完成方向变化
});
// 观察DOM变化以检测全屏模式切换
const observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
if (mutation.target === document.body) {
adjustLayout();
}
}
});
});
observer.observe(document.body, { attributes: true });
});

View File

@@ -0,0 +1,55 @@
// 响应式导航home / ai / competition_list / aboutus 共用):
// 页面在 header 内放置 <button class="nav-toggle">≤960px 时显示汉堡按钮,
// 点击切换 header.nav-openCSS 控制折叠面板与 .nav-backdrop 遮罩。
(function () {
function initNavResponsive() {
var toggles = document.querySelectorAll('.nav-toggle');
if (!toggles.length) return;
function syncBody() {
document.body.classList.toggle('nav-open', !!document.querySelector('header.nav-open'));
}
function setOpen(header, btn, open) {
header.classList.toggle('nav-open', open);
btn.setAttribute('aria-expanded', open ? 'true' : 'false');
btn.setAttribute('aria-label', open ? '关闭菜单' : '打开菜单');
syncBody();
}
toggles.forEach(function (btn) {
var header = btn.closest('header');
if (!header || header.dataset.navReady) return;
header.dataset.navReady = '1';
var backdrop = document.createElement('div');
backdrop.className = 'nav-backdrop';
header.appendChild(backdrop);
btn.addEventListener('click', function (e) {
e.stopPropagation();
setOpen(header, btn, !header.classList.contains('nav-open'));
});
backdrop.addEventListener('click', function () {
setOpen(header, btn, false);
});
header.addEventListener('click', function (e) {
if (e.target.closest('.nav__links a, .nav-list a, .navbar-collapse a, .navbar-logout')) {
setOpen(header, btn, false);
}
});
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape') setOpen(header, btn, false);
});
window.addEventListener('resize', function () {
if (window.innerWidth > 960) setOpen(header, btn, false);
});
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initNavResponsive);
} else {
initNavResponsive();
}
})();

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,163 @@
// pyodideWorker.js
importScripts("../pyodide/pyodide.js");
usecdn = false
// 确定 indexURL 的值
let indexURL;
if (usecdn) {
indexURL = 'https://oss.eanic.cn/001_code_python_res_20241213/pyodide/';
} else {
indexURL = "../pyodide/";
}
// 初始化 Pyodide
let pyodideReadyPromise = loadPyodide({
indexURL: indexURL
});
// 定义一个 Map 来存储所有的 pending 请求
let pendingRequests = new Map();
let requestIdCounter = 0;
pyodideReadyPromise.then(() => {
self.postMessage({ type: "finish", message: "Pyodide 加载完成" });
});
// 定义一个 JavaScript 函数,用于接收来自 Python 的调用
self.unitydata = {};
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// 定义发送 setTag 的函数
function send_setTag(line) {
self.postMessage({ type: "setTag", line: line });
}
// #-------------------角色相关----------------------#
// #playerMove(step)移动角色参数step为整数
// #playerJump() 向右前跳跃
// #turnLeft(times) 向左转动角色参数times为整数代表次数
// #turnRight(times) 向右转动角色参数times为整数代表次数
// #player_position() 获取角色坐标x y代表 x y坐标坐标为整数
// #-------------------载具相关----------------------#
// #vehicleMove(step)移动载具参数step为整数
// #turnVehicleLeft(times) 向左转动载具参数times为整数代表次数
// #turnVehicleRight(times) 向右转动载具参数times为整数代表次数
// #vehicle_position() 获取坐标x y代表 x y坐标坐标为整数
// #-----------------------------------------------#
async function playerMove(value) {
const requestId = ++requestIdCounter;
return new Promise((resolve, reject) => {
pendingRequests.set(requestId, { resolve, reject });
self.postMessage({ type: "playerMove", step: value, requestId: requestId });
});
}
async function playerJump() {
const requestId = ++requestIdCounter;
return new Promise((resolve, reject) => {
pendingRequests.set(requestId, { resolve, reject });
self.postMessage({ type: "playerJump", requestId: requestId });
});
}
async function turnLeft(value) {
const requestId = ++requestIdCounter;
return new Promise((resolve, reject) => {
pendingRequests.set(requestId, { resolve, reject });
self.postMessage({ type: "turnLeft", step: value, requestId: requestId });
});
}
async function turnRight(value) {
const requestId = ++requestIdCounter;
return new Promise((resolve, reject) => {
pendingRequests.set(requestId, { resolve, reject });
self.postMessage({ type: "turnRight", step: value, requestId: requestId });
});
}
async function cmdPrint(text) {
const requestId = ++requestIdCounter;
return new Promise((resolve, reject) => {
pendingRequests.set(requestId, { resolve, reject });
self.postMessage({ type: "cmdPrint", text: text, requestId: requestId });
});
}
async function vehicleMove(value) {
const requestId = ++requestIdCounter;
return new Promise((resolve, reject) => {
pendingRequests.set(requestId, { resolve, reject });
self.postMessage({ type: "vehicleMove", step: value, requestId: requestId });
});
}
async function turnVehicleLeft(value) {
const requestId = ++requestIdCounter;
return new Promise((resolve, reject) => {
pendingRequests.set(requestId, { resolve, reject });
self.postMessage({ type: "turnVehicleLeft", step: value, requestId: requestId });
});
}
async function turnVehicleRight(value) {
const requestId = ++requestIdCounter;
return new Promise((resolve, reject) => {
pendingRequests.set(requestId, { resolve, reject });
self.postMessage({ type: "turnVehicleRight", step: value, requestId: requestId });
});
}
async function scriptRunOver() {
const requestId = ++requestIdCounter;
return new Promise((resolve, reject) => {
pendingRequests.set(requestId, { resolve, reject });
self.postMessage({ type: "scriptRunOver", requestId: requestId });
});
}
self.onmessage = async (event) => {
const { type, code, unity_data, requestId} = event.data;
if (type === "response") {
self.unitydata = unity_data
console.log("get new response", self.unitydata.externalDatas[0].position)
const pending = pendingRequests.get(requestId);
if (pending) {
pending.resolve();
pendingRequests.delete(requestId);
console.log('收到回调',requestId)
}
} else if (type === "runPython") {
try {
let pyodide = await pyodideReadyPromise;
await pyodide.registerJsModule("send_setTag", send_setTag);
await pyodide.registerJsModule("playerMove", playerMove);
await pyodide.registerJsModule("playerJump", playerJump);
await pyodide.registerJsModule("turnLeft", turnLeft);
await pyodide.registerJsModule("turnRight", turnRight);
await pyodide.registerJsModule("cmdPrint", cmdPrint);
await pyodide.registerJsModule("vehicleMove", vehicleMove);
await pyodide.registerJsModule("turnVehicleLeft", turnVehicleLeft);
await pyodide.registerJsModule("turnVehicleRight", turnVehicleRight);
await pyodide.registerJsModule("scriptRunOver", scriptRunOver);
console.log(code)
let result = await pyodide.runPythonAsync(code);
// 发送执行完成的消息
self.postMessage({ type: "executionComplete" });
console.log("Python 执行完成",window.isPythonRun);
self.postMessage({ type: "result", result });
} catch (error) {
self.postMessage({ type: "error", error: error.message });
// 发生错误时也要发送执行完成的消息
self.postMessage({ type: "executionComplete" });
}
}
};