<!DOCTYPE html>
<html>
<head>
<style>
/* ================= 全局样式 ================= */
@import url("https://fonts.cdnfonts.com/css/satoshi");
* {
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
scroll-behavior: smooth;
}
html, body {
min-height: 100%;
}
body {
margin: 0;
font-family: "Satoshi", sans-serif, monospace;
color: #eee;
font-size: 18px;
line-height: 161.8%;
background: #212431;
interpolate-size: allow-keywords;
}
/* 滚动条样式 */
::-webkit-scrollbar {
width: 14px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: #ea5c1f;
border: 4px solid #212431;
cursor: pointer;
}
::-webkit-scrollbar-thumb:hover {
background: #ea5c1f77;
}
/* ================= 文章容器 ================= */
article {
max-width: 618px;
padding: 8px 24px;
margin: auto;
margin-top: 100px;
margin-bottom: 100px;
font-weight: 100;
position: relative;
overflow: hidden;
border-bottom: 4px solid #d6d7d711;
border-left: 4px solid #d6d7d711;
background: linear-gradient(90deg, #46506433 0%, #4f5d7533);
border-top-right-radius: 64px;
}
/* ================= 顶部区域 ================= */
article h1 {
position: absolute;
margin: 0;
top: 0;
padding: 16px 22px 16px 0px;
background: #292f3e;
border-bottom-right-radius: 24px;
z-index: 2;
line-height: 100%;
color: #ea5c1f;
font-size: 1.8em;
}
article .date {
position: absolute;
margin: 0;
bottom: -28px;
padding: 0px 24px 8px 0px;
border-bottom-right-radius: 22px;
z-index: 2;
color: #fff;
font-size: 16px;
font-weight: 400;
background: #292f3e;
}
/* 特色图片区域 - 修复图标部分 */
.featured-image {
width: 100%;
height: 324px;
background: url("https://picsum.photos/600/400?grayscale");
background-size: cover;
background-position: 50% 16.18%;
margin-top: 16px;
border-top-right-radius: 42px;
border-bottom: 4px solid #ea5c1f;
position: relative;
z-index: 1;
}
.featured-image .expand {
position: absolute;
right: 0px;
bottom: -4px;
color: #fff;
cursor: pointer;
background: #292f3e;
border-top-left-radius: 32px;
width: 64px;
height: 64px;
border-left: 4px solid #ea5c1f;
border-top: 4px solid #ea5c1f;
}
.featured-image .expand::before {
content: "+";
display: flex;
position: absolute;
width: 100%;
height: 100%;
align-items: center;
justify-content: center;
font-size: 2em;
font-weight: bold;
color: #ea5c1f;
transition: all 0.4s ease-in-out;
}
.featured-image .expand.close::before {
content: "-";
}
.featured-image .expand:hover::before {
transform: rotate(90deg);
}
.featured-image .expand.close:hover::before {
transform: rotate(0deg);
}
/* ================= 中部内容区域 ================= */
article .content {
transition: height 1s ease-out;
visibility: visible;
height: 0;
overflow: clip;
}
article:has(.expand.close) .content {
transition: height 1s ease-in;
height: auto;
}
article aside {
border-left: 4px solid #ea5c1f;
padding-left: 16px;
margin-bottom: 16px;
line-height: 124%;
}
article strong {
font-weight: 600;
}
article a {
font-weight: 500;
color: #ea5c1f;
text-decoration: none;
cursor: pointer;
}
article a:hover {
text-decoration: underline;
}
.dotmap {
width: 100%;
height: min(30vw, 200px);
background: linear-gradient(#fff0, #fff4, #fff0);
mask-image: url("https://assets.codepen.io/3421562/dotmap.svg");
position: relative;
}
.dotmap::before {
scale: 0;
opacity: 0;
transition: all 0.2s ease-out;
content: "";
display: block;
position: absolute;
width: 100px;
height: 100px;
border-radius: 200px;
background: #ea5c1f;
filter: blur(40px);
top: var(--y);
left: var(--x);
translate: -50% -50%;
}
.dotmap:hover::before {
scale: 1;
opacity: 1;
}
.dotmap::after {
content: "";
display: block;
position: absolute;
width: 100%;
height: 100%;
background: url("https://assets.codepen.io/3421562/dotmap_h.svg");
top: 0;
left: 0;
}
.planet {
width: 100%;
height: min(30vw, 216px);
overflow: hidden;
position: relative;
background: url("https://assets.codepen.io/3421562/stars.svg");
background-size: contain;
background-blend-mode: color-burn;
box-shadow: 4px 6px 0 0 #212431, 0 8px 16px 0 #0000;
}
.planet::after {
display: block;
content: "";
width: min(60vw, 500px);
height: min(60vw, 500px);
box-shadow: -2px 0 12px 1px #ea5c1f44, inset 42px -142px 64px 1px #ea5c1faa,
inset -2px 2px 6px 1px #ea5c1f22, inset -8px 22px 16px 1px #ea5c1f22,
inset -42px 42px 42px 1px #212431, inset 0 0 100px 1px #fff3;
border-radius: 100%;
position: absolute;
right: -34%;
top: 6%;
background: #41322c;
}
.planet span {
position: absolute;
width: min(110vw, 800px);
height: min(110vw, 800px);
right: -46%;
top: -100%;
border-radius: 100%;
transform-style: preserve-3d;
transform: rotateX(75deg) rotateY(16deg) rotate3d(0, 0, 1, 0deg);
animation: pRev 20s linear infinite;
border: 2px solid #ea5c1f44;
}
@keyframes pRev {
to {
transform: rotateX(75deg) rotateY(16deg) rotate3d(0, 0, 1, 360deg);
}
}
.skyline {
width: 100%;
height: 200px;
overflow: hidden;
position: relative;
background: url("https://assets.codepen.io/3421562/skyline.svg");
background-size: max(100%, 500px);
background-position: 50% 12px;
background-repeat: no-repeat;
background-blend-mode: color-burn;
box-shadow: 0px 6px 0 0 #212431, 0 8px 16px 0 #0000;
}
/* ================= 底部区域 ================= */
article .foot {
display: flex;
gap: 24px;
align-items: center;
flex-wrap: wrap;
padding-bottom: 16px;
position: relative;
}
article .foot img {
max-width: 84px;
height: auto;
border: 4px solid #f5f5f5;
border-top-color: #ea5c1f;
border-right-color: #ea5c1f;
object-fit: contain;
cursor: pointer;
transition: all 0.3s ease;
}
article .foot img:hover {
transform: scale(1.05);
border-color: #ea5c1f;
}
article .author a {
font-weight: 600;
}
article .author span {
font-size: 1.4em;
padding-right: 2px;
translate: 0 2px;
rotate: -33deg;
display: inline-block;
color: #eec7b7;
transition: all 0.2s ease-in-out;
}
article .author a:hover,
article .author a:hover span {
color: #ea5c1f;
rotate: 0deg;
}
/* ================= AI对话框样式 ================= */
.ai-chat-container {
width: 100%;
max-height: 0;
overflow: hidden;
transition: max-height 0.5s ease;
background: linear-gradient(135deg, #292f3e 0%, #3a4052 100%);
border-left: 4px solid #ea5c1f;
border-right: 4px solid #ea5c1f;
border-bottom: 4px solid #ea5c1f;
border-bottom-left-radius: 24px;
border-bottom-right-radius: 24px;
margin-top: 10px;
position: relative;
display: flex;
flex-direction: column;
}
.ai-chat-container.show {
max-height: 500px;
}
.ai-chat-messages {
flex: 1;
padding: 20px;
overflow-y: auto;
display: flex;
flex-direction: column-reverse; /* 消息倒序排列 */
gap: 16px;
background: linear-gradient(180deg, #212431 0%, #2a2d3a 100%);
max-height: 300px;
}
.ai-chat-messages::-webkit-scrollbar {
width: 8px;
}
.ai-chat-messages::-webkit-scrollbar-track {
background: transparent;
}
.ai-chat-messages::-webkit-scrollbar-thumb {
background: #ea5c1f;
border-radius: 4px;
}
.message {
display: flex;
gap: 12px;
animation: messageSlide 0.3s ease;
}
@keyframes messageSlide {
from {
transform: translateY(20px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
.message.user {
flex-direction: row-reverse;
}
.message-avatar {
width: 36px;
height: 36px;
border-radius: 50%;
background: #ea5c1f;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-weight: 600;
font-size: 14px;
flex-shrink: 0;
}
.message.user .message-avatar {
background: #4a90e2;
}
.message-content {
max-width: 70%;
padding: 12px 16px;
border-radius: 18px;
background: #3a4052;
color: #eee;
line-height: 1.4;
font-size: 16px;
}
.message.user .message-content {
background: #4a90e2;
color: #fff;
}
.ai-chat-input-container {
padding: 20px;
background: #292f3e;
border: 4px solid #ea5c1f;
border-radius: 24px;
display: none;
margin-top: 10px;
width: 100%;
}
.ai-chat-input-wrapper {
display: flex;
gap: 12px;
align-items: flex-end;
}
.ai-chat-input {
flex: 1;
background: #212431;
border: 2px solid #4a4a4a;
border-radius: 20px;
padding: 12px 16px;
color: #eee;
font-size: 16px;
font-family: inherit;
resize: none;
min-height: 44px;
max-height: 120px;
transition: border-color 0.2s ease;
}
.ai-chat-input:focus {
outline: none;
border-color: #ea5c1f;
}
.ai-chat-input::placeholder {
color: #888;
}
.ai-chat-send {
background: #ea5c1f;
border: none;
border-radius: 50%;
width: 44px;
height: 44px;
color: #fff;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
transition: all 0.2s ease;
flex-shrink: 0;
}
.ai-chat-send:hover {
background: #d64a0f;
transform: scale(1.05);
}
.ai-chat-send:disabled {
background: #666;
cursor: not-allowed;
transform: none;
}
.typing-indicator {
display: flex;
align-items: center;
gap: 8px;
color: #888;
font-style: italic;
}
.typing-dots {
display: flex;
gap: 4px;
}
.typing-dot {
width: 6px;
height: 6px;
background: #ea5c1f;
border-radius: 50%;
animation: typingBounce 1.4s infinite;
}
.typing-dot:nth-child(2) {
animation-delay: 0.2s;
}
.typing-dot:nth-child(3) {
animation-delay: 0.4s;
}
@keyframes typingBounce {
0%, 60%, 100% {
transform: translateY(0);
}
30% {
transform: translateY(-10px);
}
}
/* 工具容器样式 */
.tool-container {
background: var(--editor-secondary-bg);
border-radius: var(--border-radius);
margin: 15px 0;
border: var(--terminal-border);
overflow: hidden;
backdrop-filter: blur(5px);
width: 100%;
max-width: 100%;
}
.tool-header {
background: var(--header-bg);
padding: 12px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: var(--terminal-border);
}
.tool-title {
color: var(--text-color);
font-size: 14px;
font-weight: 600;
display: flex;
align-items: center;
gap: 8px;
}
.tool-close {
background: none;
border: none;
color: var(--line-color);
cursor: pointer;
padding: 4px;
border-radius: 4px;
}
.tool-close:hover {
color: var(--text-color);
background: RGBA(var(--accentRGB), 0.1);
}
.tool-content {
padding: 20px;
width: 100%;
box-sizing: border-box;
}
.tool-input {
width: 100%;
padding: 12px;
border: var(--terminal-border);
border-radius: 8px;
background: var(--editor-secondary-bg);
color: var(--text-color);
font-size: 14px;
outline: none;
box-sizing: border-box;
font-family: var(--fontBody);
}
.tool-input:focus {
border-color: var(--accent);
box-shadow: 0 0 0 3px RGBA(var(--supportRGB), 0.2);
}
.tool-btn {
background: #ea5c1f;
color: white;
border: none;
padding: 8px 16px;
border-radius: 6px;
cursor: pointer;
font-size: 12px;
display: flex;
align-items: center;
gap: 6px;
font-weight: 500;
margin: 5px;
}
.tool-btn:hover {
background: #d64a0f;
transform: translateY(-1px);
}
.tool-actions {
display: flex;
gap: 10px;
flex-wrap: wrap;
margin-top: 15px;
}
.tool-textarea {
width: 100%;
min-height: 200px;
padding: 12px;
border: var(--terminal-border);
border-radius: 8px;
background: var(--editor-bg);
color: var(--text-color);
font-family: var(--fontBody);
font-size: 14px;
line-height: 1.5;
resize: vertical;
outline: none;
white-space: pre;
overflow-x: auto;
}
.tool-info {
background: RGBA(var(--supportRGB), 0.1);
border: 1px solid RGBA(var(--supportRGB), 0.3);
border-radius: 6px;
padding: 12px;
margin: 10px 0;
color: var(--text-color);
font-size: 13px;
}
/* 视频播放器样式 */
.video-container {
background: #000;
border-radius: var(--border-radius);
margin: 15px 0;
overflow: hidden;
border: var(--terminal-border);
width: 100%;
}
.video-player {
position: relative;
width: 100%;
height: 300px;
background: #000;
}
.video-player video, .video-player iframe {
width: 100%;
height: 100%;
object-fit: contain;
border: none;
}
.video-controls {
background: var(--editor-secondary-bg);
padding: 10px;
display: flex;
gap: 10px;
align-items: center;
flex-wrap: wrap;
}
.video-button {
background: #ea5c1f;
color: white;
border: none;
padding: 6px 12px;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
display: flex;
align-items: center;
gap: 6px;
}
.video-button:hover {
background: #d64a0f;
}
/* 图片预览样式 */
.tool-preview-container {
position: relative;
width: 100%;
min-height: 200px;
margin-bottom: 15px;
border-radius: 8px;
overflow: hidden;
background: RGBA(var(--accentRGB), 0.05);
border: var(--terminal-border);
display: flex;
justify-content: center;
align-items: center;
}
.tool-preview {
max-width: 100%;
max-height: 400px;
display: none;
}
.tool-preview.active {
display: block;
}
.tool-placeholder {
text-align: center;
padding: 20px;
color: var(--line-color);
}
.tool-placeholder svg {
width: 48px;
height: 48px;
margin-bottom: 10px;
color: #ea5c1f;
}
/* 响应式设计 */
@media (max-width: 768px) {
.message-content {
max-width: 85%;
font-size: 14px;
}
}
</style>
</head>
<body>
<!-- ================= 文章整体结构 ================= -->
<article>
<!-- 顶部区域:标题和日期 -->
<h1>
<span id="ai-title">文字之墙:博客随笔</span>
<div class="date" id="current-date">2024年11月19日</div>
</h1>
<!-- 特色图片区域 -->
<div class="featured-image">
<div class="expand" onclick="this.classList.toggle('close')"></div>
</div>
<!-- 中部:主要内容区域 -->
<div class="content">
<p id="ai-content-1"></p>
<p id="ai-content-2"></p>
<!-- AI内容生成脚本 -->
<script>
function fillWithAIContent(prompt, elementId) {
const randomValue = Math.random().toString(36).substring(2, 8);
const uniquePrompt = `${prompt} unique-${randomValue}`;
fetch(`https://text.pollinations.ai/${encodeURIComponent(uniquePrompt)}`)
.then(response => response.text())
.then(data => {
const words = data.split(" ");
const wordCount = Math.min(words.length, 2);
const randomIndexes = Array.from({
length: wordCount
}, () =>
Math.floor(Math.random() * words.length)
);
randomIndexes.forEach(index => {
words[index] = `<strong>${words[index]}</strong>`;
});
document.getElementById(elementId).innerHTML = words.join(" ");
})
.catch(error => {
console.error("获取AI内容时出错:", error);
});
}
// 生成AI内容
fillWithAIContent("写一个不超过四个字的博客标题,关于日常生活中正念的好处", "ai-title");
fillWithAIContent("写一段关于日常生活中正念好处的简短介绍段落", "ai-content-1");
fillWithAIContent("写一段关于日常生活中正念好处的简短叙述段落", "ai-content-2");
</script>
<p>从前有个智者说,生活就像一本书,需要我们细细品读。当下即是永恒,每一个瞬间都值得珍惜。
<aside>
<p><strong>活在当下</strong>。正念让我们能够真正体验生活的每一个细节,不被过去困扰,不为未来焦虑。</p>
</aside>
当我们专注于呼吸,专注于此刻,心灵便得到了净化。正念不是逃避,而是更深地融入生活,感受每一刻的真实。</p>
<!-- 点阵图效果 -->
<div class="dotmap"></div>
<script>
function hoverFollow(target) {
target.addEventListener('mousemove', (event) => {
const rect = target.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
target.style.setProperty('--x', `${x}px`);
target.style.setProperty('--y', `${y}px`);
});
}
hoverFollow(document.querySelector('.dotmap'));
</script>
<h2>文字的力量</h2>
<p>文字是人类最伟大的发明之一,它承载着思想,传递着情感。<strong>用心书写</strong>的文字能够穿越时空,触动心灵。每一篇文章都是作者心血的结晶,每一个故事都值得被认真对待。</p>
<!-- 星球动画效果 -->
<div class="planet">
<span></span>
<span></span>
<span></span>
</div>
<h2>如何让文字更有魅力</h2>
<p>好的文字需要用心雕琢,就像匠人打磨艺术品一样。首先,要有清晰的思路;其次,选择恰当的词语;最重要的是,要倾注真情实感。</p>
<!-- 天际线效果 -->
<div class="skyline"></div>
<p>在这个信息爆炸的时代,静下心来写作变得越来越珍贵。文字不仅是表达的工具,更是思考的载体。当我们放慢脚步,用心书写,文字自然会散发出独特的光芒。</p>
<p>写作是一种修行,每一篇文章都是作者与世界对话的方式。无论长短,无论题材,真诚的文字总能找到它的读者。</p>
<p>让我们珍惜文字的力量,用它记录生活,表达思想,连接心灵。在这个快速变化的世界里,文字是我们永恒的伴侣。</p>
</div>
<!-- 底部区域:作者信息 -->
<!-- 在代码二的<body>标签内,替换原有的AI对话框部分为以下内容 -->
<!-- 底部区域:作者信息 -->
<div class="foot">
<img src="https://assets.codepen.io/3421562/internal/avatars/users/default.png"
alt="作者头像"
class="profile-card"
onclick="toggleAIChat()">
<div class="author">作者:<br><a href="#" target="_blank"><span>@</span>TENG YUAN</a></div>
<!-- 输入框在头像下方 -->
<div class="ai-chat-input-container" id="aiInputContainer">
<div class="ai-chat-input-wrapper">
<textarea
class="ai-chat-input"
id="chatInput"
placeholder="输入命令 (输入 'help' 查看可用命令)..."
rows="1"></textarea>
<button class="ai-chat-send" id="sendButton" onclick="sendMessage()">
➤
</button>
</div>
</div>
<!-- AI对话框在输入框下方 -->
<div class="ai-chat-container" id="aiChatContainer">
<div class="ai-chat-messages" id="chatMessages">
<div class="message">
<div class="message-avatar">AI</div>
<div class="message-content">
(。・ω・。)ノ♡ 欢迎来到藤原的数字空间<br>
✨ 这里是一个融合创意与技术的终端世界<br><br>
💡 你可以:<br>
- 输入 'help' 查看所有命令<br>
- 直接粘贴视频/图片链接进行解析<br>
- 使用快捷键提高效率<br><br>
⏰ 当前时间: <span id="current-time"></span><br><br>
请开始你的探索之旅吧~
</div>
</div>
</div>
</div>
</div>
<!-- 隐藏的文件输入元素 -->
<input type="file" id="fileInput" accept="image/*" style="display: none;">
<script>
// ================= 全局配置 =================
const API_CONFIG = {
id: "10003788",
key: "ffa8afb46dce4916c5a74fd73c8de9f6",
endpoints: {
ai: "https://cn.apihz.cn/api/ai/wxtiny.php",
ping: "https://cn.apihz.cn/api/wangzhan/ping.php",
shortVideo: "http://124.220.49.230/api/fun/douyin.php",
},
};
// 支持的视频平台列表
const supportedVideoPlatforms = [
"v.qq.com", // 腾讯视频
"iqiyi.com", // 爱奇艺
"youku.com", // 优酷
"mgtv.com", // 芒果TV
"bilibili.com", // 哔哩哔哩
"sohu.com", // 搜狐视频
"le.com", // 乐视视频
"pptv.com", // PPTV
"wasu.cn", // 华数TV
"1905.com", // 电影网
"fun.tv", // 风行网
"acfun.cn", // AcFun
"douyu.com", // 斗鱼直播
"huya.com", // 虎牙直播
"yy.com" // YY直播
];
// 影视解析接口
const videoApis = [
{ name: "默认线路【推荐】", url: "https://www.yemu.xyz/?url=" },
{ name: "线路一【推荐】", url: "https://jx.we-vip.com/?url=" },
{ name: "线路二【推荐】", url: "https://z1.m1907.top/?jx=" },
{ name: "线路三", url: "https://www.ckplayer.vip/jiexi/?url=" },
{ name: "线路五", url: "https://jx.jsonplayer.com/player/?url=" },
{ name: "线路六", url: "https://jx.4kdv.com/?url=" },
{ name: "线路七", url: "https://api.jiexi.la/?url=" },
{ name: "线路八", url: "https://jx.qqwtt.com/?url=" },
{ name: "线路九", url: "https://www.playm3u8.cn/jiexi.php?url=" },
{ name: "线路十", url: "https://jx.xmflv.com/?url=" }
];
// 优化后的帮助信息
const helpText = `
可用命令:
基础命令:
- help: 显示帮助信息
- clear: 清空终端
- history: 显示命令历史记录
代码工具:
- format [code]: 格式化代码 (支持: sql, json, html, css, js)
例如: format {"name":"John","age":30}
- minify [code]: 压缩代码 (支持: sql, json, html, css, js)
例如: minify <div><p>Hello</p></div>
- detect [code]: 自动检测代码类型
例如: detect SELECT * FROM users
设置:
- set indent [size]: 设置缩进大小 (1-8个空格)
例如: set indent 4
影视工具:
- video [url]: 解析影视视频(支持爱奇艺、腾讯视频等)
例如: video https://v.qq.com/x/cover/xxx.html
- shortvideo [url]: 解析短视频(支持抖音、快手、小红书)
例如: shortvideo https://v.douyin.com/xxxxxx
网络工具:
- ping [url]: 测试网站或IP的ping延迟
例如: ping www.example.com
AI工具:
- ai [message]: 与AI助手对话
例如: ai 你好
图片工具:
- image [format]: 转换图片格式 (支持: jpg, png, webp, bmp)
例如: image png
其他工具:
- stats: 显示系统统计信息
`;
// ================= 全局变量 =================
const commandHistoryArray = JSON.parse(localStorage.getItem("commandHistory") || "[]");
let historyIndex = -1;
let totalParseCount = parseInt(localStorage.getItem("totalParseCount") || "0");
let successParseCount = parseInt(localStorage.getItem("successParseCount") || "0");
let indentSize = 4; // 默认缩进4个空格
let isTyping = false;
let chatVisible = false;
// ================= 初始化函数 =================
function initialize() {
setCurrentDate();
setupInputEvents();
setupGlobalKeyboardShortcuts();
document.getElementById('aiInputContainer').style.display = 'none';
// 设置当前时间
document.getElementById('current-time').textContent = new Date().toLocaleString();
// 防止页面刷新时丢失焦点
window.addEventListener("beforeunload", () => {
localStorage.setItem("commandHistory", JSON.stringify(commandHistoryArray));
localStorage.setItem("totalParseCount", totalParseCount.toString());
localStorage.setItem("successParseCount", successParseCount.toString());
});
}
// ================= 基础功能 =================
function setCurrentDate() {
const now = new Date();
const year = now.getFullYear();
const month = now.getMonth() + 1;
const day = now.getDate();
document.getElementById('current-date').textContent = `${year}年${month}月${day}日`;
}
function toggleAIChat() {
const chatContainer = document.getElementById('aiChatContainer');
const inputContainer = document.getElementById('aiInputContainer');
chatVisible = !chatVisible;
if (chatVisible) {
chatContainer.classList.add('show');
inputContainer.style.display = 'block';
document.getElementById('chatInput').focus();
setTimeout(() => {
document.getElementById('chatMessages').scrollTop = 0;
}, 100);
} else {
chatContainer.classList.remove('show');
inputContainer.style.display = 'none';
}
}
function adjustTextareaHeight(textarea) {
textarea.style.height = 'auto';
textarea.style.height = Math.min(textarea.scrollHeight, 120) + 'px';
}
// ================= 消息处理 =================
function addMessage(content, sender, prepend = false) {
const messagesContainer = document.getElementById('chatMessages');
const messageDiv = document.createElement('div');
messageDiv.className = `message ${sender}`;
const avatar = document.createElement('div');
avatar.className = 'message-avatar';
avatar.textContent = sender === 'user' ? 'U' : 'AI';
const messageContent = document.createElement('div');
messageContent.className = 'message-content';
messageContent.innerHTML = content;
messageDiv.appendChild(avatar);
messageDiv.appendChild(messageContent);
if (prepend) {
messagesContainer.insertBefore(messageDiv, messagesContainer.firstChild);
} else {
messagesContainer.appendChild(messageDiv);
}
messagesContainer.scrollTop = 0;
return messageDiv;
}
function showTypingIndicator() {
isTyping = true;
const typingDiv = document.createElement('div');
typingDiv.className = 'message';
typingDiv.id = 'typingIndicator';
typingDiv.innerHTML = `
<div class="message-avatar">AI</div>
<div class="message-content">
<div class="typing-indicator">
<span>正在输入</span>
<div class="typing-dots">
<div class="typing-dot"></div>
<div class="typing-dot"></div>
<div class="typing-dot"></div>
</div>
</div>
</div>
`;
document.getElementById('chatMessages').insertBefore(typingDiv, document.getElementById('chatMessages').firstChild);
document.getElementById('chatMessages').scrollTop = 0;
return typingDiv;
}
function hideTypingIndicator() {
const typingIndicator = document.getElementById('typingIndicator');
if (typingIndicator) {
typingIndicator.remove();
}
isTyping = false;
}
// ================= 工具函数 =================
// URL提取函数
function extractURL(text) {
try {
const urlRegex = /(https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&\/=]*))/g;
const matches = text.match(urlRegex);
return matches ? matches[0] : null;
} catch (e) {
console.error('URL提取错误:', e);
return null;
}
}
// 检查URL是否属于支持的视频平台
function isSupportedVideoUrl(url) {
if (!url) return false;
try {
const domain = new URL(url).hostname.toLowerCase();
return supportedVideoPlatforms.some(platform => domain.includes(platform));
} catch (e) {
return false;
}
}
// 检测代码类型函数增强版
function detectCodeType(code) {
code = code.trim();
// 尝试解析为JSON
try {
JSON.parse(code);
return 'json';
} catch (e) {}
// 检查HTML
if (code.startsWith('<') || code.match(/<[a-z][\s\S]*>/i)) {
return 'html';
}
// 检查CSS
if (code.match(/(^|\}|\{)[^{}]*\{[^{}]*\}/) ||
code.match(/\.([a-z][\w-]*)\s*\{|\#([a-z][\w-]*)\s*\{|@(media|keyframes|import)/i)) {
return 'css';
}
// 检查SQL
if (code.match(/\b(SELECT|INSERT|UPDATE|DELETE|CREATE|ALTER|DROP|FROM|WHERE|JOIN)\b/i)) {
return 'sql';
}
// 检查JS
if (code.match(/function\s*\(|=>|\b(let|const|var)\s+\w+\s*=|console\.log|import\s+|export\s+/)) {
return 'js';
}
return 'text';
}
// 显示提示信息
function showAlert(message) {
const alert = document.createElement('div');
alert.style.cssText = `
position: fixed;
bottom: 30px;
left: 50%;
transform: translateX(-50%);
background: rgba(30, 41, 59, 0.95);
color: white;
padding: 12px 24px;
border-radius: 25px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
z-index: 9999;
font-size: 14px;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
`;
alert.textContent = message;
document.body.appendChild(alert);
setTimeout(() => alert.remove(), 3000);
}
// 保存命令历史
function saveToHistory(command) {
if (commandHistoryArray.length === 0 || commandHistoryArray[commandHistoryArray.length - 1] !== command) {
commandHistoryArray.push(command);
if (commandHistoryArray.length > 20) {
commandHistoryArray.shift();
}
localStorage.setItem("commandHistory", JSON.stringify(commandHistoryArray));
}
historyIndex = -1;
}
// 显示命令历史
function showCommandHistory() {
if (commandHistoryArray.length === 0) {
addMessage('暂无命令历史记录', 'ai', true);
return;
}
let historyText = '命令历史记录:<br>';
commandHistoryArray.slice().reverse().forEach((cmd, index) => {
historyText += `${commandHistoryArray.length - index}. ${cmd}<br>`;
});
addMessage(historyText, 'ai', true);
}
// 显示系统统计
function showStats() {
let statsText = '系统统计信息:<br>';
statsText += `总解析次数: ${totalParseCount}<br>`;
statsText += `成功解析次数: ${successParseCount}<br>`;
const rate = totalParseCount > 0 ? Math.round((successParseCount / totalParseCount) * 100) : 0;
statsText += `成功率: ${rate}%<br>`;
statsText += `命令历史记录: ${commandHistoryArray.length} 条<br>`;
statsText += `当前时间: ${new Date().toLocaleString()}<br>`;
statsText += `浏览器: ${navigator.userAgent.split(' ')[0]}`;
addMessage(statsText, 'ai', true);
}
// ================= 代码格式化功能 =================
// 代码格式化工具函数
function formatCode(code, type) {
try {
// 移除首尾空白
code = code.trim();
// 根据不同类型进行格式化
switch(type.toLowerCase()) {
case 'json':
try {
const parsed = JSON.parse(code);
return JSON.stringify(parsed, null, ' '.repeat(indentSize));
} catch (e) {
throw new Error(`JSON解析错误: ${e.message}`);
}
case 'html':
// 简单HTML格式化
let html = code;
let formatted = '';
let indent = 0;
let inTag = false;
let inAttribute = false;
let currentLine = '';
// 添加换行和缩进
for (let i = 0; i < html.length; i++) {
const char = html[i];
const nextChar = html[i+1];
if (char === '<' && nextChar === '/') {
// 结束标签
indent = Math.max(0, indent - 1);
if (currentLine.trim()) {
formatted += ' '.repeat(indent * indentSize) + currentLine + '\n';
currentLine = '';
}
currentLine += char;
} else if (char === '<') {
// 开始标签
if (currentLine.trim()) {
formatted += ' '.repeat(indent * indentSize) + currentLine + '\n';
currentLine = '';
}
currentLine += char;
if (nextChar !== '!' && nextChar !== '?') {
indent++;
}
} else if (char === '>') {
// 标签结束
currentLine += char;
formatted += ' '.repeat(Math.max(0, indent - 1) * indentSize) + currentLine + '\n';
currentLine = '';
} else {
currentLine += char;
}
}
return formatted.trim();
case 'js':
// 简单JS格式化
let js = code;
let jsIndent = 0;
let jsResult = '';
let inString = false;
let stringChar = '';
// 处理基础缩进
js = js.replace(/([{}\[\]])/g, '\n$1\n')
.replace(/([;,])\s*/g, '$1\n')
.replace(/\n+/g, '\n');
const lines = js.split('\n');
for (const line of lines) {
const trimmed = line.trim();
if (!trimmed) continue;
// 处理字符串中的内容
if (inString) {
jsResult += trimmed;
if (trimmed.endsWith(stringChar)) {
inString = false;
stringChar = '';
}
continue;
}
if (trimmed.startsWith('"') || trimmed.startsWith("'")) {
inString = true;
stringChar = trimmed[0];
}
if (trimmed.endsWith('}') || trimmed.endsWith(']')) {
jsIndent--;
}
jsResult += ' '.repeat(jsIndent * indentSize) + trimmed + '\n';
if (trimmed.endsWith('{') || trimmed.endsWith('[')) {
jsIndent++;
}
}
return jsResult.trim();
default:
return code;
}
} catch (e) {
return `格式化错误: ${e.message}`;
}
}
// 代码压缩工具函数
function minifyCode(code, type) {
try {
code = code.trim();
switch(type.toLowerCase()) {
case 'json':
try {
const parsed = JSON.parse(code);
return JSON.stringify(parsed);
} catch (e) {
throw new Error(`JSON解析错误: ${e.message}`);
}
case 'html':
// 简单HTML压缩
return code.replace(/\s+/g, ' ')
.replace(/<!--[\s\S]*?-->/g, '')
.replace(/>\s+</g, '><')
.trim();
case 'css':
// CSS压缩
return code.replace(/\/\*[\s\S]*?\*\//g, '') // 移除注释
.replace(/\s+/g, ' ') // 压缩空格
.replace(/\s*([{};:,])\s*/g, '$1') // 移除符号周围的空格
.replace(/;}/g, '}') // 移除最后一个分号
.trim();
case 'js':
// 简单JS压缩
return code.replace(/\/\/.*|\/\*[\s\S]*?\*\//g, '') // 移除注释
.replace(/\s+/g, ' ') // 压缩空格
.replace(/\s*([=+\-*\/%&|^~!<>?:;,{}()[\]])\s*/g, '$1') // 移除操作符周围的空格
.replace(/;}/g, '}') // 移除最后一个分号
.trim();
default:
return code;
}
} catch (e) {
return `压缩错误: ${e.message}`;
}
}
// 创建代码格式化器容器
function createCodeFormatter(initialCode = '', initialType = 'auto', operation = 'format') {
const formatter = document.createElement('div');
formatter.className = 'tool-container';
// 头部
const header = document.createElement('div');
header.className = 'tool-header';
const title = document.createElement('div');
title.className = 'tool-title';
title.innerHTML = `
<i class="ri-code-line"></i>
${operation === 'format' ? '代码格式化工具' : '代码压缩工具'}
`;
const closeBtn = document.createElement('button');
closeBtn.className = 'tool-close';
closeBtn.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>';
closeBtn.addEventListener('click', () => formatter.remove());
header.appendChild(title);
header.appendChild(closeBtn);
// 内容区域
const content = document.createElement('div');
content.className = 'tool-content';
// 代码类型选择器
const typeSelector = document.createElement('select');
typeSelector.className = 'tool-input';
typeSelector.style.marginBottom = '10px';
typeSelector.innerHTML = `
<option value="auto" ${initialType === 'auto' ? 'selected' : ''}>自动检测</option>
<option value="json" ${initialType === 'json' ? 'selected' : ''}>JSON</option>
<option value="html" ${initialType === 'html' ? 'selected' : ''}>HTML</option>
<option value="css" ${initialType === 'css' ? 'selected' : ''}>CSS</option>
<option value="js" ${initialType === 'js' ? 'selected' : ''}>JavaScript</option>
<option value="sql" ${initialType === 'sql' ? 'selected' : ''}>SQL</option>
`;
// 创建双面板布局
const panelContainer = document.createElement('div');
panelContainer.style.cssText = `
display: flex;
gap: 15px;
margin-bottom: 15px;
flex-direction: column;
`;
// 输入面板
const inputPanel = document.createElement('div');
inputPanel.style.cssText = `
flex: 1;
display: flex;
flex-direction: column;
`;
const inputLabel = document.createElement('div');
inputLabel.style.cssText = `
font-size: 12px;
color: #eee;
margin-bottom: 5px;
`;
inputLabel.textContent = '输入代码';
const inputTextarea = document.createElement('textarea');
inputTextarea.className = 'tool-textarea';
inputTextarea.style.cssText = `
flex: 1;
min-height: 100px;
`;
inputTextarea.value = initialCode;
inputPanel.appendChild(inputLabel);
inputPanel.appendChild(inputTextarea);
// 输出面板
const outputPanel = document.createElement('div');
outputPanel.style.cssText = `
flex: 1;
display: flex;
flex-direction: column;
`;
const outputLabel = document.createElement('div');
outputLabel.style.cssText = `
font-size: 12px;
color: #eee;
margin-bottom: 5px;
`;
outputLabel.textContent = '输出结果';
const outputTextarea = document.createElement('textarea');
outputTextarea.className = 'tool-textarea';
outputTextarea.style.cssText = `
flex: 1;
min-height: 100px;
background: rgba(234, 92, 31, 0.05);
`;
outputTextarea.readOnly = true;
outputPanel.appendChild(outputLabel);
outputPanel.appendChild(outputTextarea);
panelContainer.appendChild(inputPanel);
panelContainer.appendChild(outputPanel);
// 操作按钮
const actions = document.createElement('div');
actions.className = 'tool-actions';
const processBtn = document.createElement('button');
processBtn.className = 'tool-btn';
processBtn.innerHTML = `
<i class="ri-${operation === 'format' ? 'indent-increase' : 'indent-decrease'}"></i>
${operation === 'format' ? '格式化代码' : '压缩代码'}
`;
const copyBtn = document.createElement('button');
copyBtn.className = 'tool-btn';
copyBtn.innerHTML = `
<i class="ri-clipboard-line"></i>
复制结果
`;
const detectBtn = document.createElement('button');
detectBtn.className = 'tool-btn';
detectBtn.innerHTML = `
<i class="ri-search-line"></i>
检测类型
`;
// 处理按钮点击事件
processBtn.addEventListener('click', () => {
const code = inputTextarea.value;
const type = typeSelector.value === 'auto' ? detectCodeType(code) : typeSelector.value;
try {
const processed = operation === 'format'
? formatCode(code, type)
: minifyCode(code, type);
outputTextarea.value = processed;
showAlert(`${operation === 'format' ? '格式化' : '压缩'}成功 (类型: ${type})`);
} catch (error) {
showAlert(`${operation === 'format' ? '格式化' : '压缩'}失败: ${error.message}`);
}
});
// 复制按钮点击事件
copyBtn.addEventListener('click', () => {
outputTextarea.select();
document.execCommand('copy');
showAlert('代码已复制到剪贴板');
});
// 检测按钮点击事件
detectBtn.addEventListener('click', () => {
const code = inputTextarea.value;
const type = detectCodeType(code);
// 显示检测结果
showAlert(`检测到的代码类型: ${type}`);
// 自动选择检测到的类型
if (type !== 'text' && typeSelector.querySelector(`option[value="${type}"]`)) {
typeSelector.value = type;
}
});
actions.appendChild(typeSelector);
actions.appendChild(processBtn);
actions.appendChild(copyBtn);
actions.appendChild(detectBtn);
content.appendChild(panelContainer);
content.appendChild(actions);
formatter.appendChild(header);
formatter.appendChild(content);
// 添加到聊天区域
document.getElementById('chatMessages').insertBefore(formatter, document.getElementById('chatMessages').firstChild);
document.getElementById('chatMessages').scrollTop = 0;
// 自动聚焦到输入文本区域
inputTextarea.focus();
// 如果初始代码不为空,自动处理
if (initialCode.trim()) {
processBtn.click();
}
}
// ================= 影视解析功能 =================
function parseVideo(url) {
// 检查URL是否属于支持的视频平台
if (!isSupportedVideoUrl(url)) {
addMessage('不支持该视频平台的解析,目前支持以下平台:<br>腾讯视频、爱奇艺、优酷、芒果TV、哔哩哔哩等', 'ai', true);
return;
}
// 增加解析次数统计
totalParseCount++;
localStorage.setItem('totalParseCount', totalParseCount.toString());
// 显示加载状态
const thinkingLine = showTypingIndicator();
// 创建视频播放器容器
const videoContainer = document.createElement('div');
videoContainer.className = 'video-container';
// 创建视频元素
const videoPlayer = document.createElement('div');
videoPlayer.className = 'video-player';
// 使用iframe来播放解析后的视频
const videoElement = document.createElement('iframe');
videoElement.id = 'terminal-video';
// 使用默认解析线路
const defaultApi = videoApis[0];
videoElement.src = defaultApi.url + encodeURIComponent(url);
videoElement.frameBorder = '0';
videoElement.allowFullscreen = true;
videoElement.style.width = '100%';
videoElement.style.height = '100%';
videoPlayer.appendChild(videoElement);
// 视频控制区域
const videoControls = document.createElement('div');
videoControls.className = 'video-controls';
// 当前线路显示
const currentLine = document.createElement('span');
currentLine.style.cssText = 'color: #eee; font-size: 12px; margin-right: 10px;';
currentLine.textContent = `当前线路: ${defaultApi.name}`;
// 刷新按钮
const refreshButton = document.createElement('button');
refreshButton.className = 'video-button';
refreshButton.innerHTML = '<i class="ri-refresh-line"></i> 刷新';
refreshButton.addEventListener('click', () => {
videoElement.src = videoElement.src;
});
// 线路切换按钮
const switchButton = document.createElement('button');
switchButton.className = 'video-button';
switchButton.innerHTML = '<i class="ri-exchange-line"></i> 切换线路';
switchButton.addEventListener('click', () => {
showVideoApiSelector(url);
});
// 全屏按钮
const fullscreenButton = document.createElement('button');
fullscreenButton.className = 'video-button';
fullscreenButton.innerHTML = '<i class="ri-fullscreen-line"></i> 全屏';
fullscreenButton.addEventListener('click', () => {
if (videoElement.requestFullscreen) {
videoElement.requestFullscreen();
}
});
// 关闭按钮
const closeButton = document.createElement('button');
closeButton.className = 'video-button';
closeButton.innerHTML = '<i class="ri-close-line"></i> 关闭';
closeButton.addEventListener('click', () => videoContainer.remove());
videoControls.appendChild(currentLine);
videoControls.appendChild(refreshButton);
videoControls.appendChild(switchButton);
videoControls.appendChild(fullscreenButton);
videoControls.appendChild(closeButton);
// 组装整个播放器
videoContainer.appendChild(videoPlayer);
videoContainer.appendChild(videoControls);
// 添加到聊天区域
document.getElementById('chatMessages').insertBefore(videoContainer, document.getElementById('chatMessages').firstChild);
document.getElementById('chatMessages').scrollTop = 0;
// 更新加载状态
hideTypingIndicator();
addMessage('视频解析完成!', 'ai', true);
// 增加成功统计
successParseCount++;
localStorage.setItem('successParseCount', successParseCount.toString());
}
// 显示线路选择器
function showVideoApiSelector(originalUrl) {
addMessage('可用解析线路:', 'ai', true);
// 创建线路选择容器
const selectorContainer = document.createElement('div');
selectorContainer.className = 'tool-container';
// 头部
const header = document.createElement('div');
header.className = 'tool-header';
const title = document.createElement('div');
title.className = 'tool-title';
title.innerHTML = '<i class="ri-list-settings-line"></i> 选择解析线路';
const closeBtn = document.createElement('button');
closeBtn.className = 'tool-close';
closeBtn.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>';
closeBtn.addEventListener('click', () => selectorContainer.remove());
header.appendChild(title);
header.appendChild(closeBtn);
// 内容区域
const content = document.createElement('div');
content.className = 'tool-content';
content.style.padding = '15px';
// 添加线路选项
videoApis.forEach((api, index) => {
const apiBtn = document.createElement('button');
apiBtn.className = 'tool-btn';
apiBtn.style.width = '100%';
apiBtn.style.marginBottom = '10px';
apiBtn.style.textAlign = 'left';
apiBtn.innerHTML = `<i class="ri-play-line"></i> ${index + 1}. ${api.name}`;
apiBtn.addEventListener('click', () => {
switchVideoApi(originalUrl, index);
selectorContainer.remove();
});
content.appendChild(apiBtn);
});
selectorContainer.appendChild(header);
selectorContainer.appendChild(content);
// 添加到聊天区域
document.getElementById('chatMessages').insertBefore(selectorContainer, document.getElementById('chatMessages').firstChild);
document.getElementById('chatMessages').scrollTop = 0;
}
// 切换视频解析线路
function switchVideoApi(originalUrl, apiIndex) {
const api = videoApis[apiIndex];
const apiUrl = api.url + encodeURIComponent(originalUrl);
// 更新iframe的src
const videoElement = document.getElementById('terminal-video');
if (videoElement) {
videoElement.src = apiUrl;
// 更新当前线路显示
const currentLine = document.querySelector('.video-controls span');
if (currentLine) {
currentLine.textContent = `当前线路: ${api.name}`;
}
addMessage(`已切换到 ${api.name}`, 'ai', true);
}
}
// ================= 短视频解析功能 =================
async function parseShortVideo(url) {
// 增加解析次数统计
totalParseCount++;
localStorage.setItem('totalParseCount', totalParseCount.toString());
// 显示加载状态
const thinkingLine = showTypingIndicator();
// 检查URL是否有效
if (!url || !url.startsWith('http')) {
hideTypingIndicator();
addMessage('请输入有效的短视频URL', 'ai', true);
return;
}
try {
// 构建API请求URL
const params = new URLSearchParams({
id: API_CONFIG.id,
key: API_CONFIG.key,
url: url.replace(/&/g, '<') // 替换&为<,根据API要求
});
const apiUrl = `${API_CONFIG.endpoints.shortVideo}?${params.toString()}`;
// 发送请求
const response = await fetch(apiUrl);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
if (data.code !== 200) {
throw new Error(data.msg || '解析失败');
}
// 创建结果容器
const resultContainer = document.createElement('div');
resultContainer.className = 'tool-container';
// 头部
const header = document.createElement('div');
header.className = 'tool-header';
const title = document.createElement('div');
title.className = 'tool-title';
title.innerHTML = `
<i class="ri-video-line"></i>
短视频解析结果
`;
const closeBtn = document.createElement('button');
closeBtn.className = 'tool-close';
closeBtn.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>';
closeBtn.addEventListener('click', () => resultContainer.remove());
header.appendChild(title);
header.appendChild(closeBtn);
// 内容区域
const content = document.createElement('div');
content.className = 'tool-content';
// 作者信息区域
const authorInfo = `
<div style="display: flex; align-items: center; margin-bottom: 15px; padding: 12px; background: rgba(234, 92, 31, 0.1); border-radius: 8px;">
<img src="${data.head?.[0] || 'https://picsum.photos/100/100'}"
style="width: 40px; height: 40px; border-radius: 50%; margin-right: 12px; object-fit: cover;">
<div style="flex: 1; overflow: hidden;">
<div style="font-weight: 600; color: #eee; margin-bottom: 2px; white-space: nowrap; text-overflow: ellipsis; overflow: hidden;">
${data.name || '未知作者'}
</div>
<div style="font-size: 12px; color: #aaa; white-space: nowrap; text-overflow: ellipsis; overflow: hidden;">
${data.signature || '暂无签名'}
</div>
</div>
</div>
`;
let mediaContent = '';
if (data.type === "视频") {
// 视频内容
mediaContent = `
<div style="position: relative; padding-top: 56.25%; margin-bottom: 15px; border-radius: 8px; overflow: hidden; background: #000;">
<video controls
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"
poster="${data.cover || ''}"
crossorigin="anonymous">
<source src="${data.video || data.yvideo}" type="video/mp4">
您的浏览器不支持视频播放
</video>
</div>
<div class="tool-actions">
<button class="tool-btn" onclick="downloadVideo('${data.video || data.yvideo}', '${data.name || '短视频'}_${Date.now()}')">
<i class="ri-download-line"></i>
下载视频
</button>
<button class="tool-btn" onclick="showFullImage('${data.cover || ''}')">
<i class="ri-image-line"></i>
查看封面
</button>
</div>
`;
} else if (data.type === "图集" && data.images && data.images.length > 0) {
// 图集内容
const galleryHTML = data.images.map((img, index) => `
<div style="position: relative; margin-bottom: 10px;">
<img src="${img}"
alt="图片 ${index + 1}"
style="width: 100%; border-radius: 8px; cursor: pointer;"
onclick="showFullImage('${img}')">
<div style="position: absolute; top: 8px; right: 8px; background: rgba(0,0,0,0.7); color: white; padding: 4px 8px; border-radius: 12px; font-size: 12px;">
${index + 1}/${data.images.length}
</div>
</div>
`).join('');
mediaContent = `
<div style="max-height: 400px; overflow-y: auto; margin-bottom: 15px;">
${galleryHTML}
</div>
<div class="tool-actions">
<button class="tool-btn" onclick="downloadAllImages(${JSON.stringify(data.images).replace(/"/g, '"')}, '${data.name || '图集'}_${Date.now()}')">
<i class="ri-download-line"></i>
打包下载 (${data.images.length}张)
</button>
</div>
`;
} else {
throw new Error('无法识别的媒体类型');
}
// 标题和描述
const titleInfo = `
<div style="margin-bottom: 15px; padding: 12px; background: rgba(186, 132, 130, 0.1); border-radius: 8px;">
<div style="font-weight: 600; color: #eee; margin-bottom: 5px;">
${data.title || '无标题'}
</div>
<div style="font-size: 12px; color: #aaa;">
发布于 ${data.create_time ? new Date(data.create_time * 1000).toLocaleString() : '未知时间'}
</div>
</div>
`;
// 音乐信息
let musicInfo = '';
if (data.musictitle) {
musicInfo = `
<div style="margin-top: 15px; padding: 12px; background: rgba(166, 176, 180, 0.1); border-radius: 8px;">
<h4 style="color: #eee; margin-bottom: 8px; display: flex; align-items: center; gap: 8px;">
<i class="ri-music-2-line"></i>
背景音乐
</h4>
<div style="font-size: 14px; color: #aaa; margin-bottom: 5px;">
${data.musictitle}
</div>
<div style="font-size: 12px; color: #aaa;">
${data.musicauthor || '未知作者'}
</div>
</div>
`;
}
content.innerHTML = authorInfo + titleInfo + mediaContent + musicInfo;
resultContainer.appendChild(header);
resultContainer.appendChild(content);
// 添加到聊天区域
document.getElementById('chatMessages').insertBefore(resultContainer, document.getElementById('chatMessages').firstChild);
document.getElementById('chatMessages').scrollTop = 0;
// 更新加载状态
hideTypingIndicator();
addMessage('短视频解析完成!', 'ai', true);
// 增加成功统计
successParseCount++;
localStorage.setItem('successParseCount', successParseCount.toString());
} catch (error) {
console.error('短视频解析失败:', error);
hideTypingIndicator();
addMessage(`短视频解析失败 - ${error.message}`, 'ai', true);
}
}
// ================= 图片转换功能 =================
function createImageConverter(targetFormat) {
const converter = document.createElement('div');
converter.className = 'tool-container';
// 头部
const header = document.createElement('div');
header.className = 'tool-header';
const title = document.createElement('div');
title.className = 'tool-title';
title.innerHTML = `
<i class="ri-image-line"></i>
图片格式转换器
`;
const closeBtn = document.createElement('button');
closeBtn.className = 'tool-close';
closeBtn.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>';
closeBtn.addEventListener('click', () => converter.remove());
header.appendChild(title);
header.appendChild(closeBtn);
// 内容区域
const content = document.createElement('div');
content.className = 'tool-content';
// 预览区域
const previewContainer = document.createElement('div');
previewContainer.className = 'tool-preview-container';
const previewImage = document.createElement('img');
previewImage.className = 'tool-preview';
const placeholder = document.createElement('div');
placeholder.className = 'tool-placeholder';
placeholder.innerHTML = `
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
<circle cx="8.5" cy="8.5" r="1.5"></circle>
<polyline points="21 15 16 10 5 21"></polyline>
</svg>
<div>请选择要转换的图片</div>
<div style="font-size:0.8em;margin-top:8px;opacity:0.7;">支持 JPG, PNG, WEBP, BMP 格式</div>
`;
previewContainer.appendChild(previewImage);
previewContainer.appendChild(placeholder);
// 尺寸设置区域
const sizeControls = document.createElement('div');
sizeControls.style.cssText = `
display: flex;
gap: 10px;
margin: 15px 0;
align-items: center;
flex-wrap: wrap;
`;
// 宽度输入
const widthControl = document.createElement('div');
widthControl.style.cssText = 'flex: 1; min-width: 100px;';
widthControl.innerHTML = `
<label style="display: block; margin-bottom: 5px; font-size: 12px; color: #eee;">宽度 (px)</label>
<input type="number" id="imageWidth" class="tool-input" placeholder="自动" min="1" style="width: 100%;">
`;
// 高度输入
const heightControl = document.createElement('div');
heightControl.style.cssText = 'flex: 1; min-width: 100px;';
heightControl.innerHTML = `
<label style="display: block; margin-bottom: 5px; font-size: 12px; color: #eee;">高度 (px)</label>
<input type="number" id="imageHeight" class="tool-input" placeholder="自动" min="1" style="width: 100%;">
`;
// 保持宽高比复选框
const ratioControl = document.createElement('div');
ratioControl.style.cssText = 'flex: 1; min-width: 120px; display: flex; align-items: center;';
ratioControl.innerHTML = `
<input type="checkbox" id="keepAspectRatio" checked style="margin-right: 8px;">
<label for="keepAspectRatio" style="font-size: 12px; color: #eee;">保持宽高比</label>
`;
sizeControls.appendChild(widthControl);
sizeControls.appendChild(heightControl);
sizeControls.appendChild(ratioControl);
// 格式信息
const formatInfo = document.createElement('div');
formatInfo.innerHTML = `
<div class="tool-info">
<strong>目标格式:</strong>${targetFormat.toUpperCase()}<br>
<strong>支持格式:</strong>JPG, PNG, WEBP, BMP 等主流图片格式
</div>
`;
// 操作按钮
const actions = document.createElement('div');
actions.className = 'tool-actions';
const selectBtn = document.createElement('button');
selectBtn.className = 'tool-btn';
selectBtn.innerHTML = `
<i class="ri-folder-open-line"></i>
选择图片
`;
const convertBtn = document.createElement('button');
convertBtn.className = 'tool-btn';
convertBtn.innerHTML = `
<i class="ri-refresh-line"></i>
转换为 ${targetFormat.toUpperCase()}
`;
convertBtn.addEventListener('click', () => {
if (!previewImage.src) {
addMessage('请先选择要转换的图片', 'ai', true);
return;
}
// 获取尺寸设置
const width = document.getElementById('imageWidth').value;
const height = document.getElementById('imageHeight').value;
const keepRatio = document.getElementById('keepAspectRatio').checked;
convertImage(previewImage, targetFormat, {
width: width ? parseInt(width) : null,
height: height ? parseInt(height) : null,
keepAspectRatio: keepRatio
});
});
actions.appendChild(selectBtn);
actions.appendChild(convertBtn);
content.appendChild(previewContainer);
content.appendChild(sizeControls);
content.appendChild(formatInfo);
content.appendChild(actions);
converter.appendChild(header);
converter.appendChild(content);
// 添加到聊天区域
document.getElementById('chatMessages').insertBefore(converter, document.getElementById('chatMessages').firstChild);
document.getElementById('chatMessages').scrollTop = 0;
// 创建临时文件输入元素
const tempFileInput = document.createElement('input');
tempFileInput.type = 'file';
tempFileInput.accept = 'image/*';
tempFileInput.style.display = 'none';
// 点击选择按钮时触发文件选择
selectBtn.addEventListener('click', () => {
tempFileInput.click();
});
// 文件选择处理
tempFileInput.addEventListener('change', (e) => {
const file = e.target.files[0];
if (!file) return;
if (!file.type.match('image.*')) {
addMessage('请选择有效的图片文件', 'ai', true);
return;
}
const reader = new FileReader();
reader.onload = (event) => {
previewImage.src = event.target.result;
previewImage.classList.add('active');
placeholder.style.display = 'none';
// 图片加载完成后设置原始尺寸
previewImage.onload = function() {
const widthInput = document.getElementById('imageWidth');
const heightInput = document.getElementById('imageHeight');
widthInput.value = this.naturalWidth;
heightInput.value = this.naturalHeight;
// 计算原始宽高比
const originalRatio = this.naturalWidth / this.naturalHeight;
// 宽度变化时自动计算高度(如果保持宽高比)
widthInput.addEventListener('input', function() {
if (document.getElementById('keepAspectRatio').checked && originalRatio) {
heightInput.value = Math.round(this.value / originalRatio);
}
});
// 高度变化时自动计算宽度(如果保持宽高比)
heightInput.addEventListener('input', function() {
if (document.getElementById('keepAspectRatio').checked && originalRatio) {
widthInput.value = Math.round(this.value * originalRatio);
}
});
};
};
reader.readAsDataURL(file);
});
}
// 图片转换函数
function convertImage(imgElement, format, options = {}) {
try {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// 计算目标尺寸
let targetWidth = options.width || imgElement.naturalWidth;
let targetHeight = options.height || imgElement.naturalHeight;
// 如果保持宽高比且只指定了一个尺寸
if (options.keepAspectRatio) {
const originalRatio = imgElement.naturalWidth / imgElement.naturalHeight;
if (options.width && !options.height) {
targetHeight = Math.round(options.width / originalRatio);
} else if (!options.width && options.height) {
targetWidth = Math.round(options.height * originalRatio);
}
}
// 设置画布尺寸
canvas.width = targetWidth;
canvas.height = targetHeight;
// 绘制图片(带缩放)
ctx.drawImage(imgElement, 0, 0, targetWidth, targetHeight);
// 确定MIME类型
let mimeType;
let quality = 0.9;
switch (format.toLowerCase()) {
case 'jpg':
case 'jpeg':
mimeType = 'image/jpeg';
break;
case 'png':
mimeType = 'image/png';
break;
case 'webp':
mimeType = 'image/webp';
break;
case 'bmp':
mimeType = 'image/png';
addMessage('注意:BMP格式将转换为PNG格式', 'ai', true);
break;
default:
mimeType = 'image/jpeg';
}
// 转换并下载
canvas.toBlob((blob) => {
if (!blob) {
addMessage('图片转换失败,请重试', 'ai', true);
return;
}
const url = URL.createObjectURL(blob);
const downloadLink = document.createElement('a');
downloadLink.href = url;
downloadLink.download = `converted-image-${Date.now()}.${format}`;
// 触发下载
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
// 清理URL对象
setTimeout(() => URL.revokeObjectURL(url), 1000);
addMessage(`图片已成功转换为 ${format.toUpperCase()} 格式并开始下载!<br>文件大小: ${(blob.size / 1024).toFixed(1)}KB<br>尺寸: ${targetWidth}x${targetHeight}px`, 'ai', true);
}, mimeType, quality);
} catch (error) {
addMessage(`图片转换失败: ${error.message}`, 'ai', true);
}
}
// ================= 网络工具 =================
async function pingWebsite(host) {
// 显示加载状态
const thinkingLine = showTypingIndicator();
try {
// 构建API请求URL
const params = new URLSearchParams({
id: API_CONFIG.id,
key: API_CONFIG.key,
host: host
});
const apiUrl = `${API_CONFIG.endpoints.ping}?${params.toString()}`;
// 发送请求
const response = await fetch(apiUrl);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
if (data.code === 200) {
// 成功响应
hideTypingIndicator();
// 构建结果文本
const resultText = `Ping测试结果:<br>
目标地址: ${data.host}<br>
实际地址: ${data.realhost}<br>
IP地址: ${data.ip}<br>
延迟: ${data.time}毫秒<br>
节点位置: ${data.dy}(${data.dz})`;
addMessage(resultText, 'ai', true);
} else {
throw new Error(data.msg || 'Ping测试失败');
}
} catch (error) {
console.error('Ping测试失败:', error);
hideTypingIndicator();
addMessage(`Ping测试失败 - ${error.message}`, 'ai', true);
}
}
// ================= 聊天功能 =================
function sendChatMessage() {
const input = document.getElementById('chatInput');
const message = input.value.trim();
if (!message || isTyping) return;
addMessage(message, 'user', true);
input.value = '';
adjustTextareaHeight(input);
processCommand(message);
}
async function sendAIMessage(message) {
const thinkingLine = showTypingIndicator();
try {
const endpoint = `${API_CONFIG.endpoints.ai}?id=${API_CONFIG.id}&key=${API_CONFIG.key}&words=${encodeURIComponent(message)}`;
const response = await fetch(endpoint);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
if (data.code === 200 && data.msg) {
hideTypingIndicator();
addMessage(data.msg, 'ai', true);
} else {
hideTypingIndicator();
addMessage(`AI回复失败: ${data.msg || "未知错误"}`, 'ai', true);
}
} catch (error) {
hideTypingIndicator();
addMessage(`AI服务暂时不可用: ${error.message}`, 'ai', true);
console.error("AI API错误:", error);
}
}
// ================= 命令处理 =================
async function processCommand(input) {
const command = input.trim().toLowerCase();
const parts = input.trim().split(" ");
const cmd = parts[0].toLowerCase();
const args = parts.slice(1).join(" ");
saveToHistory(input.trim());
switch (cmd) {
case "help":
addMessage(helpText, 'ai', true);
break;
case "clear":
const messagesContainer = document.getElementById('chatMessages');
while (messagesContainer.children.length > 1) {
messagesContainer.removeChild(messagesContainer.lastChild);
}
addMessage("终端已清空", 'ai', true);
break;
case "history":
showCommandHistory();
break;
case "format":
if (args) {
const code = args;
const type = detectCodeType(code);
createCodeFormatter(code, type, 'format');
} else {
addMessage('请提供要格式化的代码,例如: format {"name":"John"}', 'ai', true);
}
break;
case "minify":
if (args) {
const code = args;
const type = detectCodeType(code);
createCodeFormatter(code, type, 'minify');
} else {
addMessage('请提供要压缩的代码,例如: minify <div><p>Hello</p></div>', 'ai', true);
}
break;
case "detect":
if (args) {
const code = args;
const type = detectCodeType(code);
addMessage(`检测到的代码类型: ${type}`, 'ai', true);
} else {
addMessage('请提供要检测的代码,例如: detect SELECT * FROM users', 'ai', true);
}
break;
case "set":
if (parts[1] === "indent" && parts[2]) {
const size = parseInt(parts[2]);
if (size >= 1 && size <= 8) {
indentSize = size;
addMessage(`缩进大小已设置为 ${size} 个空格`, 'ai', true);
} else {
addMessage('缩进大小必须在1-8之间', 'ai', true);
}
} else {
addMessage('无效的设置命令,使用: set indent [1-8]', 'ai', true);
}
break;
case "video":
if (args) {
const url = extractURL(args) || args;
parseVideo(url);
} else {
addMessage('请提供视频URL,例如: video https://v.qq.com/x/cover/xxx.html<br>支持的平台: 爱奇艺、腾讯视频、优酷、芒果TV、哔哩哔哩等', 'ai', true);
}
break;
case "shortvideo":
if (args) {
const url = extractURL(args) || args;
parseShortVideo(url);
} else {
addMessage('请提供短视频URL,例如: shortvideo https://v.douyin.com/xxx<br>支持的平台: 抖音、快手、小红书等', 'ai', true);
}
break;
case "ai":
if (args) {
await sendAIMessage(args);
} else {
addMessage('请输入要对话的内容,例如: ai 你好', 'ai', true);
}
break;
case "image":
const format = args || 'jpg';
const supportedFormats = ['jpg', 'jpeg', 'png', 'webp', 'bmp'];
if (supportedFormats.includes(format.toLowerCase())) {
createImageConverter(format.toLowerCase());
addMessage(`图片转换器已启动,目标格式: ${format.toUpperCase()}`, 'ai', true);
} else {
addMessage(`不支持的格式: ${format}<br>支持的格式: ${supportedFormats.join(', ')}`, 'ai', true);
}
break;
case "stats":
showStats();
break;
case "ping":
if (args) {
await pingWebsite(args);
} else {
addMessage('请提供要ping的网站或IP地址,例如: ping www.example.com', 'ai', true);
}
break;
default:
// 检查是否包含URL,如果是则尝试解析
const detectedUrl = extractURL(input);
if (detectedUrl) {
if (detectedUrl.includes('douyin.com') || detectedUrl.includes('kuaishou.com') || detectedUrl.includes('xiaohongshu.com')) {
parseShortVideo(detectedUrl);
} else {
parseVideo(detectedUrl);
}
} else {
await sendAIMessage(input);
}
break;
}
}
// ================= 工具函数 =================
function showFullImage(url) {
const overlay = document.createElement('div');
overlay.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.9);
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
cursor: zoom-out;
`;
const img = document.createElement('img');
img.src = url;
img.style.maxWidth = '90%';
img.style.maxHeight = '90%';
img.style.borderRadius = '10px';
overlay.onclick = () => overlay.remove();
overlay.appendChild(img);
document.body.appendChild(overlay);
}
// 下载所有图片
async function downloadAllImages(images, title) {
if (!window.JSZip) {
showAlert('打包下载功能需要JSZip库支持');
return;
}
try {
const zip = new JSZip();
const imgFolder = zip.folder("images");
showAlert('正在打包下载,请稍候...');
const downloadPromises = images.map(async (imgUrl, index) => {
try {
const response = await fetch(imgUrl);
if (!response.ok) throw new Error(`图片${index+1}下载失败`);
const blob = await response.blob();
imgFolder.file(`image_${index + 1}.jpg`, blob);
} catch (error) {
console.warn(`图片${index+1}下载失败:`, error);
}
});
await Promise.all(downloadPromises);
const zipBlob = await zip.generateAsync({
type: "blob",
compression: "DEFLATE",
compressionOptions: { level: 6 }
});
const cleanTitle = title.replace(/[<>:"/\\|?*]/g, '');
saveAs(zipBlob, `${cleanTitle}.zip`);
showAlert('打包下载完成!');
} catch (error) {
console.error('打包下载失败:', error);
showAlert('打包下载失败,请重试');
}
}
// 下载视频
async function downloadVideo(url, title) {
try {
showAlert('正在下载视频,请稍候...');
const response = await fetch(url);
if (!response.ok) throw new Error(`下载失败: ${response.status}`);
const blob = await response.blob();
const cleanTitle = title.replace(/[<>:"/\\|?*]/g, '');
saveAs(blob, `${cleanTitle}.mp4`);
showAlert('视频下载完成!');
} catch (error) {
console.error('视频下载失败:', error);
showAlert('视频下载失败,请重试');
}
}
// ================= 事件监听 =================
function setupInputEvents() {
const userInput = document.getElementById('chatInput');
// 输入框高度自适应
userInput.addEventListener('input', function() {
adjustTextareaHeight(this);
});
// 回车发送消息
userInput.addEventListener('keydown', function(e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendChatMessage();
}
});
// 历史命令导航
userInput.addEventListener("keydown", (e) => {
if (e.key === "ArrowUp") {
e.preventDefault();
if (historyIndex < commandHistoryArray.length - 1) {
historyIndex++;
userInput.value = commandHistoryArray[commandHistoryArray.length - 1 - historyIndex];
}
} else if (e.key === "ArrowDown") {
e.preventDefault();
if (historyIndex > 0) {
historyIndex--;
userInput.value = commandHistoryArray[commandHistoryArray.length - 1 - historyIndex];
} else if (historyIndex === 0) {
historyIndex = -1;
userInput.value = "";
}
}
});
}
function setupGlobalKeyboardShortcuts() {
document.addEventListener("keydown", (e) => {
// Ctrl/Cmd + K 清空终端
if ((e.ctrlKey || e.metaKey) && e.key === "k") {
e.preventDefault();
const messagesContainer = document.getElementById('chatMessages');
while (messagesContainer.children.length > 1) {
messagesContainer.removeChild(messagesContainer.lastChild);
}
addMessage("终端已清空 (快捷键: Ctrl/Cmd+K)", 'ai', true);
}
// Ctrl/Cmd + L 聚焦输入框
if ((e.ctrlKey || e.metaKey) && e.key === "l") {
e.preventDefault();
const input = document.getElementById('chatInput');
input.focus();
input.select();
}
// ESC 键清空当前输入
if (e.key === "Escape") {
const input = document.getElementById('chatInput');
input.value = "";
input.focus();
}
// Ctrl/Cmd + / 显示帮助
if ((e.ctrlKey || e.metaKey) && e.key === "/") {
e.preventDefault();
processCommand('help');
}
// Ctrl/Cmd + \ 打开/关闭终端
if ((e.ctrlKey || e.metaKey) && e.key === "\\") {
e.preventDefault();
toggleAIChat();
}
});
}
// ================= 初始化执行 =================
document.addEventListener('DOMContentLoaded', initialize);
// 全局函数
window.toggleAIChat = toggleAIChat;
window.sendMessage = sendChatMessage;
window.showFullImage = showFullImage;
window.downloadVideo = downloadVideo;
window.downloadAllImages = downloadAllImages;
</script>
</body>
</html>
index.html