不知名提交

This commit is contained in:
2025-12-13 20:53:50 +08:00
parent c147502b4d
commit 1221d6faf1
120 changed files with 11005 additions and 1092 deletions

View File

@@ -0,0 +1,136 @@
(function () {
const $ = (id) => document.getElementById(id);
const codeEl = $("code");
const runBtn = $("runBtn");
const copyBtn = $("copyBtn");
const pasteBtn = $("pasteBtn");
const clearBtn = $("clearBtn");
const outputEl = $("output");
const sandboxEl = $("sandbox");
// 以JS方式设置带换行的占位符避免HTML属性中的 \n 无效
codeEl.placeholder = "在此编写或粘贴 JavaScript 代码…\n例如\nconsole.log('Hello, InfoGenie!');";
let sandboxReady = false;
// 沙箱页面srcdoc内容拦截 console、收集错误、支持 async/await
const sandboxHtml = `<!doctype html><html><head><meta charset=\"utf-8\"></head><body>
<script>
(function(){
function serialize(value){
try {
if (typeof value === 'string') return value;
if (typeof value === 'function') return value.toString();
if (value === undefined) return 'undefined';
if (value === null) return 'null';
if (typeof value === 'object') return JSON.stringify(value, null, 2);
return String(value);
} catch (e) {
try { return String(value); } catch(_){ return Object.prototype.toString.call(value); }
}
}
['log','info','warn','error'].forEach(level => {
const orig = console[level];
console[level] = (...args) => {
try { parent.postMessage({type:'console', level, args: args.map(serialize)}, '*'); } catch(_){ }
try { orig && orig.apply(console, args); } catch(_){ }
};
});
window.onerror = function(message, source, lineno, colno, error){
var stack = error && error.stack ? error.stack : (source + ':' + lineno + ':' + colno);
parent.postMessage({type:'error', message: String(message), stack}, '*');
};
parent.postMessage({type:'ready'}, '*');
window.addEventListener('message', async (e) => {
const data = e.data;
if (!data || data.type !== 'code') return;
try {
// 用 async IIFE 包裹,支持顶层 await
await (async () => { eval(data.code); })();
parent.postMessage({type:'done'}, '*');
} catch (err) {
parent.postMessage({type:'error', message: (err && err.message) || String(err), stack: err && err.stack}, '*');
}
}, false);
})();
<\/script>
</body></html>`;
// 初始化沙箱
sandboxEl.srcdoc = sandboxHtml;
sandboxEl.addEventListener('load', () => {
// 等待 ready 消息
});
window.addEventListener('message', (e) => {
const data = e.data;
if (!data) return;
switch (data.type) {
case 'ready':
sandboxReady = true;
break;
case 'console':
(data.args || []).forEach((text) => appendLine(text, data.level));
break;
case 'error':
appendLine('[错误] ' + data.message, 'error');
if (data.stack) appendLine(String(data.stack), 'error');
break;
case 'done':
tip('执行完成。');
break;
default:
break;
}
});
runBtn.addEventListener('click', () => {
const code = codeEl.value || '';
if (!code.trim()) { tip('请先输入要执行的代码。'); return; }
outputEl.textContent = '';
if (!sandboxReady) tip('沙箱初始化中,稍候执行…');
// 发送代码到沙箱执行
try {
sandboxEl.contentWindow.postMessage({ type: 'code', code }, '*');
} catch (err) {
appendLine('[错误] ' + ((err && err.message) || String(err)), 'error');
}
});
copyBtn.addEventListener('click', async () => {
try {
await navigator.clipboard.writeText(codeEl.value);
tip('已复制到剪贴板。');
} catch (err) {
tip('复制失败,请检查剪贴板权限。');
}
});
pasteBtn.addEventListener('click', async () => {
try {
const txt = await navigator.clipboard.readText();
if (txt) codeEl.value = txt;
tip('已粘贴剪贴板内容。');
} catch (err) {
tip('粘贴失败,请允许访问剪贴板。');
}
});
clearBtn.addEventListener('click', () => {
outputEl.textContent = '';
});
function appendLine(text, level) {
const span = document.createElement('span');
span.className = 'line ' + (level || 'log');
span.textContent = String(text);
outputEl.appendChild(span);
outputEl.appendChild(document.createTextNode('\n'));
outputEl.scrollTop = outputEl.scrollHeight;
}
function tip(text){ appendLine('[提示] ' + text, 'info'); }
})();

View File

