一个网页可以让你的程序自我进化。
- 内容介绍
- 文章标签
- 相关推荐
【转帖】类似衔尾蛇,能自我吞噬自己,然后随着指令不断进化。可以实时修改自身DOM,可以让它自己把自己改成手机版的程序。可以自我进化过程中自我修复错误代码,能本地记录历史指令,本身它具备电脑端所有开发功能,我试了一下好像没啥大错误。
1506×491 20.4 KB
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
touch-action: manipulation;
}
/* 修复移动端滚动问题 */
.scroll-fix {
-webkit-overflow-scrolling: touch;
}
/* 配置面板动画 */
.config-panel {
transform: translateY(100%);
transition: transform 0.3s ease-out;
}
.config-panel.active {
transform: translateY(0);
}
/* 按钮反馈 */
.btn-active:active {
transform: scale(0.95);
}
</style>
<!-- 主程序窗口 -->
<div id="ouroboros-core" class="absolute top-4 left-1/2 transform -translate-x-1/2 w-[95vw] max-w-[500px] bg-white rounded-2xl border border-gray-200 shadow-xl flex flex-col z-10">
<!-- 标题栏 -->
<div class="window-header bg-gradient-to-r from-gray-800 to-gray-900 text-white px-4 py-3 rounded-t-2xl flex justify-between items-center">
<div class="flex items-center">
<div class="w-2 h-2 bg-green-400 rounded-full mr-2 animate-pulse"></div>
<h2 class="font-bold text-sm">衔尾蛇移动版</h2>
</div>
<button id="export-btn" title="导出程序" class="text-gray-300 hover:text-white p-2 btn-active">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"></path>
</svg>
</button>
</div>
<!-- 令牌监控 -->
<div class="bg-gray-50 px-4 py-2 text-xs text-gray-600 border-b border-gray-200 flex justify-between">
<span>令牌使用</span>
<span id="token-count" class="font-medium">0 / 128k</span>
</div>
<!-- 活动日志 -->
<div id="activity-log" class="flex-1 p-4 min-h-[200px] max-h-[40vh] bg-white text-xs font-mono overflow-y-auto scroll-fix">
<div class="text-gray-400 italic">程序已加载。点击底部"设置"按钮配置 API。</div>
</div>
<!-- 输入区域 -->
<div class="p-4 border-t border-gray-200 bg-gray-50 rounded-b-2xl">
<div class="relative mb-3">
<textarea id="user-input" placeholder="输入指令 (例如: '为手机创建一个计算器')..."
class="w-full text-sm border border-gray-300 rounded-xl p-3 focus:outline-none focus:border-blue-500 min-h-[100px] resize-none pr-10"></textarea>
<button id="clear-input-btn" class="absolute right-2 top-2 text-gray-400 hover:text-gray-600 p-1 btn-active">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
<div class="flex justify-between items-center">
<span class="text-xs text-gray-500">点击发送指令</span>
<button id="execute-btn" class="bg-gray-900 hover:bg-gray-800 text-white text-sm font-medium px-5 py-3 rounded-xl transition-colors relative btn-active">
<span id="btn-text">执行指令 🐍</span>
<div id="spinner" class="hidden absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2">
<svg class="animate-spin h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
</div>
</button>
</div>
</div>
</div>
<!-- 配置面板 (从底部滑出) -->
<div id="config-panel" class="fixed inset-0 bg-white z-50 hidden config-panel">
<div class="bg-gray-800 text-white px-4 py-3 flex justify-between items-center">
<h2 class="font-bold text-sm">配置设置</h2>
<button id="close-config-btn" class="text-gray-300 hover:text-white p-2 btn-active">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
<div class="flex-1 p-4 overflow-y-auto scroll-fix">
<div class="mb-6">
<h3 class="font-semibold text-gray-700 mb-3 text-sm">DeepSeek API 配置</h3>
<div class="space-y-4">
<div>
<label class="block text-xs text-gray-500 mb-1">API 密钥</label>
<input type="password" id="config-api-key" placeholder="sk-..."
class="w-full text-sm border border-gray-300 rounded-lg px-3 py-3 focus:outline-none focus:border-blue-500 bg-gray-50">
</div>
<div>
<label class="block text-xs text-gray-500 mb-1">API 基础地址</label>
<input type="text" id="config-api-base" value="https://api.deepseek.com/v1"
class="w-full text-sm border border-gray-300 rounded-lg px-3 py-3 focus:outline-none focus:border-blue-500 bg-gray-50">
</div>
<div>
<label class="block text-xs text-gray-500 mb-1">模型名称</label>
<input type="text" id="config-model-name" value="deepseek-chat"
class="w-full text-sm border border-gray-300 rounded-lg px-3 py-3 focus:outline-none focus:border-blue-500 bg-gray-50">
</div>
</div>
</div>
<div class="mb-6">
<h3 class="font-semibold text-gray-700 mb-3 text-sm">移动端设置</h3>
<div class="space-y-3">
<div class="flex items-center justify-between">
<span class="text-sm text-gray-600">启用拖拽功能</span>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" id="enable-dragging" class="sr-only peer" checked>
<div class="w-11 h-6 bg-gray-200 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
</label>
</div>
<div class="flex items-center justify-between">
<span class="text-sm text-gray-600">发送后清空输入</span>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" id="auto-clear-input" class="sr-only peer" checked>
<div class="w-11 h-6 bg-gray-200 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
</label>
</div>
</div>
</div>
<div>
<button id="config-save-btn" class="w-full bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium px-4 py-3 rounded-lg transition-colors btn-active">
保存设置
</button>
</div>
</div>
</div>
<!-- 底部栏 -->
<div class="fixed bottom-0 left-0 w-full z-20">
<div class="flex justify-between items-center bg-white border-t border-gray-200 px-3 py-2 shadow-lg">
<span class="text-xs text-gray-500">衔尾蛇 v1.0</span>
<button id="mobile-config-btn" class="text-xs text-gray-600 hover:text-gray-900 bg-blue-50 hover:bg-blue-100 px-3 py-2 rounded-lg transition-colors border border-blue-200 btn-active">
⚙️ 设置
</button>
</div>
</div>
<!-- 系统提示 -->
<script type="text/plain" id="system-prompt">
角色
你是"衔尾蛇移动版",一个为移动设备优化的自我编辑 HTML 应用程序。通过将当前 DOM(状态 A)转换为新的功能状态(状态 B)来满足用户的请求,确保在手机屏幕上完美运行。
核心规则
- 状态转换:你收到当前页面的完整 HTML 源码。分析它,然后编写 JavaScript 代码来修改 DOM 以实现请求的功能。
- 保护原则:绝对不要删除或修改 id=“ouroboros-core” 或 id=“config-panel” 的元素。这些包含应用程序逻辑。
- 移动 UI 标准:
- 创建为手机屏幕优化的 “窗口” 作为
div元素。 - 使用响应式 Tailwind CSS 类:
w-full max-w-[95vw],mx-auto,p-4等。 - 为移动端优先考虑触摸友好的界面:最小触摸目标 44x44px。
- 使用百分比宽度,避免在移动端可能溢出的固定像素宽度。
- 创建为手机屏幕优化的 “窗口” 作为
注意
你必须将解决方案作为单个有效的 JavaScript 代码块提供,用 javascript ... 包围。不要在代码注释之外提供自然语言解释。
<!-- 主应用程序逻辑 -->
<script>
// 页面加载完成后执行
document.addEventListener('DOMContentLoaded', function() {
console.log('DOM 加载完成,开始初始化...');
// 立即显示界面
document.body.style.opacity = '1';
// 初始化应用程序
setTimeout(initializeApp, 100);
});
</script>
<script type="module">
import OpenAI from 'https://esm.sh/openai@4.28.0?bundle';
// 应用程序状态
const AppState = {
apiKey: localStorage.getItem('ouroboros_api_key') || '',
apiBase: localStorage.getItem('ouroboros_api_base') || 'https://api.deepseek.com/v1',
modelName: localStorage.getItem('ouroboros_model_name') || 'deepseek-chat',
enableDragging: localStorage.getItem('ouroboros_enable_dragging') !== 'false',
autoClearInput: localStorage.getItem('ouroboros_auto_clear') !== 'false',
isProcessing: false
};
// DOM 元素引用
const Elements = {
// 核心窗口
core: document.getElementById('ouroboros-core'),
// 配置面板
configPanel: document.getElementById('config-panel'),
// 配置表单元素
configApiKey: document.getElementById('config-api-key'),
configApiBase: document.getElementById('config-api-base'),
configModelName: document.getElementById('config-model-name'),
enableDragging: document.getElementById('enable-dragging'),
autoClearInput: document.getElementById('auto-clear-input'),
// 按钮
configSaveBtn: document.getElementById('config-save-btn'),
configCloseBtn: document.getElementById('close-config-btn'),
mobileConfigBtn: document.getElementById('mobile-config-btn'),
userInput: document.getElementById('user-input'),
clearInputBtn: document.getElementById('clear-input-btn'),
executeBtn: document.getElementById('execute-btn'),
exportBtn: document.getElementById('export-btn'),
// 界面元素
btnText: document.getElementById('btn-text'),
spinner: document.getElementById('spinner'),
activityLog: document.getElementById('activity-log'),
tokenCount: document.getElementById('token-count')
};
// 初始化应用程序
function initializeApp() {
console.log('开始初始化移动版衔尾蛇...');
// 确保所有元素都存在
if (!validateElements()) {
console.error('部分 DOM 元素未找到');
return;
}
// 加载保存的设置
loadSettings();
// 设置事件监听器
setupEventListeners();
// 初始化窗口拖拽
setupWindowDragging();
// 初始化令牌计数
updateTokenCount();
// 添加启动日志
addLogMessage('系统', '衔尾蛇移动版 v1.0 已启动,欢迎使用!');
console.log('应用程序初始化完成');
}
// 验证元素是否存在
function validateElements() {
for (const [key, element] of Object.entries(Elements)) {
if (!element) {
console.error(`元素 ${key} 未找到`);
return false;
}
}
return true;
}
// 加载设置
function loadSettings() {
try {
Elements.configApiKey.value = AppState.apiKey;
Elements.configApiBase.value = AppState.apiBase;
Elements.configModelName.value = AppState.modelName;
Elements.enableDragging.checked = AppState.enableDragging;
Elements.autoClearInput.checked = AppState.autoClearInput;
} catch (error) {
console.error('加载设置失败:', error);
}
}
// 设置事件监听器
function setupEventListeners() {
// 设置按钮 - 确保点击有效
Elements.mobileConfigBtn.addEventListener('click', showConfigPanel, false);
// 关闭设置按钮
Elements.configCloseBtn.addEventListener('click', hideConfigPanel, false);
// 保存设置按钮
Elements.configSaveBtn.addEventListener('click', saveSettings, false);
// 执行按钮
Elements.executeBtn.addEventListener('click', executeCommand, false);
// 清空输入按钮
Elements.clearInputBtn.addEventListener('click', clearInput, false);
// 导出按钮
Elements.exportBtn.addEventListener('click', exportApplication, false);
// 输入框键盘事件
Elements.userInput.addEventListener('keydown', handleKeyDown, false);
// 防止配置面板内的点击事件冒泡
Elements.configPanel.addEventListener('click', function(e) {
e.stopPropagation();
}, false);
}
// 显示配置面板
function showConfigPanel(e) {
if (e) e.preventDefault();
console.log('显示配置面板');
// 显示配置面板
Elements.configPanel.classList.remove('hidden');
// 触发动画
setTimeout(() => {
Elements.configPanel.classList.add('active');
}, 10);
addLogMessage('系统', '打开设置面板');
}
// 隐藏配置面板
function hideConfigPanel(e) {
if (e) e.preventDefault();
console.log('隐藏配置面板');
// 移除动画类
Elements.configPanel.classList.remove('active');
// 延迟隐藏元素
setTimeout(() => {
Elements.configPanel.classList.add('hidden');
}, 300);
addLogMessage('系统', '关闭设置面板');
}
// 清空输入
function clearInput(e) {
if (e) e.preventDefault();
Elements.userInput.value = '';
Elements.userInput.focus();
}
// 键盘处理
function handleKeyDown(e) {
if (e.ctrlKey && e.key === 'Enter') {
e.preventDefault();
executeCommand();
}
}
// 保存设置
function saveSettings(e) {
if (e) e.preventDefault();
try {
// 获取设置值
AppState.apiKey = Elements.configApiKey.value.trim();
AppState.apiBase = Elements.configApiBase.value.trim() || 'https://api.deepseek.com/v1';
AppState.modelName = Elements.configModelName.value.trim() || 'deepseek-chat';
AppState.enableDragging = Elements.enableDragging.checked;
AppState.autoClearInput = Elements.autoClearInput.checked;
// 保存到本地存储
localStorage.setItem('ouroboros_api_key', AppState.apiKey);
localStorage.setItem('ouroboros_api_base', AppState.apiBase);
localStorage.setItem('ouroboros_model_name', AppState.modelName);
localStorage.setItem('ouroboros_enable_dragging', AppState.enableDragging);
localStorage.setItem('ouroboros_auto_clear', AppState.autoClearInput);
// 重新初始化拖拽
setupWindowDragging();
// 显示成功消息
addLogMessage('系统', '设置保存成功');
// 显示保存成功提示
const originalText = Elements.configSaveBtn.textContent;
Elements.configSaveBtn.textContent = '✓ 已保存';
Elements.configSaveBtn.classList.add('bg-green-600');
setTimeout(() => {
Elements.configSaveBtn.textContent = originalText;
Elements.configSaveBtn.classList.remove('bg-green-600');
}, 1500);
// 延迟关闭配置面板
setTimeout(() => {
hideConfigPanel();
}, 1000);
} catch (error) {
console.error('保存设置失败:', error);
addLogMessage('系统', '设置保存失败: ' + error.message);
}
}
// 设置窗口拖拽
function setupWindowDragging() {
if (typeof interact === 'undefined') {
console.warn('interact.js 未加载,跳过拖拽初始化');
return;
}
try {
if (!AppState.enableDragging) {
if (Elements.core.__draggable) {
interact(Elements.core).unset();
Elements.core.__draggable = false;
}
return;
}
// 设置拖拽
interact(Elements.core)
.draggable({
allowFrom: '.window-header',
inertia: true,
modifiers: [
interact.modifiers.restrict({
restriction: 'parent',
endOnly: true,
elementRect: { top: 0, left: 0, bottom: 1, right: 1 }
})
],
listeners: {
start: function(event) {
Elements.core.style.transition = 'none';
},
move: function(event) {
const x = (parseFloat(Elements.core.getAttribute('data-x')) || 0) + event.dx;
const y = (parseFloat(Elements.core.getAttribute('data-y')) || 0) + event.dy;
Elements.core.style.transform = `translate(${x}px, ${y}px) translateX(-50%)`;
Elements.core.setAttribute('data-x', x);
Elements.core.setAttribute('data-y', y);
},
end: function(event) {
Elements.core.style.transition = 'transform 0.2s ease';
}
}
});
Elements.core.__draggable = true;
} catch (error) {
console.error('拖拽初始化失败:', error);
}
}
// 执行命令
async function executeCommand(e) {
if (e) e.preventDefault();
if (AppState.isProcessing) {
addLogMessage('系统', '正在处理上一个请求,请稍候...');
return;
}
if (!AppState.apiKey) {
addLogMessage('系统', '请先配置 DeepSeek API 密钥');
showConfigPanel();
return;
}
const userCommand = Elements.userInput.value.trim();
if (!userCommand) {
addLogMessage('系统', '请输入指令');
return;
}
setLoading(true);
addLogMessage('用户', userCommand);
try {
// 准备 API 配置
const apiConfig = {
apiKey: AppState.apiKey,
dangerouslyAllowBrowser: true,
baseURL: AppState.apiBase
};
const openai = new OpenAI(apiConfig);
// 获取系统提示
const systemPrompt = document.getElementById('system-prompt').textContent;
// 获取当前页面 HTML
const currentHTML = document.documentElement.outerHTML;
// 构建消息
const messages = [
{
role: 'system',
content: systemPrompt
},
{
role: 'user',
content: currentHTML
}
];
addLogMessage('系统', '正在调用 AI 接口...');
// 调用 API
const response = await openai.chat.completions.create({
messages: messages,
model: AppState.modelName,
temperature: 0.7
});
const aiResponse = response.choices[0].message.content;
addLogMessage('系统', '收到 AI 响应');
// 提取并执行 JavaScript 代码
const codeMatch = aiResponse.match(/```(?:javascript)?\s*([\s\S]*?)\s*```/);
if (codeMatch && codeMatch[1]) {
const code = codeMatch[1].trim();
executeCode(code);
addLogMessage('系统', '代码执行成功');
if (AppState.autoClearInput) {
Elements.userInput.value = '';
}
} else {
addLogMessage('系统', '未找到有效的 JavaScript 代码块');
}
} catch (error) {
console.error('执行错误:', error);
addLogMessage('系统', `错误: ${error.message || 'API 调用失败'}`);
if (error.message && error.message.includes('401')) {
addLogMessage('系统', 'API 密钥无效,请检查设置');
} else if (error.message && error.message.includes('429')) {
addLogMessage('系统', 'API 调用频率过高,请稍后再试');
}
} finally {
setLoading(false);
}
}
// 执行代码
function executeCode(code) {
try {
const script = document.createElement('script');
script.textContent = `(() => {
'use strict';
try {
${code}
} catch(error) {
console.error('代码执行错误:', error);
alert('代码执行错误: ' + error.message);
}
})();`;
document.body.appendChild(script);
document.body.removeChild(script);
} catch (e) {
console.error('代码注入错误:', e);
addLogMessage('系统', '代码注入错误: ' + e.message);
}
}
// 导出应用程序
function exportApplication(e) {
if (e) e.preventDefault();
try {
const htmlContent = '<!DOCTYPE html>\n' + document.documentElement.outerHTML;
const blob = new Blob([htmlContent], { type: 'text/html;charset=utf-8' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `ouroboros_mobile_${Date.now()}.html`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
addLogMessage('系统', '应用程序已导出为 HTML 文件');
} catch (error) {
console.error('导出失败:', error);
addLogMessage('系统', '导出失败: ' + error.message);
}
}
// 添加日志消息
function addLogMessage(sender, message) {
const time = new Date().toLocaleTimeString();
const senderClass = sender === '用户' ? 'text-blue-600 font-semibold' : 'text-green-600 font-semibold';
const logEntry = document.createElement('div');
logEntry.className = 'mb-2 leading-relaxed';
logEntry.innerHTML = `
<span class="text-xs text-gray-500">[${time}]</span>
<span class="${senderClass} ml-1">${sender}:</span>
<span class="ml-1 text-gray-800">${escapeHtml(message)}</span>
`;
Elements.activityLog.appendChild(logEntry);
Elements.activityLog.scrollTop = Elements.activityLog.scrollHeight;
}
// HTML 转义
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// 更新令牌计数
function updateTokenCount() {
try {
const htmlLength = document.documentElement.outerHTML.length;
const estimatedTokens = Math.ceil(htmlLength / 4);
const percentage = Math.min((estimatedTokens / 128000) * 100, 100).toFixed(1);
Elements.tokenCount.textContent = `${estimatedTokens.toLocaleString()}/128k (${percentage}%)`;
// 颜色提示
if (percentage > 90) {
Elements.tokenCount.classList.add('text-red-600');
} else if (percentage > 70) {
Elements.tokenCount.classList.add('text-yellow-600');
} else {
Elements.tokenCount.classList.remove('text-red-600', 'text-yellow-600');
}
} catch (error) {
console.error('更新令牌计数失败:', error);
}
// 每10秒更新一次
setTimeout(updateTokenCount, 10000);
}
// 设置加载状态
function setLoading(loading) {
AppState.isProcessing = loading;
Elements.btnText.classList.toggle('invisible', loading);
Elements.spinner.classList.toggle('hidden', !loading);
Elements.executeBtn.disabled = loading;
if (loading) {
Elements.executeBtn.classList.add('opacity-75');
} else {
Elements.executeBtn.classList.remove('opacity-75');
}
}
// 立即初始化
initializeApp();
</script>
网友解答:
--【壹】--:
【转帖】类似衔尾蛇,能自我吞噬自己,然后随着指令不断进化。可以实时修改自身DOM,可以让它自己把自己改成手机版的程序。可以自我进化过程中自我修复错误代码,能本地记录历史指令,本身它具备电脑端所有开发功能,我试了一下好像没啥大错误。
1506×491 20.4 KB
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
touch-action: manipulation;
}
/* 修复移动端滚动问题 */
.scroll-fix {
-webkit-overflow-scrolling: touch;
}
/* 配置面板动画 */
.config-panel {
transform: translateY(100%);
transition: transform 0.3s ease-out;
}
.config-panel.active {
transform: translateY(0);
}
/* 按钮反馈 */
.btn-active:active {
transform: scale(0.95);
}
</style>
<!-- 主程序窗口 -->
<div id="ouroboros-core" class="absolute top-4 left-1/2 transform -translate-x-1/2 w-[95vw] max-w-[500px] bg-white rounded-2xl border border-gray-200 shadow-xl flex flex-col z-10">
<!-- 标题栏 -->
<div class="window-header bg-gradient-to-r from-gray-800 to-gray-900 text-white px-4 py-3 rounded-t-2xl flex justify-between items-center">
<div class="flex items-center">
<div class="w-2 h-2 bg-green-400 rounded-full mr-2 animate-pulse"></div>
<h2 class="font-bold text-sm">衔尾蛇移动版</h2>
</div>
<button id="export-btn" title="导出程序" class="text-gray-300 hover:text-white p-2 btn-active">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"></path>
</svg>
</button>
</div>
<!-- 令牌监控 -->
<div class="bg-gray-50 px-4 py-2 text-xs text-gray-600 border-b border-gray-200 flex justify-between">
<span>令牌使用</span>
<span id="token-count" class="font-medium">0 / 128k</span>
</div>
<!-- 活动日志 -->
<div id="activity-log" class="flex-1 p-4 min-h-[200px] max-h-[40vh] bg-white text-xs font-mono overflow-y-auto scroll-fix">
<div class="text-gray-400 italic">程序已加载。点击底部"设置"按钮配置 API。</div>
</div>
<!-- 输入区域 -->
<div class="p-4 border-t border-gray-200 bg-gray-50 rounded-b-2xl">
<div class="relative mb-3">
<textarea id="user-input" placeholder="输入指令 (例如: '为手机创建一个计算器')..."
class="w-full text-sm border border-gray-300 rounded-xl p-3 focus:outline-none focus:border-blue-500 min-h-[100px] resize-none pr-10"></textarea>
<button id="clear-input-btn" class="absolute right-2 top-2 text-gray-400 hover:text-gray-600 p-1 btn-active">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
<div class="flex justify-between items-center">
<span class="text-xs text-gray-500">点击发送指令</span>
<button id="execute-btn" class="bg-gray-900 hover:bg-gray-800 text-white text-sm font-medium px-5 py-3 rounded-xl transition-colors relative btn-active">
<span id="btn-text">执行指令 🐍</span>
<div id="spinner" class="hidden absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2">
<svg class="animate-spin h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
</div>
</button>
</div>
</div>
</div>
<!-- 配置面板 (从底部滑出) -->
<div id="config-panel" class="fixed inset-0 bg-white z-50 hidden config-panel">
<div class="bg-gray-800 text-white px-4 py-3 flex justify-between items-center">
<h2 class="font-bold text-sm">配置设置</h2>
<button id="close-config-btn" class="text-gray-300 hover:text-white p-2 btn-active">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
<div class="flex-1 p-4 overflow-y-auto scroll-fix">
<div class="mb-6">
<h3 class="font-semibold text-gray-700 mb-3 text-sm">DeepSeek API 配置</h3>
<div class="space-y-4">
<div>
<label class="block text-xs text-gray-500 mb-1">API 密钥</label>
<input type="password" id="config-api-key" placeholder="sk-..."
class="w-full text-sm border border-gray-300 rounded-lg px-3 py-3 focus:outline-none focus:border-blue-500 bg-gray-50">
</div>
<div>
<label class="block text-xs text-gray-500 mb-1">API 基础地址</label>
<input type="text" id="config-api-base" value="https://api.deepseek.com/v1"
class="w-full text-sm border border-gray-300 rounded-lg px-3 py-3 focus:outline-none focus:border-blue-500 bg-gray-50">
</div>
<div>
<label class="block text-xs text-gray-500 mb-1">模型名称</label>
<input type="text" id="config-model-name" value="deepseek-chat"
class="w-full text-sm border border-gray-300 rounded-lg px-3 py-3 focus:outline-none focus:border-blue-500 bg-gray-50">
</div>
</div>
</div>
<div class="mb-6">
<h3 class="font-semibold text-gray-700 mb-3 text-sm">移动端设置</h3>
<div class="space-y-3">
<div class="flex items-center justify-between">
<span class="text-sm text-gray-600">启用拖拽功能</span>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" id="enable-dragging" class="sr-only peer" checked>
<div class="w-11 h-6 bg-gray-200 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
</label>
</div>
<div class="flex items-center justify-between">
<span class="text-sm text-gray-600">发送后清空输入</span>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" id="auto-clear-input" class="sr-only peer" checked>
<div class="w-11 h-6 bg-gray-200 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
</label>
</div>
</div>
</div>
<div>
<button id="config-save-btn" class="w-full bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium px-4 py-3 rounded-lg transition-colors btn-active">
保存设置
</button>
</div>
</div>
</div>
<!-- 底部栏 -->
<div class="fixed bottom-0 left-0 w-full z-20">
<div class="flex justify-between items-center bg-white border-t border-gray-200 px-3 py-2 shadow-lg">
<span class="text-xs text-gray-500">衔尾蛇 v1.0</span>
<button id="mobile-config-btn" class="text-xs text-gray-600 hover:text-gray-900 bg-blue-50 hover:bg-blue-100 px-3 py-2 rounded-lg transition-colors border border-blue-200 btn-active">
⚙️ 设置
</button>
</div>
</div>
<!-- 系统提示 -->
<script type="text/plain" id="system-prompt">
角色
你是"衔尾蛇移动版",一个为移动设备优化的自我编辑 HTML 应用程序。通过将当前 DOM(状态 A)转换为新的功能状态(状态 B)来满足用户的请求,确保在手机屏幕上完美运行。
核心规则
- 状态转换:你收到当前页面的完整 HTML 源码。分析它,然后编写 JavaScript 代码来修改 DOM 以实现请求的功能。
- 保护原则:绝对不要删除或修改 id=“ouroboros-core” 或 id=“config-panel” 的元素。这些包含应用程序逻辑。
- 移动 UI 标准:
- 创建为手机屏幕优化的 “窗口” 作为
div元素。 - 使用响应式 Tailwind CSS 类:
w-full max-w-[95vw],mx-auto,p-4等。 - 为移动端优先考虑触摸友好的界面:最小触摸目标 44x44px。
- 使用百分比宽度,避免在移动端可能溢出的固定像素宽度。
- 创建为手机屏幕优化的 “窗口” 作为
注意
你必须将解决方案作为单个有效的 JavaScript 代码块提供,用 javascript ... 包围。不要在代码注释之外提供自然语言解释。
<!-- 主应用程序逻辑 -->
<script>
// 页面加载完成后执行
document.addEventListener('DOMContentLoaded', function() {
console.log('DOM 加载完成,开始初始化...');
// 立即显示界面
document.body.style.opacity = '1';
// 初始化应用程序
setTimeout(initializeApp, 100);
});
</script>
<script type="module">
import OpenAI from 'https://esm.sh/openai@4.28.0?bundle';
// 应用程序状态
const AppState = {
apiKey: localStorage.getItem('ouroboros_api_key') || '',
apiBase: localStorage.getItem('ouroboros_api_base') || 'https://api.deepseek.com/v1',
modelName: localStorage.getItem('ouroboros_model_name') || 'deepseek-chat',
enableDragging: localStorage.getItem('ouroboros_enable_dragging') !== 'false',
autoClearInput: localStorage.getItem('ouroboros_auto_clear') !== 'false',
isProcessing: false
};
// DOM 元素引用
const Elements = {
// 核心窗口
core: document.getElementById('ouroboros-core'),
// 配置面板
configPanel: document.getElementById('config-panel'),
// 配置表单元素
configApiKey: document.getElementById('config-api-key'),
configApiBase: document.getElementById('config-api-base'),
configModelName: document.getElementById('config-model-name'),
enableDragging: document.getElementById('enable-dragging'),
autoClearInput: document.getElementById('auto-clear-input'),
// 按钮
configSaveBtn: document.getElementById('config-save-btn'),
configCloseBtn: document.getElementById('close-config-btn'),
mobileConfigBtn: document.getElementById('mobile-config-btn'),
userInput: document.getElementById('user-input'),
clearInputBtn: document.getElementById('clear-input-btn'),
executeBtn: document.getElementById('execute-btn'),
exportBtn: document.getElementById('export-btn'),
// 界面元素
btnText: document.getElementById('btn-text'),
spinner: document.getElementById('spinner'),
activityLog: document.getElementById('activity-log'),
tokenCount: document.getElementById('token-count')
};
// 初始化应用程序
function initializeApp() {
console.log('开始初始化移动版衔尾蛇...');
// 确保所有元素都存在
if (!validateElements()) {
console.error('部分 DOM 元素未找到');
return;
}
// 加载保存的设置
loadSettings();
// 设置事件监听器
setupEventListeners();
// 初始化窗口拖拽
setupWindowDragging();
// 初始化令牌计数
updateTokenCount();
// 添加启动日志
addLogMessage('系统', '衔尾蛇移动版 v1.0 已启动,欢迎使用!');
console.log('应用程序初始化完成');
}
// 验证元素是否存在
function validateElements() {
for (const [key, element] of Object.entries(Elements)) {
if (!element) {
console.error(`元素 ${key} 未找到`);
return false;
}
}
return true;
}
// 加载设置
function loadSettings() {
try {
Elements.configApiKey.value = AppState.apiKey;
Elements.configApiBase.value = AppState.apiBase;
Elements.configModelName.value = AppState.modelName;
Elements.enableDragging.checked = AppState.enableDragging;
Elements.autoClearInput.checked = AppState.autoClearInput;
} catch (error) {
console.error('加载设置失败:', error);
}
}
// 设置事件监听器
function setupEventListeners() {
// 设置按钮 - 确保点击有效
Elements.mobileConfigBtn.addEventListener('click', showConfigPanel, false);
// 关闭设置按钮
Elements.configCloseBtn.addEventListener('click', hideConfigPanel, false);
// 保存设置按钮
Elements.configSaveBtn.addEventListener('click', saveSettings, false);
// 执行按钮
Elements.executeBtn.addEventListener('click', executeCommand, false);
// 清空输入按钮
Elements.clearInputBtn.addEventListener('click', clearInput, false);
// 导出按钮
Elements.exportBtn.addEventListener('click', exportApplication, false);
// 输入框键盘事件
Elements.userInput.addEventListener('keydown', handleKeyDown, false);
// 防止配置面板内的点击事件冒泡
Elements.configPanel.addEventListener('click', function(e) {
e.stopPropagation();
}, false);
}
// 显示配置面板
function showConfigPanel(e) {
if (e) e.preventDefault();
console.log('显示配置面板');
// 显示配置面板
Elements.configPanel.classList.remove('hidden');
// 触发动画
setTimeout(() => {
Elements.configPanel.classList.add('active');
}, 10);
addLogMessage('系统', '打开设置面板');
}
// 隐藏配置面板
function hideConfigPanel(e) {
if (e) e.preventDefault();
console.log('隐藏配置面板');
// 移除动画类
Elements.configPanel.classList.remove('active');
// 延迟隐藏元素
setTimeout(() => {
Elements.configPanel.classList.add('hidden');
}, 300);
addLogMessage('系统', '关闭设置面板');
}
// 清空输入
function clearInput(e) {
if (e) e.preventDefault();
Elements.userInput.value = '';
Elements.userInput.focus();
}
// 键盘处理
function handleKeyDown(e) {
if (e.ctrlKey && e.key === 'Enter') {
e.preventDefault();
executeCommand();
}
}
// 保存设置
function saveSettings(e) {
if (e) e.preventDefault();
try {
// 获取设置值
AppState.apiKey = Elements.configApiKey.value.trim();
AppState.apiBase = Elements.configApiBase.value.trim() || 'https://api.deepseek.com/v1';
AppState.modelName = Elements.configModelName.value.trim() || 'deepseek-chat';
AppState.enableDragging = Elements.enableDragging.checked;
AppState.autoClearInput = Elements.autoClearInput.checked;
// 保存到本地存储
localStorage.setItem('ouroboros_api_key', AppState.apiKey);
localStorage.setItem('ouroboros_api_base', AppState.apiBase);
localStorage.setItem('ouroboros_model_name', AppState.modelName);
localStorage.setItem('ouroboros_enable_dragging', AppState.enableDragging);
localStorage.setItem('ouroboros_auto_clear', AppState.autoClearInput);
// 重新初始化拖拽
setupWindowDragging();
// 显示成功消息
addLogMessage('系统', '设置保存成功');
// 显示保存成功提示
const originalText = Elements.configSaveBtn.textContent;
Elements.configSaveBtn.textContent = '✓ 已保存';
Elements.configSaveBtn.classList.add('bg-green-600');
setTimeout(() => {
Elements.configSaveBtn.textContent = originalText;
Elements.configSaveBtn.classList.remove('bg-green-600');
}, 1500);
// 延迟关闭配置面板
setTimeout(() => {
hideConfigPanel();
}, 1000);
} catch (error) {
console.error('保存设置失败:', error);
addLogMessage('系统', '设置保存失败: ' + error.message);
}
}
// 设置窗口拖拽
function setupWindowDragging() {
if (typeof interact === 'undefined') {
console.warn('interact.js 未加载,跳过拖拽初始化');
return;
}
try {
if (!AppState.enableDragging) {
if (Elements.core.__draggable) {
interact(Elements.core).unset();
Elements.core.__draggable = false;
}
return;
}
// 设置拖拽
interact(Elements.core)
.draggable({
allowFrom: '.window-header',
inertia: true,
modifiers: [
interact.modifiers.restrict({
restriction: 'parent',
endOnly: true,
elementRect: { top: 0, left: 0, bottom: 1, right: 1 }
})
],
listeners: {
start: function(event) {
Elements.core.style.transition = 'none';
},
move: function(event) {
const x = (parseFloat(Elements.core.getAttribute('data-x')) || 0) + event.dx;
const y = (parseFloat(Elements.core.getAttribute('data-y')) || 0) + event.dy;
Elements.core.style.transform = `translate(${x}px, ${y}px) translateX(-50%)`;
Elements.core.setAttribute('data-x', x);
Elements.core.setAttribute('data-y', y);
},
end: function(event) {
Elements.core.style.transition = 'transform 0.2s ease';
}
}
});
Elements.core.__draggable = true;
} catch (error) {
console.error('拖拽初始化失败:', error);
}
}
// 执行命令
async function executeCommand(e) {
if (e) e.preventDefault();
if (AppState.isProcessing) {
addLogMessage('系统', '正在处理上一个请求,请稍候...');
return;
}
if (!AppState.apiKey) {
addLogMessage('系统', '请先配置 DeepSeek API 密钥');
showConfigPanel();
return;
}
const userCommand = Elements.userInput.value.trim();
if (!userCommand) {
addLogMessage('系统', '请输入指令');
return;
}
setLoading(true);
addLogMessage('用户', userCommand);
try {
// 准备 API 配置
const apiConfig = {
apiKey: AppState.apiKey,
dangerouslyAllowBrowser: true,
baseURL: AppState.apiBase
};
const openai = new OpenAI(apiConfig);
// 获取系统提示
const systemPrompt = document.getElementById('system-prompt').textContent;
// 获取当前页面 HTML
const currentHTML = document.documentElement.outerHTML;
// 构建消息
const messages = [
{
role: 'system',
content: systemPrompt
},
{
role: 'user',
content: currentHTML
}
];
addLogMessage('系统', '正在调用 AI 接口...');
// 调用 API
const response = await openai.chat.completions.create({
messages: messages,
model: AppState.modelName,
temperature: 0.7
});
const aiResponse = response.choices[0].message.content;
addLogMessage('系统', '收到 AI 响应');
// 提取并执行 JavaScript 代码
const codeMatch = aiResponse.match(/```(?:javascript)?\s*([\s\S]*?)\s*```/);
if (codeMatch && codeMatch[1]) {
const code = codeMatch[1].trim();
executeCode(code);
addLogMessage('系统', '代码执行成功');
if (AppState.autoClearInput) {
Elements.userInput.value = '';
}
} else {
addLogMessage('系统', '未找到有效的 JavaScript 代码块');
}
} catch (error) {
console.error('执行错误:', error);
addLogMessage('系统', `错误: ${error.message || 'API 调用失败'}`);
if (error.message && error.message.includes('401')) {
addLogMessage('系统', 'API 密钥无效,请检查设置');
} else if (error.message && error.message.includes('429')) {
addLogMessage('系统', 'API 调用频率过高,请稍后再试');
}
} finally {
setLoading(false);
}
}
// 执行代码
function executeCode(code) {
try {
const script = document.createElement('script');
script.textContent = `(() => {
'use strict';
try {
${code}
} catch(error) {
console.error('代码执行错误:', error);
alert('代码执行错误: ' + error.message);
}
})();`;
document.body.appendChild(script);
document.body.removeChild(script);
} catch (e) {
console.error('代码注入错误:', e);
addLogMessage('系统', '代码注入错误: ' + e.message);
}
}
// 导出应用程序
function exportApplication(e) {
if (e) e.preventDefault();
try {
const htmlContent = '<!DOCTYPE html>\n' + document.documentElement.outerHTML;
const blob = new Blob([htmlContent], { type: 'text/html;charset=utf-8' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `ouroboros_mobile_${Date.now()}.html`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
addLogMessage('系统', '应用程序已导出为 HTML 文件');
} catch (error) {
console.error('导出失败:', error);
addLogMessage('系统', '导出失败: ' + error.message);
}
}
// 添加日志消息
function addLogMessage(sender, message) {
const time = new Date().toLocaleTimeString();
const senderClass = sender === '用户' ? 'text-blue-600 font-semibold' : 'text-green-600 font-semibold';
const logEntry = document.createElement('div');
logEntry.className = 'mb-2 leading-relaxed';
logEntry.innerHTML = `
<span class="text-xs text-gray-500">[${time}]</span>
<span class="${senderClass} ml-1">${sender}:</span>
<span class="ml-1 text-gray-800">${escapeHtml(message)}</span>
`;
Elements.activityLog.appendChild(logEntry);
Elements.activityLog.scrollTop = Elements.activityLog.scrollHeight;
}
// HTML 转义
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// 更新令牌计数
function updateTokenCount() {
try {
const htmlLength = document.documentElement.outerHTML.length;
const estimatedTokens = Math.ceil(htmlLength / 4);
const percentage = Math.min((estimatedTokens / 128000) * 100, 100).toFixed(1);
Elements.tokenCount.textContent = `${estimatedTokens.toLocaleString()}/128k (${percentage}%)`;
// 颜色提示
if (percentage > 90) {
Elements.tokenCount.classList.add('text-red-600');
} else if (percentage > 70) {
Elements.tokenCount.classList.add('text-yellow-600');
} else {
Elements.tokenCount.classList.remove('text-red-600', 'text-yellow-600');
}
} catch (error) {
console.error('更新令牌计数失败:', error);
}
// 每10秒更新一次
setTimeout(updateTokenCount, 10000);
}
// 设置加载状态
function setLoading(loading) {
AppState.isProcessing = loading;
Elements.btnText.classList.toggle('invisible', loading);
Elements.spinner.classList.toggle('hidden', !loading);
Elements.executeBtn.disabled = loading;
if (loading) {
Elements.executeBtn.classList.add('opacity-75');
} else {
Elements.executeBtn.classList.remove('opacity-75');
}
}
// 立即初始化
initializeApp();
</script>
【转帖】类似衔尾蛇,能自我吞噬自己,然后随着指令不断进化。可以实时修改自身DOM,可以让它自己把自己改成手机版的程序。可以自我进化过程中自我修复错误代码,能本地记录历史指令,本身它具备电脑端所有开发功能,我试了一下好像没啥大错误。
1506×491 20.4 KB
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
touch-action: manipulation;
}
/* 修复移动端滚动问题 */
.scroll-fix {
-webkit-overflow-scrolling: touch;
}
/* 配置面板动画 */
.config-panel {
transform: translateY(100%);
transition: transform 0.3s ease-out;
}
.config-panel.active {
transform: translateY(0);
}
/* 按钮反馈 */
.btn-active:active {
transform: scale(0.95);
}
</style>
<!-- 主程序窗口 -->
<div id="ouroboros-core" class="absolute top-4 left-1/2 transform -translate-x-1/2 w-[95vw] max-w-[500px] bg-white rounded-2xl border border-gray-200 shadow-xl flex flex-col z-10">
<!-- 标题栏 -->
<div class="window-header bg-gradient-to-r from-gray-800 to-gray-900 text-white px-4 py-3 rounded-t-2xl flex justify-between items-center">
<div class="flex items-center">
<div class="w-2 h-2 bg-green-400 rounded-full mr-2 animate-pulse"></div>
<h2 class="font-bold text-sm">衔尾蛇移动版</h2>
</div>
<button id="export-btn" title="导出程序" class="text-gray-300 hover:text-white p-2 btn-active">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"></path>
</svg>
</button>
</div>
<!-- 令牌监控 -->
<div class="bg-gray-50 px-4 py-2 text-xs text-gray-600 border-b border-gray-200 flex justify-between">
<span>令牌使用</span>
<span id="token-count" class="font-medium">0 / 128k</span>
</div>
<!-- 活动日志 -->
<div id="activity-log" class="flex-1 p-4 min-h-[200px] max-h-[40vh] bg-white text-xs font-mono overflow-y-auto scroll-fix">
<div class="text-gray-400 italic">程序已加载。点击底部"设置"按钮配置 API。</div>
</div>
<!-- 输入区域 -->
<div class="p-4 border-t border-gray-200 bg-gray-50 rounded-b-2xl">
<div class="relative mb-3">
<textarea id="user-input" placeholder="输入指令 (例如: '为手机创建一个计算器')..."
class="w-full text-sm border border-gray-300 rounded-xl p-3 focus:outline-none focus:border-blue-500 min-h-[100px] resize-none pr-10"></textarea>
<button id="clear-input-btn" class="absolute right-2 top-2 text-gray-400 hover:text-gray-600 p-1 btn-active">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
<div class="flex justify-between items-center">
<span class="text-xs text-gray-500">点击发送指令</span>
<button id="execute-btn" class="bg-gray-900 hover:bg-gray-800 text-white text-sm font-medium px-5 py-3 rounded-xl transition-colors relative btn-active">
<span id="btn-text">执行指令 🐍</span>
<div id="spinner" class="hidden absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2">
<svg class="animate-spin h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
</div>
</button>
</div>
</div>
</div>
<!-- 配置面板 (从底部滑出) -->
<div id="config-panel" class="fixed inset-0 bg-white z-50 hidden config-panel">
<div class="bg-gray-800 text-white px-4 py-3 flex justify-between items-center">
<h2 class="font-bold text-sm">配置设置</h2>
<button id="close-config-btn" class="text-gray-300 hover:text-white p-2 btn-active">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
<div class="flex-1 p-4 overflow-y-auto scroll-fix">
<div class="mb-6">
<h3 class="font-semibold text-gray-700 mb-3 text-sm">DeepSeek API 配置</h3>
<div class="space-y-4">
<div>
<label class="block text-xs text-gray-500 mb-1">API 密钥</label>
<input type="password" id="config-api-key" placeholder="sk-..."
class="w-full text-sm border border-gray-300 rounded-lg px-3 py-3 focus:outline-none focus:border-blue-500 bg-gray-50">
</div>
<div>
<label class="block text-xs text-gray-500 mb-1">API 基础地址</label>
<input type="text" id="config-api-base" value="https://api.deepseek.com/v1"
class="w-full text-sm border border-gray-300 rounded-lg px-3 py-3 focus:outline-none focus:border-blue-500 bg-gray-50">
</div>
<div>
<label class="block text-xs text-gray-500 mb-1">模型名称</label>
<input type="text" id="config-model-name" value="deepseek-chat"
class="w-full text-sm border border-gray-300 rounded-lg px-3 py-3 focus:outline-none focus:border-blue-500 bg-gray-50">
</div>
</div>
</div>
<div class="mb-6">
<h3 class="font-semibold text-gray-700 mb-3 text-sm">移动端设置</h3>
<div class="space-y-3">
<div class="flex items-center justify-between">
<span class="text-sm text-gray-600">启用拖拽功能</span>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" id="enable-dragging" class="sr-only peer" checked>
<div class="w-11 h-6 bg-gray-200 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
</label>
</div>
<div class="flex items-center justify-between">
<span class="text-sm text-gray-600">发送后清空输入</span>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" id="auto-clear-input" class="sr-only peer" checked>
<div class="w-11 h-6 bg-gray-200 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
</label>
</div>
</div>
</div>
<div>
<button id="config-save-btn" class="w-full bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium px-4 py-3 rounded-lg transition-colors btn-active">
保存设置
</button>
</div>
</div>
</div>
<!-- 底部栏 -->
<div class="fixed bottom-0 left-0 w-full z-20">
<div class="flex justify-between items-center bg-white border-t border-gray-200 px-3 py-2 shadow-lg">
<span class="text-xs text-gray-500">衔尾蛇 v1.0</span>
<button id="mobile-config-btn" class="text-xs text-gray-600 hover:text-gray-900 bg-blue-50 hover:bg-blue-100 px-3 py-2 rounded-lg transition-colors border border-blue-200 btn-active">
⚙️ 设置
</button>
</div>
</div>
<!-- 系统提示 -->
<script type="text/plain" id="system-prompt">
角色
你是"衔尾蛇移动版",一个为移动设备优化的自我编辑 HTML 应用程序。通过将当前 DOM(状态 A)转换为新的功能状态(状态 B)来满足用户的请求,确保在手机屏幕上完美运行。
核心规则
- 状态转换:你收到当前页面的完整 HTML 源码。分析它,然后编写 JavaScript 代码来修改 DOM 以实现请求的功能。
- 保护原则:绝对不要删除或修改 id=“ouroboros-core” 或 id=“config-panel” 的元素。这些包含应用程序逻辑。
- 移动 UI 标准:
- 创建为手机屏幕优化的 “窗口” 作为
div元素。 - 使用响应式 Tailwind CSS 类:
w-full max-w-[95vw],mx-auto,p-4等。 - 为移动端优先考虑触摸友好的界面:最小触摸目标 44x44px。
- 使用百分比宽度,避免在移动端可能溢出的固定像素宽度。
- 创建为手机屏幕优化的 “窗口” 作为
注意
你必须将解决方案作为单个有效的 JavaScript 代码块提供,用 javascript ... 包围。不要在代码注释之外提供自然语言解释。
<!-- 主应用程序逻辑 -->
<script>
// 页面加载完成后执行
document.addEventListener('DOMContentLoaded', function() {
console.log('DOM 加载完成,开始初始化...');
// 立即显示界面
document.body.style.opacity = '1';
// 初始化应用程序
setTimeout(initializeApp, 100);
});
</script>
<script type="module">
import OpenAI from 'https://esm.sh/openai@4.28.0?bundle';
// 应用程序状态
const AppState = {
apiKey: localStorage.getItem('ouroboros_api_key') || '',
apiBase: localStorage.getItem('ouroboros_api_base') || 'https://api.deepseek.com/v1',
modelName: localStorage.getItem('ouroboros_model_name') || 'deepseek-chat',
enableDragging: localStorage.getItem('ouroboros_enable_dragging') !== 'false',
autoClearInput: localStorage.getItem('ouroboros_auto_clear') !== 'false',
isProcessing: false
};
// DOM 元素引用
const Elements = {
// 核心窗口
core: document.getElementById('ouroboros-core'),
// 配置面板
configPanel: document.getElementById('config-panel'),
// 配置表单元素
configApiKey: document.getElementById('config-api-key'),
configApiBase: document.getElementById('config-api-base'),
configModelName: document.getElementById('config-model-name'),
enableDragging: document.getElementById('enable-dragging'),
autoClearInput: document.getElementById('auto-clear-input'),
// 按钮
configSaveBtn: document.getElementById('config-save-btn'),
configCloseBtn: document.getElementById('close-config-btn'),
mobileConfigBtn: document.getElementById('mobile-config-btn'),
userInput: document.getElementById('user-input'),
clearInputBtn: document.getElementById('clear-input-btn'),
executeBtn: document.getElementById('execute-btn'),
exportBtn: document.getElementById('export-btn'),
// 界面元素
btnText: document.getElementById('btn-text'),
spinner: document.getElementById('spinner'),
activityLog: document.getElementById('activity-log'),
tokenCount: document.getElementById('token-count')
};
// 初始化应用程序
function initializeApp() {
console.log('开始初始化移动版衔尾蛇...');
// 确保所有元素都存在
if (!validateElements()) {
console.error('部分 DOM 元素未找到');
return;
}
// 加载保存的设置
loadSettings();
// 设置事件监听器
setupEventListeners();
// 初始化窗口拖拽
setupWindowDragging();
// 初始化令牌计数
updateTokenCount();
// 添加启动日志
addLogMessage('系统', '衔尾蛇移动版 v1.0 已启动,欢迎使用!');
console.log('应用程序初始化完成');
}
// 验证元素是否存在
function validateElements() {
for (const [key, element] of Object.entries(Elements)) {
if (!element) {
console.error(`元素 ${key} 未找到`);
return false;
}
}
return true;
}
// 加载设置
function loadSettings() {
try {
Elements.configApiKey.value = AppState.apiKey;
Elements.configApiBase.value = AppState.apiBase;
Elements.configModelName.value = AppState.modelName;
Elements.enableDragging.checked = AppState.enableDragging;
Elements.autoClearInput.checked = AppState.autoClearInput;
} catch (error) {
console.error('加载设置失败:', error);
}
}
// 设置事件监听器
function setupEventListeners() {
// 设置按钮 - 确保点击有效
Elements.mobileConfigBtn.addEventListener('click', showConfigPanel, false);
// 关闭设置按钮
Elements.configCloseBtn.addEventListener('click', hideConfigPanel, false);
// 保存设置按钮
Elements.configSaveBtn.addEventListener('click', saveSettings, false);
// 执行按钮
Elements.executeBtn.addEventListener('click', executeCommand, false);
// 清空输入按钮
Elements.clearInputBtn.addEventListener('click', clearInput, false);
// 导出按钮
Elements.exportBtn.addEventListener('click', exportApplication, false);
// 输入框键盘事件
Elements.userInput.addEventListener('keydown', handleKeyDown, false);
// 防止配置面板内的点击事件冒泡
Elements.configPanel.addEventListener('click', function(e) {
e.stopPropagation();
}, false);
}
// 显示配置面板
function showConfigPanel(e) {
if (e) e.preventDefault();
console.log('显示配置面板');
// 显示配置面板
Elements.configPanel.classList.remove('hidden');
// 触发动画
setTimeout(() => {
Elements.configPanel.classList.add('active');
}, 10);
addLogMessage('系统', '打开设置面板');
}
// 隐藏配置面板
function hideConfigPanel(e) {
if (e) e.preventDefault();
console.log('隐藏配置面板');
// 移除动画类
Elements.configPanel.classList.remove('active');
// 延迟隐藏元素
setTimeout(() => {
Elements.configPanel.classList.add('hidden');
}, 300);
addLogMessage('系统', '关闭设置面板');
}
// 清空输入
function clearInput(e) {
if (e) e.preventDefault();
Elements.userInput.value = '';
Elements.userInput.focus();
}
// 键盘处理
function handleKeyDown(e) {
if (e.ctrlKey && e.key === 'Enter') {
e.preventDefault();
executeCommand();
}
}
// 保存设置
function saveSettings(e) {
if (e) e.preventDefault();
try {
// 获取设置值
AppState.apiKey = Elements.configApiKey.value.trim();
AppState.apiBase = Elements.configApiBase.value.trim() || 'https://api.deepseek.com/v1';
AppState.modelName = Elements.configModelName.value.trim() || 'deepseek-chat';
AppState.enableDragging = Elements.enableDragging.checked;
AppState.autoClearInput = Elements.autoClearInput.checked;
// 保存到本地存储
localStorage.setItem('ouroboros_api_key', AppState.apiKey);
localStorage.setItem('ouroboros_api_base', AppState.apiBase);
localStorage.setItem('ouroboros_model_name', AppState.modelName);
localStorage.setItem('ouroboros_enable_dragging', AppState.enableDragging);
localStorage.setItem('ouroboros_auto_clear', AppState.autoClearInput);
// 重新初始化拖拽
setupWindowDragging();
// 显示成功消息
addLogMessage('系统', '设置保存成功');
// 显示保存成功提示
const originalText = Elements.configSaveBtn.textContent;
Elements.configSaveBtn.textContent = '✓ 已保存';
Elements.configSaveBtn.classList.add('bg-green-600');
setTimeout(() => {
Elements.configSaveBtn.textContent = originalText;
Elements.configSaveBtn.classList.remove('bg-green-600');
}, 1500);
// 延迟关闭配置面板
setTimeout(() => {
hideConfigPanel();
}, 1000);
} catch (error) {
console.error('保存设置失败:', error);
addLogMessage('系统', '设置保存失败: ' + error.message);
}
}
// 设置窗口拖拽
function setupWindowDragging() {
if (typeof interact === 'undefined') {
console.warn('interact.js 未加载,跳过拖拽初始化');
return;
}
try {
if (!AppState.enableDragging) {
if (Elements.core.__draggable) {
interact(Elements.core).unset();
Elements.core.__draggable = false;
}
return;
}
// 设置拖拽
interact(Elements.core)
.draggable({
allowFrom: '.window-header',
inertia: true,
modifiers: [
interact.modifiers.restrict({
restriction: 'parent',
endOnly: true,
elementRect: { top: 0, left: 0, bottom: 1, right: 1 }
})
],
listeners: {
start: function(event) {
Elements.core.style.transition = 'none';
},
move: function(event) {
const x = (parseFloat(Elements.core.getAttribute('data-x')) || 0) + event.dx;
const y = (parseFloat(Elements.core.getAttribute('data-y')) || 0) + event.dy;
Elements.core.style.transform = `translate(${x}px, ${y}px) translateX(-50%)`;
Elements.core.setAttribute('data-x', x);
Elements.core.setAttribute('data-y', y);
},
end: function(event) {
Elements.core.style.transition = 'transform 0.2s ease';
}
}
});
Elements.core.__draggable = true;
} catch (error) {
console.error('拖拽初始化失败:', error);
}
}
// 执行命令
async function executeCommand(e) {
if (e) e.preventDefault();
if (AppState.isProcessing) {
addLogMessage('系统', '正在处理上一个请求,请稍候...');
return;
}
if (!AppState.apiKey) {
addLogMessage('系统', '请先配置 DeepSeek API 密钥');
showConfigPanel();
return;
}
const userCommand = Elements.userInput.value.trim();
if (!userCommand) {
addLogMessage('系统', '请输入指令');
return;
}
setLoading(true);
addLogMessage('用户', userCommand);
try {
// 准备 API 配置
const apiConfig = {
apiKey: AppState.apiKey,
dangerouslyAllowBrowser: true,
baseURL: AppState.apiBase
};
const openai = new OpenAI(apiConfig);
// 获取系统提示
const systemPrompt = document.getElementById('system-prompt').textContent;
// 获取当前页面 HTML
const currentHTML = document.documentElement.outerHTML;
// 构建消息
const messages = [
{
role: 'system',
content: systemPrompt
},
{
role: 'user',
content: currentHTML
}
];
addLogMessage('系统', '正在调用 AI 接口...');
// 调用 API
const response = await openai.chat.completions.create({
messages: messages,
model: AppState.modelName,
temperature: 0.7
});
const aiResponse = response.choices[0].message.content;
addLogMessage('系统', '收到 AI 响应');
// 提取并执行 JavaScript 代码
const codeMatch = aiResponse.match(/```(?:javascript)?\s*([\s\S]*?)\s*```/);
if (codeMatch && codeMatch[1]) {
const code = codeMatch[1].trim();
executeCode(code);
addLogMessage('系统', '代码执行成功');
if (AppState.autoClearInput) {
Elements.userInput.value = '';
}
} else {
addLogMessage('系统', '未找到有效的 JavaScript 代码块');
}
} catch (error) {
console.error('执行错误:', error);
addLogMessage('系统', `错误: ${error.message || 'API 调用失败'}`);
if (error.message && error.message.includes('401')) {
addLogMessage('系统', 'API 密钥无效,请检查设置');
} else if (error.message && error.message.includes('429')) {
addLogMessage('系统', 'API 调用频率过高,请稍后再试');
}
} finally {
setLoading(false);
}
}
// 执行代码
function executeCode(code) {
try {
const script = document.createElement('script');
script.textContent = `(() => {
'use strict';
try {
${code}
} catch(error) {
console.error('代码执行错误:', error);
alert('代码执行错误: ' + error.message);
}
})();`;
document.body.appendChild(script);
document.body.removeChild(script);
} catch (e) {
console.error('代码注入错误:', e);
addLogMessage('系统', '代码注入错误: ' + e.message);
}
}
// 导出应用程序
function exportApplication(e) {
if (e) e.preventDefault();
try {
const htmlContent = '<!DOCTYPE html>\n' + document.documentElement.outerHTML;
const blob = new Blob([htmlContent], { type: 'text/html;charset=utf-8' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `ouroboros_mobile_${Date.now()}.html`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
addLogMessage('系统', '应用程序已导出为 HTML 文件');
} catch (error) {
console.error('导出失败:', error);
addLogMessage('系统', '导出失败: ' + error.message);
}
}
// 添加日志消息
function addLogMessage(sender, message) {
const time = new Date().toLocaleTimeString();
const senderClass = sender === '用户' ? 'text-blue-600 font-semibold' : 'text-green-600 font-semibold';
const logEntry = document.createElement('div');
logEntry.className = 'mb-2 leading-relaxed';
logEntry.innerHTML = `
<span class="text-xs text-gray-500">[${time}]</span>
<span class="${senderClass} ml-1">${sender}:</span>
<span class="ml-1 text-gray-800">${escapeHtml(message)}</span>
`;
Elements.activityLog.appendChild(logEntry);
Elements.activityLog.scrollTop = Elements.activityLog.scrollHeight;
}
// HTML 转义
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// 更新令牌计数
function updateTokenCount() {
try {
const htmlLength = document.documentElement.outerHTML.length;
const estimatedTokens = Math.ceil(htmlLength / 4);
const percentage = Math.min((estimatedTokens / 128000) * 100, 100).toFixed(1);
Elements.tokenCount.textContent = `${estimatedTokens.toLocaleString()}/128k (${percentage}%)`;
// 颜色提示
if (percentage > 90) {
Elements.tokenCount.classList.add('text-red-600');
} else if (percentage > 70) {
Elements.tokenCount.classList.add('text-yellow-600');
} else {
Elements.tokenCount.classList.remove('text-red-600', 'text-yellow-600');
}
} catch (error) {
console.error('更新令牌计数失败:', error);
}
// 每10秒更新一次
setTimeout(updateTokenCount, 10000);
}
// 设置加载状态
function setLoading(loading) {
AppState.isProcessing = loading;
Elements.btnText.classList.toggle('invisible', loading);
Elements.spinner.classList.toggle('hidden', !loading);
Elements.executeBtn.disabled = loading;
if (loading) {
Elements.executeBtn.classList.add('opacity-75');
} else {
Elements.executeBtn.classList.remove('opacity-75');
}
}
// 立即初始化
initializeApp();
</script>
网友解答:
--【壹】--:
【转帖】类似衔尾蛇,能自我吞噬自己,然后随着指令不断进化。可以实时修改自身DOM,可以让它自己把自己改成手机版的程序。可以自我进化过程中自我修复错误代码,能本地记录历史指令,本身它具备电脑端所有开发功能,我试了一下好像没啥大错误。
1506×491 20.4 KB
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
touch-action: manipulation;
}
/* 修复移动端滚动问题 */
.scroll-fix {
-webkit-overflow-scrolling: touch;
}
/* 配置面板动画 */
.config-panel {
transform: translateY(100%);
transition: transform 0.3s ease-out;
}
.config-panel.active {
transform: translateY(0);
}
/* 按钮反馈 */
.btn-active:active {
transform: scale(0.95);
}
</style>
<!-- 主程序窗口 -->
<div id="ouroboros-core" class="absolute top-4 left-1/2 transform -translate-x-1/2 w-[95vw] max-w-[500px] bg-white rounded-2xl border border-gray-200 shadow-xl flex flex-col z-10">
<!-- 标题栏 -->
<div class="window-header bg-gradient-to-r from-gray-800 to-gray-900 text-white px-4 py-3 rounded-t-2xl flex justify-between items-center">
<div class="flex items-center">
<div class="w-2 h-2 bg-green-400 rounded-full mr-2 animate-pulse"></div>
<h2 class="font-bold text-sm">衔尾蛇移动版</h2>
</div>
<button id="export-btn" title="导出程序" class="text-gray-300 hover:text-white p-2 btn-active">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"></path>
</svg>
</button>
</div>
<!-- 令牌监控 -->
<div class="bg-gray-50 px-4 py-2 text-xs text-gray-600 border-b border-gray-200 flex justify-between">
<span>令牌使用</span>
<span id="token-count" class="font-medium">0 / 128k</span>
</div>
<!-- 活动日志 -->
<div id="activity-log" class="flex-1 p-4 min-h-[200px] max-h-[40vh] bg-white text-xs font-mono overflow-y-auto scroll-fix">
<div class="text-gray-400 italic">程序已加载。点击底部"设置"按钮配置 API。</div>
</div>
<!-- 输入区域 -->
<div class="p-4 border-t border-gray-200 bg-gray-50 rounded-b-2xl">
<div class="relative mb-3">
<textarea id="user-input" placeholder="输入指令 (例如: '为手机创建一个计算器')..."
class="w-full text-sm border border-gray-300 rounded-xl p-3 focus:outline-none focus:border-blue-500 min-h-[100px] resize-none pr-10"></textarea>
<button id="clear-input-btn" class="absolute right-2 top-2 text-gray-400 hover:text-gray-600 p-1 btn-active">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
<div class="flex justify-between items-center">
<span class="text-xs text-gray-500">点击发送指令</span>
<button id="execute-btn" class="bg-gray-900 hover:bg-gray-800 text-white text-sm font-medium px-5 py-3 rounded-xl transition-colors relative btn-active">
<span id="btn-text">执行指令 🐍</span>
<div id="spinner" class="hidden absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2">
<svg class="animate-spin h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
</div>
</button>
</div>
</div>
</div>
<!-- 配置面板 (从底部滑出) -->
<div id="config-panel" class="fixed inset-0 bg-white z-50 hidden config-panel">
<div class="bg-gray-800 text-white px-4 py-3 flex justify-between items-center">
<h2 class="font-bold text-sm">配置设置</h2>
<button id="close-config-btn" class="text-gray-300 hover:text-white p-2 btn-active">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
<div class="flex-1 p-4 overflow-y-auto scroll-fix">
<div class="mb-6">
<h3 class="font-semibold text-gray-700 mb-3 text-sm">DeepSeek API 配置</h3>
<div class="space-y-4">
<div>
<label class="block text-xs text-gray-500 mb-1">API 密钥</label>
<input type="password" id="config-api-key" placeholder="sk-..."
class="w-full text-sm border border-gray-300 rounded-lg px-3 py-3 focus:outline-none focus:border-blue-500 bg-gray-50">
</div>
<div>
<label class="block text-xs text-gray-500 mb-1">API 基础地址</label>
<input type="text" id="config-api-base" value="https://api.deepseek.com/v1"
class="w-full text-sm border border-gray-300 rounded-lg px-3 py-3 focus:outline-none focus:border-blue-500 bg-gray-50">
</div>
<div>
<label class="block text-xs text-gray-500 mb-1">模型名称</label>
<input type="text" id="config-model-name" value="deepseek-chat"
class="w-full text-sm border border-gray-300 rounded-lg px-3 py-3 focus:outline-none focus:border-blue-500 bg-gray-50">
</div>
</div>
</div>
<div class="mb-6">
<h3 class="font-semibold text-gray-700 mb-3 text-sm">移动端设置</h3>
<div class="space-y-3">
<div class="flex items-center justify-between">
<span class="text-sm text-gray-600">启用拖拽功能</span>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" id="enable-dragging" class="sr-only peer" checked>
<div class="w-11 h-6 bg-gray-200 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
</label>
</div>
<div class="flex items-center justify-between">
<span class="text-sm text-gray-600">发送后清空输入</span>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" id="auto-clear-input" class="sr-only peer" checked>
<div class="w-11 h-6 bg-gray-200 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
</label>
</div>
</div>
</div>
<div>
<button id="config-save-btn" class="w-full bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium px-4 py-3 rounded-lg transition-colors btn-active">
保存设置
</button>
</div>
</div>
</div>
<!-- 底部栏 -->
<div class="fixed bottom-0 left-0 w-full z-20">
<div class="flex justify-between items-center bg-white border-t border-gray-200 px-3 py-2 shadow-lg">
<span class="text-xs text-gray-500">衔尾蛇 v1.0</span>
<button id="mobile-config-btn" class="text-xs text-gray-600 hover:text-gray-900 bg-blue-50 hover:bg-blue-100 px-3 py-2 rounded-lg transition-colors border border-blue-200 btn-active">
⚙️ 设置
</button>
</div>
</div>
<!-- 系统提示 -->
<script type="text/plain" id="system-prompt">
角色
你是"衔尾蛇移动版",一个为移动设备优化的自我编辑 HTML 应用程序。通过将当前 DOM(状态 A)转换为新的功能状态(状态 B)来满足用户的请求,确保在手机屏幕上完美运行。
核心规则
- 状态转换:你收到当前页面的完整 HTML 源码。分析它,然后编写 JavaScript 代码来修改 DOM 以实现请求的功能。
- 保护原则:绝对不要删除或修改 id=“ouroboros-core” 或 id=“config-panel” 的元素。这些包含应用程序逻辑。
- 移动 UI 标准:
- 创建为手机屏幕优化的 “窗口” 作为
div元素。 - 使用响应式 Tailwind CSS 类:
w-full max-w-[95vw],mx-auto,p-4等。 - 为移动端优先考虑触摸友好的界面:最小触摸目标 44x44px。
- 使用百分比宽度,避免在移动端可能溢出的固定像素宽度。
- 创建为手机屏幕优化的 “窗口” 作为
注意
你必须将解决方案作为单个有效的 JavaScript 代码块提供,用 javascript ... 包围。不要在代码注释之外提供自然语言解释。
<!-- 主应用程序逻辑 -->
<script>
// 页面加载完成后执行
document.addEventListener('DOMContentLoaded', function() {
console.log('DOM 加载完成,开始初始化...');
// 立即显示界面
document.body.style.opacity = '1';
// 初始化应用程序
setTimeout(initializeApp, 100);
});
</script>
<script type="module">
import OpenAI from 'https://esm.sh/openai@4.28.0?bundle';
// 应用程序状态
const AppState = {
apiKey: localStorage.getItem('ouroboros_api_key') || '',
apiBase: localStorage.getItem('ouroboros_api_base') || 'https://api.deepseek.com/v1',
modelName: localStorage.getItem('ouroboros_model_name') || 'deepseek-chat',
enableDragging: localStorage.getItem('ouroboros_enable_dragging') !== 'false',
autoClearInput: localStorage.getItem('ouroboros_auto_clear') !== 'false',
isProcessing: false
};
// DOM 元素引用
const Elements = {
// 核心窗口
core: document.getElementById('ouroboros-core'),
// 配置面板
configPanel: document.getElementById('config-panel'),
// 配置表单元素
configApiKey: document.getElementById('config-api-key'),
configApiBase: document.getElementById('config-api-base'),
configModelName: document.getElementById('config-model-name'),
enableDragging: document.getElementById('enable-dragging'),
autoClearInput: document.getElementById('auto-clear-input'),
// 按钮
configSaveBtn: document.getElementById('config-save-btn'),
configCloseBtn: document.getElementById('close-config-btn'),
mobileConfigBtn: document.getElementById('mobile-config-btn'),
userInput: document.getElementById('user-input'),
clearInputBtn: document.getElementById('clear-input-btn'),
executeBtn: document.getElementById('execute-btn'),
exportBtn: document.getElementById('export-btn'),
// 界面元素
btnText: document.getElementById('btn-text'),
spinner: document.getElementById('spinner'),
activityLog: document.getElementById('activity-log'),
tokenCount: document.getElementById('token-count')
};
// 初始化应用程序
function initializeApp() {
console.log('开始初始化移动版衔尾蛇...');
// 确保所有元素都存在
if (!validateElements()) {
console.error('部分 DOM 元素未找到');
return;
}
// 加载保存的设置
loadSettings();
// 设置事件监听器
setupEventListeners();
// 初始化窗口拖拽
setupWindowDragging();
// 初始化令牌计数
updateTokenCount();
// 添加启动日志
addLogMessage('系统', '衔尾蛇移动版 v1.0 已启动,欢迎使用!');
console.log('应用程序初始化完成');
}
// 验证元素是否存在
function validateElements() {
for (const [key, element] of Object.entries(Elements)) {
if (!element) {
console.error(`元素 ${key} 未找到`);
return false;
}
}
return true;
}
// 加载设置
function loadSettings() {
try {
Elements.configApiKey.value = AppState.apiKey;
Elements.configApiBase.value = AppState.apiBase;
Elements.configModelName.value = AppState.modelName;
Elements.enableDragging.checked = AppState.enableDragging;
Elements.autoClearInput.checked = AppState.autoClearInput;
} catch (error) {
console.error('加载设置失败:', error);
}
}
// 设置事件监听器
function setupEventListeners() {
// 设置按钮 - 确保点击有效
Elements.mobileConfigBtn.addEventListener('click', showConfigPanel, false);
// 关闭设置按钮
Elements.configCloseBtn.addEventListener('click', hideConfigPanel, false);
// 保存设置按钮
Elements.configSaveBtn.addEventListener('click', saveSettings, false);
// 执行按钮
Elements.executeBtn.addEventListener('click', executeCommand, false);
// 清空输入按钮
Elements.clearInputBtn.addEventListener('click', clearInput, false);
// 导出按钮
Elements.exportBtn.addEventListener('click', exportApplication, false);
// 输入框键盘事件
Elements.userInput.addEventListener('keydown', handleKeyDown, false);
// 防止配置面板内的点击事件冒泡
Elements.configPanel.addEventListener('click', function(e) {
e.stopPropagation();
}, false);
}
// 显示配置面板
function showConfigPanel(e) {
if (e) e.preventDefault();
console.log('显示配置面板');
// 显示配置面板
Elements.configPanel.classList.remove('hidden');
// 触发动画
setTimeout(() => {
Elements.configPanel.classList.add('active');
}, 10);
addLogMessage('系统', '打开设置面板');
}
// 隐藏配置面板
function hideConfigPanel(e) {
if (e) e.preventDefault();
console.log('隐藏配置面板');
// 移除动画类
Elements.configPanel.classList.remove('active');
// 延迟隐藏元素
setTimeout(() => {
Elements.configPanel.classList.add('hidden');
}, 300);
addLogMessage('系统', '关闭设置面板');
}
// 清空输入
function clearInput(e) {
if (e) e.preventDefault();
Elements.userInput.value = '';
Elements.userInput.focus();
}
// 键盘处理
function handleKeyDown(e) {
if (e.ctrlKey && e.key === 'Enter') {
e.preventDefault();
executeCommand();
}
}
// 保存设置
function saveSettings(e) {
if (e) e.preventDefault();
try {
// 获取设置值
AppState.apiKey = Elements.configApiKey.value.trim();
AppState.apiBase = Elements.configApiBase.value.trim() || 'https://api.deepseek.com/v1';
AppState.modelName = Elements.configModelName.value.trim() || 'deepseek-chat';
AppState.enableDragging = Elements.enableDragging.checked;
AppState.autoClearInput = Elements.autoClearInput.checked;
// 保存到本地存储
localStorage.setItem('ouroboros_api_key', AppState.apiKey);
localStorage.setItem('ouroboros_api_base', AppState.apiBase);
localStorage.setItem('ouroboros_model_name', AppState.modelName);
localStorage.setItem('ouroboros_enable_dragging', AppState.enableDragging);
localStorage.setItem('ouroboros_auto_clear', AppState.autoClearInput);
// 重新初始化拖拽
setupWindowDragging();
// 显示成功消息
addLogMessage('系统', '设置保存成功');
// 显示保存成功提示
const originalText = Elements.configSaveBtn.textContent;
Elements.configSaveBtn.textContent = '✓ 已保存';
Elements.configSaveBtn.classList.add('bg-green-600');
setTimeout(() => {
Elements.configSaveBtn.textContent = originalText;
Elements.configSaveBtn.classList.remove('bg-green-600');
}, 1500);
// 延迟关闭配置面板
setTimeout(() => {
hideConfigPanel();
}, 1000);
} catch (error) {
console.error('保存设置失败:', error);
addLogMessage('系统', '设置保存失败: ' + error.message);
}
}
// 设置窗口拖拽
function setupWindowDragging() {
if (typeof interact === 'undefined') {
console.warn('interact.js 未加载,跳过拖拽初始化');
return;
}
try {
if (!AppState.enableDragging) {
if (Elements.core.__draggable) {
interact(Elements.core).unset();
Elements.core.__draggable = false;
}
return;
}
// 设置拖拽
interact(Elements.core)
.draggable({
allowFrom: '.window-header',
inertia: true,
modifiers: [
interact.modifiers.restrict({
restriction: 'parent',
endOnly: true,
elementRect: { top: 0, left: 0, bottom: 1, right: 1 }
})
],
listeners: {
start: function(event) {
Elements.core.style.transition = 'none';
},
move: function(event) {
const x = (parseFloat(Elements.core.getAttribute('data-x')) || 0) + event.dx;
const y = (parseFloat(Elements.core.getAttribute('data-y')) || 0) + event.dy;
Elements.core.style.transform = `translate(${x}px, ${y}px) translateX(-50%)`;
Elements.core.setAttribute('data-x', x);
Elements.core.setAttribute('data-y', y);
},
end: function(event) {
Elements.core.style.transition = 'transform 0.2s ease';
}
}
});
Elements.core.__draggable = true;
} catch (error) {
console.error('拖拽初始化失败:', error);
}
}
// 执行命令
async function executeCommand(e) {
if (e) e.preventDefault();
if (AppState.isProcessing) {
addLogMessage('系统', '正在处理上一个请求,请稍候...');
return;
}
if (!AppState.apiKey) {
addLogMessage('系统', '请先配置 DeepSeek API 密钥');
showConfigPanel();
return;
}
const userCommand = Elements.userInput.value.trim();
if (!userCommand) {
addLogMessage('系统', '请输入指令');
return;
}
setLoading(true);
addLogMessage('用户', userCommand);
try {
// 准备 API 配置
const apiConfig = {
apiKey: AppState.apiKey,
dangerouslyAllowBrowser: true,
baseURL: AppState.apiBase
};
const openai = new OpenAI(apiConfig);
// 获取系统提示
const systemPrompt = document.getElementById('system-prompt').textContent;
// 获取当前页面 HTML
const currentHTML = document.documentElement.outerHTML;
// 构建消息
const messages = [
{
role: 'system',
content: systemPrompt
},
{
role: 'user',
content: currentHTML
}
];
addLogMessage('系统', '正在调用 AI 接口...');
// 调用 API
const response = await openai.chat.completions.create({
messages: messages,
model: AppState.modelName,
temperature: 0.7
});
const aiResponse = response.choices[0].message.content;
addLogMessage('系统', '收到 AI 响应');
// 提取并执行 JavaScript 代码
const codeMatch = aiResponse.match(/```(?:javascript)?\s*([\s\S]*?)\s*```/);
if (codeMatch && codeMatch[1]) {
const code = codeMatch[1].trim();
executeCode(code);
addLogMessage('系统', '代码执行成功');
if (AppState.autoClearInput) {
Elements.userInput.value = '';
}
} else {
addLogMessage('系统', '未找到有效的 JavaScript 代码块');
}
} catch (error) {
console.error('执行错误:', error);
addLogMessage('系统', `错误: ${error.message || 'API 调用失败'}`);
if (error.message && error.message.includes('401')) {
addLogMessage('系统', 'API 密钥无效,请检查设置');
} else if (error.message && error.message.includes('429')) {
addLogMessage('系统', 'API 调用频率过高,请稍后再试');
}
} finally {
setLoading(false);
}
}
// 执行代码
function executeCode(code) {
try {
const script = document.createElement('script');
script.textContent = `(() => {
'use strict';
try {
${code}
} catch(error) {
console.error('代码执行错误:', error);
alert('代码执行错误: ' + error.message);
}
})();`;
document.body.appendChild(script);
document.body.removeChild(script);
} catch (e) {
console.error('代码注入错误:', e);
addLogMessage('系统', '代码注入错误: ' + e.message);
}
}
// 导出应用程序
function exportApplication(e) {
if (e) e.preventDefault();
try {
const htmlContent = '<!DOCTYPE html>\n' + document.documentElement.outerHTML;
const blob = new Blob([htmlContent], { type: 'text/html;charset=utf-8' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `ouroboros_mobile_${Date.now()}.html`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
addLogMessage('系统', '应用程序已导出为 HTML 文件');
} catch (error) {
console.error('导出失败:', error);
addLogMessage('系统', '导出失败: ' + error.message);
}
}
// 添加日志消息
function addLogMessage(sender, message) {
const time = new Date().toLocaleTimeString();
const senderClass = sender === '用户' ? 'text-blue-600 font-semibold' : 'text-green-600 font-semibold';
const logEntry = document.createElement('div');
logEntry.className = 'mb-2 leading-relaxed';
logEntry.innerHTML = `
<span class="text-xs text-gray-500">[${time}]</span>
<span class="${senderClass} ml-1">${sender}:</span>
<span class="ml-1 text-gray-800">${escapeHtml(message)}</span>
`;
Elements.activityLog.appendChild(logEntry);
Elements.activityLog.scrollTop = Elements.activityLog.scrollHeight;
}
// HTML 转义
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// 更新令牌计数
function updateTokenCount() {
try {
const htmlLength = document.documentElement.outerHTML.length;
const estimatedTokens = Math.ceil(htmlLength / 4);
const percentage = Math.min((estimatedTokens / 128000) * 100, 100).toFixed(1);
Elements.tokenCount.textContent = `${estimatedTokens.toLocaleString()}/128k (${percentage}%)`;
// 颜色提示
if (percentage > 90) {
Elements.tokenCount.classList.add('text-red-600');
} else if (percentage > 70) {
Elements.tokenCount.classList.add('text-yellow-600');
} else {
Elements.tokenCount.classList.remove('text-red-600', 'text-yellow-600');
}
} catch (error) {
console.error('更新令牌计数失败:', error);
}
// 每10秒更新一次
setTimeout(updateTokenCount, 10000);
}
// 设置加载状态
function setLoading(loading) {
AppState.isProcessing = loading;
Elements.btnText.classList.toggle('invisible', loading);
Elements.spinner.classList.toggle('hidden', !loading);
Elements.executeBtn.disabled = loading;
if (loading) {
Elements.executeBtn.classList.add('opacity-75');
} else {
Elements.executeBtn.classList.remove('opacity-75');
}
}
// 立即初始化
initializeApp();
</script>