@@ -0,0 +1,42 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, viewport-fit=cover" />
<meta name="theme-color" content="#e8f8e4" />
<meta name="color-scheme" content="light" />
<title>JavaScript在线执行器</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<main class="page" role="main">
<header class="header">
<h1 class="title">JavaScript在线执行器</h1>
</header>
<section class="editor-section">
<label class="label" for="code">代码编辑区</label>
<textarea id="code" class="editor" placeholder="在此编写或粘贴 JavaScript 代码… \n 例如:\n console.log('Hello, InfoGenie!');" spellcheck="false" autocomplete="off" autocapitalize="off" autocorrect="off"></textarea>
<div class="toolbar" role="group" aria-label="编辑操作">
<button id="pasteBtn" class="btn" type="button" title="从剪贴板粘贴">粘贴代码</button>
<button id="copyBtn" class="btn" type="button" title="复制到剪贴板">复制代码</button>
<button id="runBtn" class="btn primary" type="button" title="执行当前代码">执行代码</button>
</div>
</section>
<section class="output-section">
<div class="output-header">
<span class="label">结果显示区</span>
<button id="clearBtn" class="btn ghost" type="button" title="清空结果">清空结果</button>
</div>
<pre id="output" class="output" aria-live="polite" aria-atomic="false"></pre>
</section>
<!-- 隐藏的沙箱 iframe用于安全执行 JS 代码 -->
<iframe id="sandbox" class="sandbox" sandbox="allow-scripts" title="执行沙箱" aria-hidden="true"></iframe>
</main>
<script src="./app.js"></script>
</body>
</html>

View File

@@ -0,0 +1,176 @@
/* 全局与主题 */
:root {
--bg-1: #eaf9e8; /* 淡绿色 */
--bg-2: #f4ffd9; /* 淡黄绿色 */
--panel: rgba(255, 255, 255, 0.78);
--text: #1d2a1d;
--muted: #486a48;
--accent: #5bb271;
--accent-2: #93d18f;
--border: rgba(93, 160, 93, 0.25);
--code-bg: rgba(255, 255, 255, 0.88);
--error: #b00020;
--warn: #8a6d3b;
--info: #2f6f3a;
}
/* 隐藏滚动条但保留滚动 */
html, body {
height: 100%;
overflow: auto;
-ms-overflow-style: none; /* IE 10+ */
scrollbar-width: none; /* Firefox */
}
html::-webkit-scrollbar, body::-webkit-scrollbar { width: 0; height: 0; }
/* 背景与排版 */
html, body {
margin: 0;
padding: 0;
background: linear-gradient(180deg, var(--bg-1), var(--bg-2));
color: var(--text);
font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "PingFang SC", "Microsoft YaHei", sans-serif;
}
.page {
box-sizing: border-box;
max-width: 760px;
margin: 0 auto;
padding: calc(env(safe-area-inset-top, 12px) + 8px) 14px calc(env(safe-area-inset-bottom, 12px) + 14px);
display: flex;
flex-direction: column;
gap: 16px;
}
.header {
display: flex;
flex-direction: column;
gap: 6px;
}
.title {
margin: 0;
font-size: 22px;
line-height: 1.2;
letter-spacing: 0.2px;
}
.subtitle {
margin: 0;
font-size: 13px;
color: var(--muted);
}
.editor-section, .output-section {
background: var(--panel);
border: 1px solid var(--border);
border-radius: 14px;
box-shadow: 0 10px 20px rgba(64, 129, 64, 0.06);
backdrop-filter: saturate(1.2) blur(8px);
padding: 12px;
}
.label {
display: block;
font-size: 12px;
color: var(--muted);
margin-bottom: 8px;
}
.editor {
box-sizing: border-box;
width: 100%;
min-height: 36vh;
max-height: 48vh;
resize: vertical;
padding: 10px 12px;
border-radius: 10px;
border: 1px solid var(--border);
outline: none;
background: var(--code-bg);
color: #192519;
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 14px;
line-height: 1.5;
white-space: pre;
overflow: auto;
-ms-overflow-style: none;
scrollbar-width: none;
}
.editor::-webkit-scrollbar { width: 0; height: 0; }
.toolbar {
display: flex;
gap: 8px;
margin-top: 10px;
}
.btn {
-webkit-tap-highlight-color: transparent;
appearance: none;
border: 1px solid var(--border);
background: #ffffffd6;
color: #204220;
padding: 10px 14px;
border-radius: 10px;
font-size: 14px;
line-height: 1;
cursor: pointer;
}
.btn:hover { filter: brightness(1.02) saturate(1.02); }
.btn:active { transform: translateY(1px); }
.btn.primary {
background: linear-gradient(180deg, var(--accent-2), var(--accent));
color: #fff;
border-color: rgba(0,0,0,0.06);
}
.btn.ghost {
background: transparent;
}
.output-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 8px;
}
.output {
box-sizing: border-box;
width: 100%;
min-height: 28vh;
max-height: 40vh;
padding: 10px 12px;
border-radius: 10px;
border: 1px solid var(--border);
background: var(--code-bg);
color: #192519;
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 14px;
line-height: 1.5;
white-space: pre-wrap;
overflow: auto;
-ms-overflow-style: none;
scrollbar-width: none;
}
.output::-webkit-scrollbar { width: 0; height: 0; }
.sandbox { display: none; width: 0; height: 0; border: 0; }
/* 控制不同日志级别颜色 */
.line.log { color: #1f2a1f; }
.line.info { color: var(--info); }
.line.warn { color: var(--warn); }
.line.error { color: var(--error); }
.line.tip { color: #507a58; font-style: italic; }
/* 竖屏优化 */
@media (orientation: portrait) {
.page { max-width: 640px; }
.editor { min-height: 40vh; }
.output { min-height: 30vh; }
}
/* 小屏进一步优化 */
@media (max-width: 380px) {
.btn { padding: 9px 12px; font-size: 13px; }
.title { font-size: 20px; }
}