未命名 ESNeYvedit icon

作者:
藤原
Fork(复制)
下载
嵌入
设置
BUG反馈
index.html
style.css
index.js
现在支持上传本地图片了!
            
            <!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>藤原的个人终端 - 完整版</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;700&family=Inter:wght@300;400;500;600;700&display=swap">
<style>
:root {
  /* 毛玻璃主色调 */
  --glass-bg: rgba(255, 255, 255, 0.08);
  --glass-bg-strong: rgba(255, 255, 255, 0.12);
  --glass-border: rgba(255, 255, 255, 0.18);
  --glass-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
  --glass-shadow-strong: 0 12px 40px rgba(0, 0, 0, 0.2);

  /* 背景渐变 */
  --bg-primary: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  --bg-secondary: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
  --bg-accent: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);

  /* 文字颜色 */
  --text-primary: rgba(255, 255, 255, 0.95);
  --text-secondary: rgba(255, 255, 255, 0.7);
  --text-muted: rgba(255, 255, 255, 0.5);
  --text-accent: #64ffda;
  --text-warning: #ffb74d;
  --text-error: #f48fb1;
  --text-success: #81c784;

  /* 代码高亮 */
  --code-keyword: #bb86fc;
  --code-string: #a5d6ff;
  --code-comment: #6c7b7f;
  --code-number: #ffcc95;
  --code-function: #82aaff;

  /* 交互色彩 */
  --accent-primary: #64ffda;
  --accent-secondary: #bb86fc;
  --accent-hover: rgba(100, 255, 218, 0.2);

  /* 字体 */
  --font-mono: 'JetBrains Mono', 'Consolas', 'Monaco', monospace;
  --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;

  /* 尺寸 */
  --border-radius: 16px;
  --border-radius-small: 8px;
  --spacing-xs: 4px;
  --spacing-sm: 8px;
  --spacing-md: 16px;
  --spacing-lg: 24px;
  --spacing-xl: 32px;
}

[data-theme="light"] {
  --glass-bg: rgba(255, 255, 255, 0.25);
  --glass-bg-strong: rgba(255, 255, 255, 0.35);
  --glass-border: rgba(255, 255, 255, 0.3);
  --glass-shadow: 0 8px 32px rgba(0, 0, 0, 0.08);
  --glass-shadow-strong: 0 12px 40px rgba(0, 0, 0, 0.15);

  --bg-primary: linear-gradient(135deg, #74b9ff 0%, #0984e3 100%);
  --bg-secondary: linear-gradient(135deg, #fd79a8 0%, #e84393 100%);
  --bg-accent: linear-gradient(135deg, #00cec9 0%, #55a3ff 100%);

  --text-primary: rgba(0, 0, 0, 0.9);
  --text-secondary: rgba(0, 0, 0, 0.7);
  --text-muted: rgba(0, 0, 0, 0.5);
  --text-accent: #00b894;
  --text-warning: #e17055;
  --text-error: #d63031;
  --text-success: #00b894;

  --code-keyword: #6c5ce7;
  --code-string: #00b894;
  --code-comment: #636e72;
  --code-number: #e17055;
  --code-function: #0984e3;

  --accent-primary: #00b894;
  --accent-secondary: #6c5ce7;
  --accent-hover: rgba(0, 184, 148, 0.2);
}

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  font-family: var(--font-sans);
  background: var(--bg-primary);
  color: var(--text-primary);
  min-height: 100vh;
  overflow-x: hidden;
  position: relative;
}

/* 动态背景 */
body::before {
  content: '';
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: var(--bg-primary);
  z-index: -2;
}

body::after {
  content: '';
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: 
    radial-gradient(circle at 20% 80%, rgba(120, 119, 198, 0.3) 0%, transparent 50%),
    radial-gradient(circle at 80% 20%, rgba(255, 119, 198, 0.3) 0%, transparent 50%),
    radial-gradient(circle at 40% 40%, rgba(120, 219, 255, 0.2) 0%, transparent 50%);
  z-index: -1;
  animation: backgroundShift 20s ease-in-out infinite;
}

@keyframes backgroundShift {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.8; }
}

/* 主容器 */
.app-container {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  padding: var(--spacing-lg);
  gap: var(--spacing-lg);
}

/* 顶部导航栏 */
.top-nav {
  display: flex;
  justify-content: space-between;
  align-items: center;
  background: var(--glass-bg);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: 1px solid var(--glass-border);
  border-radius: var(--border-radius);
  padding: var(--spacing-md) var(--spacing-lg);
  box-shadow: var(--glass-shadow);
  position: relative;
  overflow: hidden;
}

.top-nav::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--accent-primary), transparent);
  opacity: 0.6;
}

.nav-brand {
  display: flex;
  align-items: center;
  gap: var(--spacing-md);
  font-weight: 600;
  font-size: 1.1em;
}

.nav-brand .logo {
  width: 32px;
  height: 32px;
  background: var(--bg-accent);
  border-radius: var(--border-radius-small);
  display: flex;
  align-items: center;
  justify-content: center;
  color: white;
  font-weight: bold;
  font-size: 0.9em;
}

.nav-controls {
  display: flex;
  align-items: center;
  gap: var(--spacing-md);
}

.theme-toggle {
  background: var(--glass-bg-strong);
  border: 1px solid var(--glass-border);
  border-radius: 50px;
  padding: var(--spacing-sm) var(--spacing-md);
  color: var(--text-primary);
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  font-size: 0.9em;
}

.theme-toggle:hover {
  background: var(--glass-bg-strong);
  transform: translateY(-2px);
  box-shadow: var(--glass-shadow);
}

/* 主要内容区域 */
.main-content {
  display: grid;
  grid-template-columns: 1fr 300px;
  gap: var(--spacing-lg);
  flex: 1;
}

/* 终端区域 */
.terminal-container {
  background: var(--glass-bg);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: 1px solid var(--glass-border);
  border-radius: var(--border-radius);
  box-shadow: var(--glass-shadow-strong);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  position: relative;
}

.terminal-container::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--accent-primary), transparent);
  opacity: 0.4;
}

.terminal-header {
  background: var(--glass-bg-strong);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  padding: var(--spacing-md) var(--spacing-lg);
  border-bottom: 1px solid var(--glass-border);
  display: flex;
  align-items: center;
  gap: var(--spacing-md);
}

.window-controls {
  display: flex;
  gap: var(--spacing-sm);
}

.window-btn {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  cursor: pointer;
  transition: all 0.2s ease;
  position: relative;
}

.window-btn.close { background: #ff5f57; }
.window-btn.minimize { background: #ffbd2e; }
.window-btn.maximize { background: #28ca42; }

.window-btn:hover {
  transform: scale(1.1);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
}

.terminal-title {
  color: var(--text-secondary);
  font-family: var(--font-mono);
  font-size: 0.9em;
  margin-left: var(--spacing-md);
}

.terminal-content {
  flex: 1;
  padding: var(--spacing-lg);
  font-family: var(--font-mono);
  font-size: 14px;
  line-height: 1.6;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--glass-border) transparent;
}

.terminal-content::-webkit-scrollbar {
  width: 6px;
}

.terminal-content::-webkit-scrollbar-track {
  background: transparent;
}

.terminal-content::-webkit-scrollbar-thumb {
  background: var(--glass-border);
  border-radius: 3px;
}

.code-line {
  display: flex;
  margin: 2px 0;
  position: relative;
  min-height: 1.5em;
  align-items: center;
}

.line-number {
  color: var(--text-muted);
  width: 3em;
  text-align: right;
  padding-right: 1em;
  user-select: none;
  font-size: 0.85em;
}

.line-content {
  flex: 1;
}

/* 用户输入样式 - 与个人信息样式一致 */
.code-line.user-input {
  border-left: 3px solid var(--accent-primary);
  padding-left: var(--spacing-md);
  margin: var(--spacing-sm) 0;
}

.code-line.user-input .line-content {
  color: var(--text-primary);
}

.code-line.user-input .user-prompt {
  color: var(--accent-primary);
  font-weight: bold;
  margin-right: var(--spacing-sm);
}

.code-line.user-input .user-command {
  color: var(--code-function);
  font-weight: 500;
}

/* AI回复样式 - 与个人信息样式一致 */
.code-line.ai-response {
  border-left: 3px solid var(--accent-secondary);
  padding-left: var(--spacing-md);
  margin: var(--spacing-sm) 0;
}

.code-line.ai-response .line-content {
  color: var(--text-primary);
}

.code-line.ai-response .ai-prompt {
  color: var(--accent-secondary);
  font-weight: bold;
  margin-right: var(--spacing-sm);
}

.code-line.ai-response .ai-message {
  color: var(--code-string);
}

/* 系统消息样式 */
.code-line.system-message {
  border-left: 3px solid var(--text-warning);
  padding-left: var(--spacing-md);
  margin: var(--spacing-sm) 0;
}

.code-line.system-message .line-content {
  color: var(--text-warning);
  font-style: italic;
}

/* 语法高亮 */
.keyword { color: var(--code-keyword); font-weight: 500; }
.string { color: var(--code-string); }
.comment { color: var(--code-comment); font-style: italic; }
.number { color: var(--code-number); }
.function { color: var(--code-function); }

/* 输入区域 */
.terminal-input {
  background: var(--glass-bg-strong);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-top: 1px solid var(--glass-border);
  padding: var(--spacing-md) var(--spacing-lg);
  display: flex;
  align-items: center;
  gap: var(--spacing-md);
}

.input-prompt {
  color: var(--accent-primary);
  font-family: var(--font-mono);
  font-weight: bold;
}

.input-field {
  flex: 1;
  background: transparent;
  border: none;
  color: var(--text-primary);
  font-family: var(--font-mono);
  font-size: 14px;
  outline: none;
}

.input-field::placeholder {
  color: var(--text-muted);
}

/* 侧边栏 */
.sidebar {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-lg);
}

/* 工具面板 */
.tool-panel {
  background: var(--glass-bg);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: 1px solid var(--glass-border);
  border-radius: var(--border-radius);
  box-shadow: var(--glass-shadow);
  overflow: hidden;
  position: relative;
}

.tool-panel::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--accent-secondary), transparent);
  opacity: 0.4;
}

.tool-header {
  background: var(--glass-bg-strong);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  padding: var(--spacing-md);
  border-bottom: 1px solid var(--glass-border);
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.tool-title {
  font-weight: 600;
  font-size: 0.9em;
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
}

.tool-icon {
  width: 16px;
  height: 16px;
  opacity: 0.8;
}

.tool-content {
  padding: var(--spacing-md);
}

/* 快捷命令面板 */
.quick-commands {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--spacing-sm);
}

.quick-cmd {
  background: var(--glass-bg-strong);
  border: 1px solid var(--glass-border);
  border-radius: var(--border-radius-small);
  padding: var(--spacing-sm);
  cursor: pointer;
  transition: all 0.2s ease;
  text-align: center;
  font-size: 0.8em;
  color: var(--text-secondary);
}

.quick-cmd:hover {
  background: var(--accent-hover);
  color: var(--accent-primary);
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}

/* 状态指示器 */
.status-indicators {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-sm);
}

.status-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--spacing-sm);
  background: var(--glass-bg-strong);
  border-radius: var(--border-radius-small);
  font-size: 0.8em;
}

.status-label {
  color: var(--text-secondary);
}

.status-value {
  color: var(--text-accent);
  font-weight: 500;
}

.status-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--text-success);
  animation: pulse 2s infinite;
}

@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.5; }
}

/* 命令历史 */
.history-list {
  max-height: 200px;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--glass-border) transparent;
}

.history-item {
  padding: var(--spacing-sm);
  cursor: pointer;
  border-radius: var(--border-radius-small);
  font-family: var(--font-mono);
  font-size: 0.8em;
  color: var(--text-secondary);
  transition: all 0.2s ease;
  margin-bottom: 2px;
}

.history-item:hover {
  background: var(--accent-hover);
  color: var(--accent-primary);
  transform: translateX(4px);
}

/* 命令面板 */
.command-palette {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 90%;
  max-width: 600px;
  background: var(--glass-bg);
  border-radius: var(--border-radius);
  box-shadow: var(--glass-shadow-strong);
  z-index: 1000;
  display: none;
  border: 1px solid var(--glass-border);
  max-height: 70vh;
  overflow: hidden;
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
}

.command-palette-header {
  background: var(--glass-bg-strong);
  padding: 8px 12px;
  border-radius: var(--border-radius) var(--border-radius) 0 0;
  display: flex;
  align-items: center;
  gap: 8px;
  border-bottom: 1px solid var(--glass-border);
}

.command-palette .window-btn {
  width: 10px;
  height: 10px;
  margin-right: 6px;
  flex-shrink: 0;
}

.command-palette-title {
  color: var(--text-secondary);
  font-size: 0.8em;
  flex: 1;
}

.command-palette-content {
  padding: 12px;
  max-height: calc(70vh - 40px);
  overflow-y: auto;
}

.command-header {
  display: flex;
  align-items: center;
  margin-bottom: 10px;
  gap: 10px;
  position: sticky;
  top: 0;
  background: var(--glass-bg);
  padding: 5px 0;
  z-index: 10;
}

.command-search {
  flex: 1;
  background: var(--glass-bg-strong);
  border: 1px solid var(--glass-border);
  color: var(--text-primary);
  padding: 8px 12px;
  border-radius: var(--border-radius-small);
  font-family: var(--font-mono);
  transition: all 0.2s;
  backdrop-filter: blur(5px);
  -webkit-backdrop-filter: blur(5px);
}

.command-search:focus {
  border-color: var(--accent-primary);
  outline: none;
  box-shadow: 0 0 0 2px rgba(100, 255, 218, 0.3);
}

.command-list {
  max-height: 50vh;
  overflow-y: auto;
}

.command-item {
  padding: 8px 10px;
  cursor: pointer;
  border-radius: var(--border-radius-small);
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 4px 0;
  transition: all 0.2s;
  backdrop-filter: blur(5px);
  -webkit-backdrop-filter: blur(5px);
}

.command-item:hover {
  background: var(--glass-bg-strong);
}

.command-item.selected {
  background: var(--accent-primary);
  color: white;
}

.command-icon {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
}

.command-content {
  flex: 1;
}

.command-title {
  font-weight: bold;
  font-size: 0.9em;
}

.command-description {
  font-size: 0.8em;
  color: var(--text-secondary);
  margin-top: 2px;
}

.command-item.selected .command-description {
  color: rgba(255, 255, 255, 0.8);
}

.command-shortcut {
  margin-left: auto;
  color: var(--text-secondary);
  font-size: 0.75em;
  background: var(--glass-bg-strong);
  padding: 3px 6px;
  border-radius: 4px;
  white-space: nowrap;
  backdrop-filter: blur(5px);
  -webkit-backdrop-filter: blur(5px);
}

.command-item.selected .command-shortcut {
  background: rgba(255, 255, 255, 0.2);
  color: white;
}

.command-category {
  font-size: 0.75em;
  color: var(--text-secondary);
  padding: 6px 10px;
  margin-top: 8px;
  border-top: 1px solid var(--glass-border);
  display: flex;
  align-items: center;
  gap: 6px;
  position: sticky;
  top: 0;
  background: var(--glass-bg);
  z-index: 5;
}

.command-category:first-child {
  border-top: none;
  margin-top: 0;
}

.command-category svg {
  width: 12px;
  height: 12px;
  opacity: 0.7;
}

/* 命令历史记录 */
.command-history {
  margin-top: 20px;
  background: var(--glass-bg);
  border-radius: var(--border-radius);
  padding: 15px;
  display: none;
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: 1px solid var(--glass-border);
}

.history-title {
  font-size: 0.9em;
  margin-bottom: 10px;
  color: var(--accent-primary);
  display: flex;
  align-items: center;
  gap: 6px;
}

.history-title svg {
  width: 16px;
  height: 16px;
}

/* 工具提示 */
.tooltip {
  position: relative;
}

.tooltip:hover::before {
  content: attr(data-tooltip);
  position: absolute;
  bottom: 120%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: var(--glass-bg-strong);
  color: var(--text-primary);
  padding: 5px 10px;
  border-radius: 4px;
  font-size: 0.8em;
  white-space: nowrap;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2);
  z-index: 100;
  backdrop-filter: blur(5px);
  -webkit-backdrop-filter: blur(5px);
}

/* 打字机动画 */
@keyframes typing {
  from { width: 0 }
  to { width: 100% }
}

.typing-animation {
  overflow: hidden;
  white-space: nowrap;
  animation: typing 1s steps(30, end);
}

/* AI思考动画 */
.thinking-animation {
  display: inline-block;
  position: relative;
  width: 80px;
  height: 20px;
}

.thinking-animation span {
  position: absolute;
  width: 8px;
  height: 8px;
  background: var(--accent-secondary);
  border-radius: 50%;
  animation: thinking 1.5s infinite ease-in-out;
}

.thinking-animation span:nth-child(1) {
  left: 0;
  animation-delay: 0s;
}

.thinking-animation span:nth-child(2) {
  left: 15px;
  animation-delay: 0.2s;
}

.thinking-animation span:nth-child(3) {
  left: 30px;
  animation-delay: 0.4s;
}

@keyframes thinking {
  0%, 100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-10px);
  }
}

/* 响应式设计 */
@media (max-width: 1024px) {
  .main-content {
    grid-template-columns: 1fr;
  }

  .sidebar {
    order: -1;
  }

  .app-container {
    padding: var(--spacing-md);
    gap: var(--spacing-md);
  }
}

@media (max-width: 768px) {
  .top-nav {
    padding: var(--spacing-sm) var(--spacing-md);
  }

  .nav-brand {
    font-size: 1em;
  }

  .terminal-content {
    padding: var(--spacing-md);
    font-size: 13px;
  }

  .quick-commands {
    grid-template-columns: 1fr;
  }

  .app-container {
    padding: var(--spacing-sm);
    gap: var(--spacing-sm);
  }

  .command-palette {
    width: 95%;
    max-height: 80vh;
  }
}

@media (max-width: 480px) {
  .terminal-content {
    font-size: 12px;
  }

  .command-palette {
    max-height: 85vh;
  }
}

/* 动画效果 */
.fade-in {
  animation: fadeIn 0.5s ease-out;
}

@keyframes fadeIn {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
}

.typing-cursor {
  display: inline-block;
  width: 2px;
  height: 1.2em;
  background: var(--accent-primary);
  margin-left: 2px;
  animation: blink 1s step-end infinite;
  vertical-align: middle;
}

@keyframes blink {
  50% { opacity: 0; }
}

/* 特殊效果 */
.glow {
  box-shadow: 0 0 20px rgba(100, 255, 218, 0.3);
}

.glass-intense {
  background: var(--glass-bg-strong);
  backdrop-filter: blur(30px);
  -webkit-backdrop-filter: blur(30px);
}

/* 滚动条美化 */
::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}

::-webkit-scrollbar-track {
  background: transparent;
}

::-webkit-scrollbar-thumb {
  background: var(--glass-border);
  border-radius: 4px;
}

::-webkit-scrollbar-thumb:hover {
  background: var(--glass-bg-strong);
}
</style>
</head>
<body>
<div class="app-container">
    <!-- 顶部导航栏 -->
    <nav class="top-nav">
        <div class="nav-brand">
            <div class="logo">藤</div>
            <span>藤原的个人终端</span>
        </div>
        <div class="nav-controls">
            <button class="theme-toggle" id="themeToggle">
                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                    <circle cx="12" cy="12" r="5"></circle>
                    <line x1="12" y1="1" x2="12" y2="3"></line>
                    <line x1="12" y1="21" x2="12" y2="23"></line>
                    <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
                    <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
                    <line x1="1" y1="12" x2="3" y2="12"></line>
                    <line x1="21" y1="12" x2="23" y2="12"></line>
                    <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
                    <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
                </svg>
                <span id="themeText">切换主题</span>
            </button>
        </div>
    </nav>

    <!-- 主要内容区域 -->
    <main class="main-content">
        <!-- 终端区域 -->
        <div class="terminal-container">
            <div class="terminal-header">
                <div class="window-controls">
                    <div class="window-btn close" data-tooltip="关闭" id="closeBtn"></div>
                    <div class="window-btn minimize" data-tooltip="最小化" id="minimizeBtn"></div>
                    <div class="window-btn maximize" data-tooltip="最大化" id="maximizeBtn"></div>
                </div>
                <div class="terminal-title">terminal.js - 藤原的个人终端</div>
            </div>
            
            <div class="terminal-content" id="terminalContent">
                <div class="code-line">
                    <div class="line-number">1</div>
                    <div class="line-content"><span class="comment">// 个人信息配置</span></div>
                </div>
                <div class="code-line">
                    <div class="line-number">2</div>
                    <div class="line-content"><span class="keyword">const</span> <span class="function">profile</span> = {</div>
                </div>
                <div class="code-line">
                    <div class="line-number">3</div>
                    <div class="line-content">&nbsp;&nbsp;<span class="keyword">name</span>: <span class="string">"藤原"</span>,</div>
                </div>
                <div class="code-line">
                    <div class="line-number">4</div>
                    <div class="line-content">&nbsp;&nbsp;<span class="keyword">title</span>: <span class="string">"职场牛马!!!"</span>,</div>
                </div>
                <div class="code-line">
                    <div class="line-number">5</div>
                    <div class="line-content">&nbsp;&nbsp;<span class="keyword">contact</span>: {</div>
                </div>
                <div class="code-line">
                    <div class="line-number">6</div>
                    <div class="line-content">&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">email</span>: <span class="string"><a href="mailto:2083737075@qq.com" style="color: var(--text-accent);">"2083737075@qq.com"</a></span>,</div>
                </div>
                <div class="code-line">
                    <div class="line-number">7</div>
                    <div class="line-content">&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">website</span>: <span class="string"><a href="http://tengyuan.icu" target="_blank" style="color: var(--text-accent);">"TengYuan.icu"</a></span>,</div>
                </div>
                <div class="code-line">
                    <div class="line-number">8</div>
                    <div class="line-content">&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">FileCodeBox</span>: <span class="string"><a href="http://wp.tengyuan.icu/" target="_blank" style="color: var(--text-accent);">"wp.tengyuan.icu"</a></span>,</div>
                </div>
                <div class="code-line">
                    <div class="line-number">9</div>
                    <div class="line-content">&nbsp;&nbsp;},</div>
                </div>
                <div class="code-line">
                    <div class="line-number">10</div>
                    <div class="line-content">&nbsp;&nbsp;<span class="keyword">links</span>: {</div>
                </div>
                <div class="code-line">
                    <div class="line-number">11</div>
                    <div class="line-content">&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">travel blog</span>: <span class="string"><a href="http://blog.tengyuan.icu/" target="_blank" style="color: var(--text-accent);">"blog.tengyuan.icu"</a></span>,</div>
                </div>
                <div class="code-line">
                    <div class="line-number">12</div>
                    <div class="line-content">&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">birthday</span>: <span class="string"><a href="http://sr.0814.cn" target="_blank" style="color: var(--text-accent);">"2001/11/01"</a></span>,</div>
                </div>
                <div class="code-line">
                    <div class="line-number">13</div>
                    <div class="line-content">&nbsp;&nbsp;},</div>
                </div>
                <div class="code-line">
                    <div class="line-number">14</div>
                    <div class="line-content">&nbsp;&nbsp;<span class="comment">// 座右铭</span></div>
                </div>
                <div class="code-line">
                    <div class="line-number">15</div>
                    <div class="line-content">&nbsp;&nbsp;<span class="keyword">motto</span>: <span class="string">"以清简代码,筑玖维数字宇宙。"</span>,</div>
                </div>
                <div class="code-line">
                    <div class="line-number">16</div>
                    <div class="line-content">&nbsp;&nbsp;<span class="keyword">copyright</span>: <span class="string">"2017-<span id="currentYear"></span> 藤原"</span>,</div>
                </div>
                <div class="code-line">
                    <div class="line-number">17</div>
                    <div class="line-content">};</div>
                </div>
                <div class="code-line">
                    <div class="line-number">18</div>
                    <div class="line-content"></div>
                </div>
                <div class="code-line">
                    <div class="line-number">19</div>
                    <div class="line-content"><span class="comment">// 终端交互</span></div>
                </div>
                <div class="code-line">
                    <div class="line-number">20</div>
                    <div class="line-content"><span class="function">console</span>.<span class="function">log</span>(<span class="string">"欢迎访问藤原的个人终端"</span>);</div>
                </div>
                <div class="code-line">
                    <div class="line-number">21</div>
                    <div class="line-content"><span class="function">console</span>.<span class="function">log</span>(<span class="string">"输入 'help' 获取可用命令"</span>);</div>
                </div>
                
                <!-- 命令历史记录 -->
                <div class="command-history" id="commandHistory">
                    <div class="history-title">
                        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                            <circle cx="12" cy="12" r="10"></circle>
                            <polyline points="12 6 12 12 16 14"></polyline>
                        </svg>
                        命令历史记录
                    </div>
                    <div class="history-list" id="historyList"></div>
                </div>
            </div>
            
            <div class="terminal-input">
                <div class="input-prompt">></div>
                <input type="text" class="input-field" id="commandInput" placeholder="输入命令..." autocomplete="off">
            </div>
        </div>

        <!-- 侧边栏 -->
        <aside class="sidebar">
            <!-- 快捷命令面板 -->
            <div class="tool-panel">
                <div class="tool-header">
                    <div class="tool-title">
                        <svg class="tool-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                            <rect x="3" y="3" width="7" height="7"></rect>
                            <rect x="14" y="3" width="7" height="7"></rect>
                            <rect x="14" y="14" width="7" height="7"></rect>
                            <rect x="3" y="14" width="7" height="7"></rect>
                        </svg>
                        快捷命令
                    </div>
                </div>
                <div class="tool-content">
                    <div class="quick-commands">
                        <div class="quick-cmd" data-cmd="help">帮助</div>
                        <div class="quick-cmd" data-cmd="clear">清空</div>
                        <div class="quick-cmd" data-cmd="ai">AI助手</div>
                        <div class="quick-cmd" data-cmd="video">视频</div>
                        <div class="quick-cmd" data-cmd="image">图片</div>
                        <div class="quick-cmd" data-cmd="weather">天气</div>
                        <div class="quick-cmd" data-cmd="calendar">日历</div>
                        <div class="quick-cmd" data-cmd="history">历史</div>
                    </div>
                </div>
            </div>

            <!-- 系统状态 -->
            <div class="tool-panel">
                <div class="tool-header">
                    <div class="tool-title">
                        <svg class="tool-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                            <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path>
                        </svg>
                        系统状态
                    </div>
                </div>
                <div class="tool-content">
                    <div class="status-indicators">
                        <div class="status-item">
                            <span class="status-label">连接状态</span>
                            <div style="display: flex; align-items: center; gap: 8px;">
                                <div class="status-dot"></div>
                                <span class="status-value">在线</span>
                            </div>
                        </div>
                        <div class="status-item">
                            <span class="status-label">当前时间</span>
                            <span class="status-value" id="currentTime">--:--:--</span>
                        </div>
                        <div class="status-item">
                            <span class="status-label">命令数量</span>
                            <span class="status-value" id="commandCount">0</span>
                        </div>
                    </div>
                </div>
            </div>

            <!-- 命令历史 -->
            <div class="tool-panel">
                <div class="tool-header">
                    <div class="tool-title">
                        <svg class="tool-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                            <circle cx="12" cy="12" r="10"></circle>
                            <polyline points="12 6 12 12 16 14"></polyline>
                        </svg>
                        命令历史
                    </div>
                </div>
                <div class="tool-content">
                    <div class="history-list" id="sidebarHistoryList">
                        <div class="history-item">help</div>
                        <div class="history-item">clear</div>
                        <div class="history-item">ai 你好</div>
                    </div>
                </div>
            </div>
        </aside>
    </main>
</div>

<!-- 命令面板 -->
<div class="command-palette" id="commandPalette">
    <div class="command-palette-header">
        <span class="window-btn close" data-tooltip="关闭" id="closeCommandPaletteBtn"></span>
        <span class="window-btn minimize" data-tooltip="最小化"></span>
        <span class="window-btn maximize" data-tooltip="最大化"></span>
        <span class="command-palette-title">command-palette.js - 命令面板</span>
    </div>
    <div class="command-palette-content">
        <div class="command-header">
            <input type="text" class="command-search" id="commandSearch" placeholder="搜索命令...">
        </div>
        <div class="command-list" id="commandList">
            <!-- 命令将通过JS动态添加 -->
        </div>
    </div>
</div>

<!-- 隐藏的文件输入元素 -->
<input type="file" id="fileInput" accept="image/*" style="display: none;">

<script>
// 全局变量
let commandHistory = JSON.parse(localStorage.getItem('commandHistory') || '[]');
let historyIndex = -1;
let commandCount = 0;
let lineCounter = 22; // 从个人介绍后开始计数

// DOM 元素
const themeToggle = document.getElementById('themeToggle');
const themeText = document.getElementById('themeText');
const terminalContent = document.getElementById('terminalContent');
const commandInput = document.getElementById('commandInput');
const historyList = document.getElementById('historyList');
const sidebarHistoryList = document.getElementById('sidebarHistoryList');
const commandCountEl = document.getElementById('commandCount');
const currentTimeEl = document.getElementById('currentTime');
const currentYearEl = document.getElementById('currentYear');
const fileInput = document.getElementById('fileInput');
const commandPalette = document.getElementById('commandPalette');
const commandSearch = document.getElementById('commandSearch');
const commandList = document.getElementById('commandList');
const commandHistoryEl = document.getElementById('commandHistory');
const closeBtn = document.getElementById('closeBtn');
const minimizeBtn = document.getElementById('minimizeBtn');
const maximizeBtn = document.getElementById('maximizeBtn');
const closeCommandPaletteBtn = document.getElementById('closeCommandPaletteBtn');

// AI接口配置
const aiApis = [
    {
        id: 'api1',
        name: 'DeepSeek API',
        description: '需要输入API密钥',
        requireKey: true,
        endpoint: async function(key, message) {
            try {
                const response = await fetch('https://api.deepseek.com/v1/chat/completions', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                        'Authorization': `Bearer ${key}`
                    },
                    body: JSON.stringify({
                        model: "deepseek-chat",
                        messages: [
                            {
                                role: "user",
                                content: message
                            }
                        ],
                        temperature: 0.7
                    })
                });
                
                if (!response.ok) {
                    throw new Error(`HTTP error! status: ${response.status}`);
                }
                
                const data = await response.json();
                return {
                    code: 200,
                    msg: data.choices[0].message.content
                };
            } catch (error) {
                return {
                    code: 500,
                    msg: `请求失败: ${error.message}`
                };
            }
        }
    },
    {
        id: 'api2',
        name: '备用API (默认)',
        description: '无需密钥',
        requireKey: false,
        endpoint: async function(key, message) {
            try {
                const response = await fetch(`https://cn.apihz.cn/api/ai/wxtiny.php?id=10003788&key=ffa8afb46dce4916c5a74fd73c8de9f6&words=${encodeURIComponent(message)}`);
                
                if (!response.ok) {
                    throw new Error(`HTTP error! status: ${response.status}`);
                }
                
                const data = await response.json();
                return {
                    code: data.code || 200,
                    msg: data.msg || data.content || 'AI回复为空'
                };
            } catch (error) {
                return {
                    code: 500,
                    msg: `请求失败: ${error.message}`
                };
            }
        }
    }
];

let currentApi = aiApis[1]; // 默认使用备用API

// 视频解析接口
const videoApis = [
    { name: "默认线路【推荐】", url: "https://jx.xmflv.com/?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://www.8090g.cn/jiexi/?url=" }
];

// 命令定义
const commands = [
    {
        id: 'help',
        name: '帮助',
        description: '显示所有可用命令',
        category: '基础',
        shortcut: 'help'
    },
    {
        id: 'clear',
        name: '清空终端',
        description: '清除终端中的所有内容',
        category: '基础',
        shortcut: 'Ctrl+L'
    },
    {
        id: 'history',
        name: '历史记录',
        description: '显示之前执行的命令',
        category: '基础',
        shortcut: 'history'
    },
    {
        id: 'theme',
        name: '切换主题',
        description: '在深色和浅色主题之间切换',
        category: '基础',
        shortcut: 'theme'
    },
    {
        id: 'video',
        name: '视频播放器',
        description: '解析并播放视频',
        category: '工具',
        shortcut: 'video [url]'
    },
    {
        id: 'ai',
        name: 'AI助手',
        description: '与AI助手对话 (使用 --api [api名称] 切换接口)',
        category: '工具',
        shortcut: 'ai [message]'
    },
    {
        id: 'image',
        name: '图片转换',
        description: '转换图片格式 (支持: jpg, png, webp, bmp)',
        category: '工具',
        shortcut: 'image [format]'
    },
    {
        id: 'calendar',
        name: '日历',
        description: '显示日历和事件管理',
        category: '新功能',
        shortcut: 'calendar'
    },
    {
        id: 'weather',
        name: '天气',
        description: '查看天气预报',
        category: '新功能',
        shortcut: 'weather [城市]'
    },
    {
        id: 'set-api',
        name: '设置AI接口',
        description: '设置当前使用的AI接口',
        category: '配置',
        shortcut: 'ai --api [api名称]'
    }
];

// 初始化
function init() {
    // 设置当前年份
    currentYearEl.textContent = new Date().getFullYear();
    
    // 初始化主题
    const savedTheme = localStorage.getItem('theme') || 'dark';
    document.body.setAttribute('data-theme', savedTheme);
    updateThemeText(savedTheme);
    
    // 更新时间
    updateTime();
    setInterval(updateTime, 1000);
    
    // 更新命令计数
    updateCommandCount();
    
    // 更新历史记录
    updateHistoryList();
    
    // 聚焦输入框
    commandInput.focus();
    
    // 初始化命令面板
    initCommandPalette();
    
    // 初始化键盘事件
    initKeyboardEvents();
    
    // 添加欢迎消息
    setTimeout(() => {
        addSystemMessage('欢迎来到藤原的个人终端!输入 "help" 获取可用命令。');
    }, 1000);
}

// 主题切换
themeToggle.addEventListener('click', () => {
    const currentTheme = document.body.getAttribute('data-theme');
    const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
    document.body.setAttribute('data-theme', newTheme);
    localStorage.setItem('theme', newTheme);
    updateThemeText(newTheme);
});

function updateThemeText(theme) {
    themeText.textContent = theme === 'dark' ? '浅色模式' : '深色模式';
}

// 时间更新
function updateTime() {
    const now = new Date();
    const timeString = now.toLocaleTimeString('zh-CN', { hour12: false });
    currentTimeEl.textContent = timeString;
}

// 命令计数更新
function updateCommandCount() {
    commandCountEl.textContent = commandCount;
}

// 历史记录更新
function updateHistoryList() {
    // 更新主历史记录
    if (historyList) {
        historyList.innerHTML = '';
        const recentHistory = commandHistory.slice(-10).reverse();
        
        recentHistory.forEach(cmd => {
            const item = document.createElement('div');
            item.className = 'history-item';
            item.textContent = cmd;
            item.addEventListener('click', () => {
                commandInput.value = cmd;
                commandInput.focus();
            });
            historyList.appendChild(item);
        });
    }
    
    // 更新侧边栏历史记录
    if (sidebarHistoryList) {
        sidebarHistoryList.innerHTML = '';
        const recentHistory = commandHistory.slice(-10).reverse();
        
        recentHistory.forEach(cmd => {
            const item = document.createElement('div');
            item.className = 'history-item';
            item.textContent = cmd;
            item.addEventListener('click', () => {
                commandInput.value = cmd;
                commandInput.focus();
            });
            sidebarHistoryList.appendChild(item);
        });
    }
}

// 添加系统消息
function addSystemMessage(content, animate = true) {
    const messageDiv = document.createElement('div');
    messageDiv.className = 'code-line system-message';
    
    if (animate) {
        messageDiv.classList.add('fade-in');
    }
    
    const lineNumber = document.createElement('div');
    lineNumber.className = 'line-number';
    lineNumber.textContent = lineCounter++;
    
    const lineContent = document.createElement('div');
    lineContent.className = 'line-content';
    lineContent.innerHTML = `<span class="comment">// ${content}</span>`;
    
    messageDiv.appendChild(lineNumber);
    messageDiv.appendChild(lineContent);
    terminalContent.appendChild(messageDiv);
    
    // 滚动到底部
    terminalContent.scrollTop = terminalContent.scrollHeight;
}

// 添加用户输入消息
function addUserMessage(command, animate = true) {
    const messageDiv = document.createElement('div');
    messageDiv.className = 'code-line user-input';
    
    if (animate) {
        messageDiv.classList.add('fade-in');
    }
    
    const lineNumber = document.createElement('div');
    lineNumber.className = 'line-number';
    lineNumber.textContent = lineCounter++;
    
    const lineContent = document.createElement('div');
    lineContent.className = 'line-content';
    lineContent.innerHTML = `<span class="user-prompt">></span> <span class="user-command">${escapeHtml(command)}</span>`;
    
    messageDiv.appendChild(lineNumber);
    messageDiv.appendChild(lineContent);
    terminalContent.appendChild(messageDiv);
    
    // 滚动到底部
    terminalContent.scrollTop = terminalContent.scrollHeight;
}

// 添加AI回复消息
function addAIMessage(content, animate = true) {
    const messageDiv = document.createElement('div');
    messageDiv.className = 'code-line ai-response';
    
    if (animate) {
        messageDiv.classList.add('fade-in');
    }
    
    const lineNumber = document.createElement('div');
    lineNumber.className = 'line-number';
    lineNumber.textContent = lineCounter++;
    
    const lineContent = document.createElement('div');
    lineContent.className = 'line-content';
    lineContent.innerHTML = `<span class="ai-prompt">藤原:</span> <span class="ai-message">"${escapeHtml(content)}"</span>`;
    
    messageDiv.appendChild(lineNumber);
    messageDiv.appendChild(lineContent);
    terminalContent.appendChild(messageDiv);
    
    // 滚动到底部
    terminalContent.scrollTop = terminalContent.scrollHeight;
}

// 打字机效果的AI回复
async function typeAIMessage(content) {
    const messageDiv = document.createElement('div');
    messageDiv.className = 'code-line ai-response fade-in';
    
    const lineNumber = document.createElement('div');
    lineNumber.className = 'line-number';
    lineNumber.textContent = lineCounter++;
    
    const lineContent = document.createElement('div');
    lineContent.className = 'line-content';
    lineContent.innerHTML = `<span class="ai-prompt">藤原:</span> <span class="ai-message">"</span>`;
    
    messageDiv.appendChild(lineNumber);
    messageDiv.appendChild(lineContent);
    terminalContent.appendChild(messageDiv);
    
    // 添加光标
    const cursor = document.createElement('span');
    cursor.className = 'typing-cursor';
    const aiMessage = lineContent.querySelector('.ai-message');
    aiMessage.appendChild(cursor);
    
    // 打字效果
    for (let i = 0; i < content.length; i++) {
        await new Promise(resolve => setTimeout(resolve, 30));
        aiMessage.insertBefore(document.createTextNode(content[i]), cursor);
        terminalContent.scrollTop = terminalContent.scrollHeight;
    }
    
    // 添加结束引号并移除光标
    aiMessage.insertBefore(document.createTextNode('"'), cursor);
    cursor.remove();
}

// HTML转义函数
function escapeHtml(text) {
    const div = document.createElement('div');
    div.textContent = text;
    return div.innerHTML;
}

// 初始化命令面板
function initCommandPalette() {
    commandList.innerHTML = '';
    const categories = [...new Set(commands.map(cmd => cmd.category))];
    
    categories.forEach(category => {
        const categoryCommands = commands.filter(cmd => cmd.category === category);
        
        const categoryTitle = document.createElement('div');
        categoryTitle.className = 'command-category';
        categoryTitle.innerHTML = `
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                ${getCategoryIcon(category)}
            </svg>
            ${category}
        `;
        
        commandList.appendChild(categoryTitle);
        
        categoryCommands.forEach(cmd => {
            const item = document.createElement('div');
            item.className = 'command-item';
            item.dataset.id = cmd.id;
            item.dataset.category = cmd.category;
            
            const icon = document.createElement('div');
            icon.className = 'command-icon';
            icon.innerHTML = getCommandIcon(cmd.id);
            
            const content = document.createElement('div');
            content.className = 'command-content';
            
            const title = document.createElement('div');
            title.className = 'command-title';
            title.textContent = cmd.name;
            
            const description = document.createElement('div');
            description.className = 'command-description';
            description.textContent = cmd.description;
            
            content.appendChild(title);
            content.appendChild(description);
            
            const shortcut = document.createElement('div');
            shortcut.className = 'command-shortcut';
            shortcut.textContent = cmd.shortcut || '';
            
            item.appendChild(icon);
            item.appendChild(content);
            item.appendChild(shortcut);
            
            item.addEventListener('click', () => {
                executeCommandFromPalette(cmd);
            });
            
            commandList.appendChild(item);
        });
    });
}

function getCategoryIcon(category) {
    switch(category) {
        case '基础': return '<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10 9 9 9 8 9"></polyline>';
        case '工具': return '<rect x="3" y="3" width="7" height="7"></rect><rect x="14" y="3" width="7" height="7"></rect><rect x="14" y="14" width="7" height="7"></rect><rect x="3" y="14" width="7" height="7"></rect>';
        case '新功能': return '<circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line>';
        case '配置': return '<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path>';
        default: return '<circle cx="12" cy="12" r="10"></circle>';
    }
}

function getCommandIcon(cmdId) {
    switch(cmdId) {
        case 'help': return '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>';
        case 'clear': return '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path></svg>';
        case 'video': return '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="2" width="20" height="20" rx="2.18" ry="2.18"></rect><line x1="7" y1="2" x2="7" y2="22"></line><line x1="17" y1="2" x2="17" y2="22"></line><line x1="2" y1="12" x2="22" y2="12"></line><line x1="2" y1="7" x2="7" y2="7"></line><line x1="2" y1="17" x2="7" y2="17"></line><line x1="17" y1="17" x2="22" y2="17"></line><line x1="17" y1="7" x2="22" y2="7"></line></svg>';
        case 'ai': return '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polygon points="10 8 16 12 10 16 10 8"></polygon></svg>';
        case 'image': return '<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>';
        case 'history': return '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg>';
        case 'theme': return '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg>';
        case 'calendar': return '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg>';
        case 'weather': return '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path><circle cx="12" cy="10" r="3"></circle></svg>';
        case 'set-api': return '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path></svg>';
        default: return '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle></svg>';
    }
}

function executeCommandFromPalette(cmd) {
    commandPalette.style.display = 'none';
    
    switch(cmd.id) {
        case 'help':
        case 'clear':
        case 'history':
        case 'calendar':
            commandInput.value = cmd.id;
            break;
        case 'video':
        case 'ai':
        case 'image':
        case 'weather':
            commandInput.value = cmd.id + ' ';
            break;
        case 'theme':
            themeToggle.click();
            return;
        case 'set-api':
            commandInput.value = 'ai --api ';
            break;
        default:
            return;
    }
    
    commandInput.focus();
    if (['help', 'clear', 'history', 'calendar'].includes(cmd.id)) {
        const event = new KeyboardEvent('keypress', {'key': 'Enter'});
        commandInput.dispatchEvent(event);
    }
}

function filterCommands(query) {
    const items = commandList.querySelectorAll('.command-item');
    const categories = commandList.querySelectorAll('.command-category');
    query = query.toLowerCase();
    
    let firstVisible = null;
    let visibleCategories = new Set();
    
    categories.forEach(category => {
        category.style.display = 'none';
    });
    
    items.forEach(item => {
        const cmd = commands.find(c => c.id === item.dataset.id);
        const matchesName = cmd.name.toLowerCase().includes(query);
        const matchesId = cmd.id.toLowerCase().includes(query);
        const matchesDesc = cmd.description.toLowerCase().includes(query);
        const matchesCategory = cmd.category.toLowerCase().includes(query);
        
        if (matchesName || matchesId || matchesDesc || matchesCategory) {
            item.style.display = 'flex';
            visibleCategories.add(cmd.category);
            
            if (!firstVisible) {
                firstVisible = item;
                item.classList.add('selected');
            } else {
                item.classList.remove('selected');
            }
        } else {
            item.style.display = 'none';
            item.classList.remove('selected');
        }
    });
    
    categories.forEach(category => {
        if (visibleCategories.has(category.textContent.trim())) {
            category.style.display = 'flex';
        }
    });
    
    if (!query) {
        categories.forEach(category => {
            category.style.display = 'flex';
        });
    }
}

// 初始化键盘事件
function initKeyboardEvents() {
    document.addEventListener('keydown', (e) => {
        if (e.ctrlKey && e.key === 'k') {
            e.preventDefault();
            commandPalette.style.display = 'block';
            commandSearch.value = '';
            filterCommands('');
            commandSearch.focus();
            return;
        }
        
        if (e.ctrlKey && e.key === 'l') {
            e.preventDefault();
            clearTerminal();
            return;
        }
        
        if (e.key === 'Escape') {
            commandPalette.style.display = 'none';
            commandInput.focus();
            return;
        }
        
        if (commandPalette.style.display === 'block') {
            const items = Array.from(commandList.querySelectorAll('.command-item')).filter(
                item => item.style.display !== 'none'
            );
            
            if (items.length > 0) {
                const currentIndex = items.findIndex(item => item.classList.contains('selected'));
                
                if (e.key === 'ArrowDown') {
                    e.preventDefault();
                    if (currentIndex < items.length - 1) {
                        items[currentIndex].classList.remove('selected');
                        items[currentIndex + 1].classList.add('selected');
                        items[currentIndex + 1].scrollIntoView({ behavior: 'smooth', block: 'nearest' });
                    }
                } else if (e.key === 'ArrowUp') {
                    e.preventDefault();
                    if (currentIndex > 0) {
                        items[currentIndex].classList.remove('selected');
                        items[currentIndex - 1].classList.add('selected');
                        items[currentIndex - 1].scrollIntoView({ behavior: 'smooth', block: 'nearest' });
                    }
                } else if (e.key === 'Enter') {
                    e.preventDefault();
                    const selectedItem = commandList.querySelector('.command-item.selected');
                    if (selectedItem) {
                        const cmdId = selectedItem.dataset.id;
                        const cmd = commands.find(c => c.id === cmdId);
                        executeCommandFromPalette(cmd);
                    }
                }
            }
        }
        
        if (document.activeElement === commandInput) {
            if (e.key === 'ArrowUp' && commandHistory.length > 0) {
                e.preventDefault();
                
                if (historyIndex < commandHistory.length - 1) {
                    historyIndex++;
                    commandInput.value = commandHistory[commandHistory.length - 1 - historyIndex];
                }
            } else if (e.key === 'ArrowDown' && historyIndex > -1) {
                e.preventDefault();
                
                historyIndex--;
                if (historyIndex === -1) {
                    commandInput.value = '';
                } else {
                    commandInput.value = commandHistory[commandHistory.length - 1 - historyIndex];
                }
            }
        }
    });
    
    commandSearch.addEventListener('input', () => {
        filterCommands(commandSearch.value);
    });
}

// 帮助信息
const helpText = `可用命令:

基础命令:
• help - 显示帮助信息
• clear - 清空终端
• history - 显示命令历史记录
• theme - 切换深色/浅色主题

工具命令:
• video [url] - 解析并播放视频
  例如: video https://v.qq.com/x/cover/mzc00200mp8er9d.html
• ai [message] - 与AI助手对话
  例如: ai 你好
  切换API: ai --api [api名称]
• image [format] - 转换图片格式 (支持: jpg, png, webp, bmp)
  例如: image png

新功能:
• calendar - 显示日历和事件管理
• weather [城市名] - 查看天气预报
  例如: weather 北京

配置命令:
• ai --api [api名称] - 切换AI接口
  可用API: DeepSeek API (需要密钥), 备用API (默认)

快捷键:
• Ctrl+K - 打开命令面板
• Ctrl+L - 清空终端
• 上下箭头 - 浏览命令历史`;

// ============== 视频播放器功能 ==============
function createVideoPlayer(videoUrl) {
    // 移除现有的播放器
    const existingPlayer = document.querySelector('.video-container');
    if (existingPlayer) existingPlayer.remove();

    // 创建容器
    const videoContainer = document.createElement('div');
    videoContainer.className = 'video-container';
    
    // 创建视频元素
    const videoPlayer = document.createElement('div');
    videoPlayer.className = 'video-player';
    
    const videoElement = document.createElement('video');
    videoElement.id = 'terminal-video';
    videoElement.controls = true;
    videoElement.src = videoUrl;
    
    videoPlayer.appendChild(videoElement);
    
    // 视频控制区域
    const videoControls = document.createElement('div');
    videoControls.className = 'video-controls';
    
    // 视频信息
    const videoInfo = document.createElement('div');
    videoInfo.className = 'video-info';
    
    const videoTitle = document.createElement('div');
    videoTitle.textContent = '视频播放器';
    
    const videoStatus = document.createElement('div');
    videoStatus.className = 'video-status';
    videoStatus.textContent = '加载中...';
    
    videoInfo.appendChild(videoTitle);
    videoInfo.appendChild(videoStatus);
    
    // 主控制按钮
    const videoMainControls = document.createElement('div');
    videoMainControls.className = 'video-main-controls';
    
    // 播放控制按钮组
    const playButtonGroup = document.createElement('div');
    playButtonGroup.className = 'video-button-group';
    
    const playButton = document.createElement('button');
    playButton.className = 'video-button';
    playButton.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="5 3 19 12 5 21 5 3"></polygon></svg> 播放';
    playButton.addEventListener('click', () => {
        if (videoElement.paused) {
            videoElement.play();
            playButton.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="6" y="4" width="4" height="16"></rect><rect x="14" y="4" width="4" height="16"></rect></svg> 暂停';
        } else {
            videoElement.pause();
            playButton.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="5 3 19 12 5 21 5 3"></polygon></svg> 播放';
        }
    });
    
    const fullscreenButton = document.createElement('button');
    fullscreenButton.className = 'video-button';
    fullscreenButton.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"></path></svg> 全屏';
    fullscreenButton.addEventListener('click', toggleFullscreen);
    
    playButtonGroup.appendChild(playButton);
    playButtonGroup.appendChild(fullscreenButton);
    
    // 进度条
    const videoTimeline = document.createElement('div');
    videoTimeline.className = 'video-timeline';
    
    const videoProgress = document.createElement('div');
    videoProgress.className = 'video-progress';
    
    videoTimeline.appendChild(videoProgress);
    videoTimeline.addEventListener('click', (e) => {
        const rect = videoTimeline.getBoundingClientRect();
        const pos = (e.clientX - rect.left) / rect.width;
        videoElement.currentTime = pos * videoElement.duration;
    });
    
    // 时间显示
    const videoTime = document.createElement('div');
    videoTime.className = 'video-time';
    videoTime.textContent = '00:00 / 00:00';
    
    videoMainControls.appendChild(playButtonGroup);
    videoMainControls.appendChild(videoTimeline);
    videoMainControls.appendChild(videoTime);
    
    // 高级控制
    const videoAdvancedControls = document.createElement('div');
    videoAdvancedControls.className = 'video-advanced-controls';
    
    // 播放速度按钮
    const speedButton = document.createElement('button');
    speedButton.className = 'video-button';
    speedButton.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 4 23 10 17 10"></polyline><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"></path></svg> 1.0x';
    speedButton.addEventListener('click', changePlaybackSpeed);
    
    // 音量控制
    const volumeButton = document.createElement('button');
    volumeButton.className = 'video-button';
    volumeButton.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"></polygon></svg> 音量';
    volumeButton.addEventListener('click', toggleMute);
    
    // 网页全屏按钮
    const webFullscreenButton = document.createElement('button');
    webFullscreenButton.className = 'video-button';
    webFullscreenButton.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg> 网页全屏';
    webFullscreenButton.addEventListener('click', () => {
        window.open(videoUrl, '_blank');
    });
    
    // 关闭按钮
    const closeButton = document.createElement('button');
    closeButton.className = 'video-button';
    closeButton.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> 关闭';
    closeButton.addEventListener('click', () => videoContainer.remove());
    
    // 解析接口选择器
    const apiSelector = document.createElement('div');
    apiSelector.className = 'video-api-selector';
    
    const apiSelect = document.createElement('select');
    apiSelect.className = 'video-api-select';
    
    videoApis.forEach((api, index) => {
        const option = document.createElement('option');
        option.className = 'video-api-option';
        option.value = index;
        option.textContent = api.name;
        apiSelect.appendChild(option);
    });
    
    apiSelect.addEventListener('change', () => {
        const selectedApi = videoApis[apiSelect.value];
        const newUrl = selectedApi.url + encodeURIComponent(videoUrl.split('url=')[1] || videoUrl);
        videoElement.src = newUrl;
        videoStatus.textContent = `已切换到 ${selectedApi.name}`;
    });
    
    apiSelector.appendChild(apiSelect);
    
    videoAdvancedControls.appendChild(speedButton);
    videoAdvancedControls.appendChild(volumeButton);
    videoAdvancedControls.appendChild(webFullscreenButton);
    videoAdvancedControls.appendChild(closeButton);
    videoAdvancedControls.appendChild(apiSelector);
    
    // 组装控制区域
    videoControls.appendChild(videoInfo);
    videoControls.appendChild(videoMainControls);
    videoControls.appendChild(videoAdvancedControls);
    
    // 组装整个播放器
    videoContainer.appendChild(videoPlayer);
    videoContainer.appendChild(videoControls);
    
    // 添加到编辑器内容区
    terminalContent.appendChild(videoContainer);
    terminalContent.scrollTop = terminalContent.scrollHeight;
    
    // 自动播放(需要用户交互后才会真正播放)
    videoElement.play().catch(e => {
        videoStatus.textContent = '点击播放按钮开始播放';
    });
    
    // 更新进度条和时间显示
    videoElement.addEventListener('timeupdate', updateProgress);
    videoElement.addEventListener('loadedmetadata', () => {
        videoStatus.textContent = '已加载';
        updateTimeDisplay();
    });
    
    // 双击全屏
    videoPlayer.addEventListener('dblclick', toggleFullscreen);
    
    // 键盘控制
    document.addEventListener('keydown', handleVideoKeyControls);
    
    // ============== 功能函数 ==============
    
    function toggleFullscreen() {
        if (!document.fullscreenElement) {
            videoPlayer.requestFullscreen().catch(err => {
                videoStatus.textContent = `全屏错误: ${err.message}`;
            });
        } else {
            document.exitFullscreen();
        }
    }
    
    function changePlaybackSpeed() {
        const speeds = [0.5, 0.75, 1.0, 1.25, 1.5, 2.0];
        const currentSpeed = videoElement.playbackRate;
        const currentIndex = speeds.indexOf(currentSpeed);
        const nextIndex = (currentIndex + 1) % speeds.length;
        videoElement.playbackRate = speeds[nextIndex];
        speedButton.innerHTML = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 4 23 10 17 10"></polyline><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"></path></svg> ${speeds[nextIndex]}x`;
    }
    
    function toggleMute() {
        videoElement.muted = !videoElement.muted;
        volumeButton.innerHTML = videoElement.muted ? 
            '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="1" y1="1" x2="23" y2="23"></line><path d="M16.5 16.5 12 21l-4-4H2V7h4l4.5-4.5"></path><line x1="21" y1="12" x2="23" y2="12"></line></svg> 静音' : 
            '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"></polygon></svg> 音量';
    }
    
    function updateProgress() {
        const percent = (videoElement.currentTime / videoElement.duration) * 100;
        videoProgress.style.width = `${percent}%`;
        updateTimeDisplay();
    }
    
    function updateTimeDisplay() {
        const formatTime = (seconds) => {
            const mins = Math.floor(seconds / 60);
            const secs = Math.floor(seconds % 60);
            return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
        };
        
        videoTime.textContent = `${formatTime(videoElement.currentTime)} / ${formatTime(videoElement.duration)}`;
    }
    
    function handleVideoKeyControls(e) {
        // 只在视频可见时处理按键
        if (!videoContainer.isConnected) {
            document.removeEventListener('keydown', handleVideoKeyControls);
            return;
        }
        
        // 空格键切换播放/暂停
        if (e.code === 'Space' && document.activeElement !== commandInput) {
            e.preventDefault();
            if (videoElement.paused) {
                videoElement.play();
                playButton.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="6" y="4" width="4" height="16"></rect><rect x="14" y="4" width="4" height="16"></rect></svg> 暂停';
            } else {
                videoElement.pause();
                playButton.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="5 3 19 12 5 21 5 3"></polygon></svg> 播放';
            }
        }
        
        // 左右箭头快进/快退
        if (e.code === 'ArrowRight') {
            videoElement.currentTime += 5;
        } else if (e.code === 'ArrowLeft') {
            videoElement.currentTime -= 5;
        }
        
        // F键切换全屏
        if (e.code === 'KeyF') {
            toggleFullscreen();
        }
        
        // M键切换静音
        if (e.code === 'KeyM') {
            toggleMute();
        }
    }
}

// ============== 视频解析功能 ==============
async function parseVideo(url) {
    try {
        if (!/^https?:\/\/.+/.test(url)) {
            addAIMessage('错误:请提供有效的URL地址');
            return;
        }
        
        await typeAIMessage('正在解析视频,请稍候...');
        
        // 尝试直接播放(适用于MP4等直接可播放格式)
        if (url.match(/\.(mp4|webm|ogg)$/i)) {
            createVideoPlayer(url);
            addAIMessage('视频已加载');
            return;
        }
        
        // 使用默认解析方案
        const defaultApi = videoApis[0];
        const apiUrl = defaultApi.url + encodeURIComponent(url);
        createVideoPlayer(apiUrl);
        
        addAIMessage('视频已加载,如果无法播放请尝试其他线路');
        
    } catch (error) {
        addAIMessage(`视频解析失败: ${error.message}`);
    }
}

// 创建图片转换器界面
function createImageConverter(targetFormat) {
    const existingConverter = document.querySelector('.image-converter');
    if (existingConverter) {
        existingConverter.remove();
        return;
    }
    
    const converter = document.createElement('div');
    converter.className = 'image-converter';
    
    // 头部
    const imageHeader = document.createElement('div');
    imageHeader.className = 'image-header';
    
    const imageTitle = document.createElement('div');
    imageTitle.className = 'image-title';
    imageTitle.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>
        图片格式转换
    `;
    
    const imageClose = document.createElement('button');
    imageClose.className = 'image-close';
    imageClose.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>';
    imageClose.addEventListener('click', () => converter.remove());
    
    imageHeader.appendChild(imageTitle);
    imageHeader.appendChild(imageClose);
    
    // 内容区域
    const imageContent = document.createElement('div');
    imageContent.className = 'image-content';
    
    // 预览区域
    const previewContainer = document.createElement('div');
    previewContainer.className = 'image-preview-container';
    
    const previewImage = document.createElement('img');
    previewImage.className = 'image-preview';
    
    const previewPlaceholder = document.createElement('div');
    previewPlaceholder.className = 'image-placeholder';
    previewPlaceholder.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;">支持 JPG, PNG, WEBP, BMP 格式</div>
    `;
    
    previewContainer.appendChild(previewImage);
    previewContainer.appendChild(previewPlaceholder);
    
    // 控制区域
    const controls = document.createElement('div');
    controls.className = 'image-controls';
    
    // 图片质量控制
    const qualityControl = document.createElement('div');
    qualityControl.className = 'image-control';
    
    const qualityLabel = document.createElement('label');
    qualityLabel.innerHTML = `
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <circle cx="12" cy="12" r="10"></circle>
            <path d="M8.56 2.75c4.37 6.03 6.02 9.42 8.03 17.72m2.54-15.38c-3.72 4.35-8.94 5.66-16.88 5.85m19.5 1.9c-3.5-.93-6.63-.82-8.94 0-2.58.92-5.01 2.86-7.44 6.32"></path>
        </svg>
        图片质量 (0-100)
    `;
    
    const qualityInput = document.createElement('input');
    qualityInput.type = 'range';
    qualityInput.id = 'imageQuality';
    qualityInput.min = '0';
    qualityInput.max = '100';
    qualityInput.value = '80';
    
    const qualityValue = document.createElement('span');
    qualityValue.textContent = '80';
    
    qualityInput.addEventListener('input', () => {
        qualityValue.textContent = qualityInput.value;
    });
    
    qualityControl.appendChild(qualityLabel);
    qualityControl.appendChild(qualityInput);
    qualityControl.appendChild(qualityValue);
    
    // 图片宽度控制
    const widthControl = document.createElement('div');
    widthControl.className = 'image-control';
    
    const widthLabel = document.createElement('label');
    widthLabel.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>
            <line x1="9" y1="9" x2="15" y2="15"></line>
            <line x1="15" y1="9" x2="9" y2="15"></line>
        </svg>
        宽度 (像素)
    `;
    
    const widthInput = document.createElement('input');
    widthInput.type = 'number';
    widthInput.id = 'imageWidth';
    widthInput.placeholder = '自动';
    
    widthControl.appendChild(widthLabel);
    widthControl.appendChild(widthInput);
    
    // 图片高度控制
    const heightControl = document.createElement('div');
    heightControl.className = 'image-control';
    
    const heightLabel = document.createElement('label');
    heightLabel.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>
            <line x1="12" y1="7" x2="12" y2="12"></line>
            <line x1="5" y1="12" x2="19" y2="12"></line>
        </svg>
        高度 (像素)
    `;
    
    const heightInput = document.createElement('input');
    heightInput.type = 'number';
    heightInput.id = 'imageHeight';
    heightInput.placeholder = '自动';
    
    heightControl.appendChild(heightLabel);
    heightControl.appendChild(heightInput);
    
    // 图片格式选择
    const formatControl = document.createElement('div');
    formatControl.className = 'image-control';
    
    const formatLabel = document.createElement('label');
    formatLabel.innerHTML = `
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"></path>
            <path d="M3 5v14a2 2 0 0 0 2 2h16v-4"></path>
            <path d="M18 17a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2Z"></path>
        </svg>
        目标格式
    `;
    
    const formatSelect = document.createElement('select');
    formatSelect.id = 'imageFormat';
    
    const formats = ['jpg', 'jpeg', 'png', 'webp', 'bmp'];
    formats.forEach(format => {
        const option = document.createElement('option');
        option.value = format;
        option.textContent = format.toUpperCase();
        if (format === targetFormat) option.selected = true;
        formatSelect.appendChild(option);
    });
    
    formatControl.appendChild(formatLabel);
    formatControl.appendChild(formatSelect);
    
    controls.appendChild(qualityControl);
    controls.appendChild(widthControl);
    controls.appendChild(heightControl);
    controls.appendChild(formatControl);
    
    // 操作按钮
    const actions = document.createElement('div');
    actions.className = 'image-actions';
    
    const selectBtn = document.createElement('button');
    selectBtn.className = 'image-btn secondary';
    selectBtn.innerHTML = `
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h7"></path>
            <line x1="16" y1="5" x2="22" y2="5"></line>
            <line x1="19" y1="2" x2="19" y2="8"></line>
            <circle cx="9" cy="9" r="2"></circle>
            <path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"></path>
        </svg>
        选择图片
    `;
    selectBtn.addEventListener('click', () => {
        fileInput.click();
    });
    
    const convertBtn = document.createElement('button');
    convertBtn.className = 'image-btn';
    convertBtn.innerHTML = `
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h7"></path>
            <line x1="16" y1="5" x2="22" y2="5"></line>
            <line x1="19" y1="2" x2="19" y2="8"></line>
            <path d="M12 12H9.5a2.5 2.5 0 0 1 0-5H17"></path>
            <path d="M12 12h2.5a2.5 2.5 0 0 0 0-5H7"></path>
        </svg>
        转换图片
    `;
    convertBtn.addEventListener('click', () => {
        if (!previewImage.src) {
            addAIMessage('请先选择要转换的图片');
            return;
        }
        convertImage(previewImage, formatSelect.value, qualityInput.value, widthInput.value, heightInput.value);
    });
    
    const downloadBtn = document.createElement('button');
    downloadBtn.className = 'image-btn';
    downloadBtn.innerHTML = `
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
            <polyline points="7 10 12 15 17 10"></polyline>
            <line x1="12" y1="15" x2="12" y2="3"></line>
        </svg>
        下载图片
    `;
    downloadBtn.style.display = 'none';
    downloadBtn.id = 'downloadBtn';
    
    const downloadLink = document.createElement('a');
    downloadLink.className = 'image-download-link';
    downloadLink.id = 'downloadLink';
    downloadLink.download = `converted-image.${formatSelect.value}`;
    
    actions.appendChild(selectBtn);
    actions.appendChild(convertBtn);
    actions.appendChild(downloadBtn);
    actions.appendChild(downloadLink);
    
    imageContent.appendChild(previewContainer);
    imageContent.appendChild(controls);
    imageContent.appendChild(actions);
    
    converter.appendChild(imageHeader);
    converter.appendChild(imageContent);
    
    terminalContent.appendChild(converter);
    terminalContent.scrollTop = terminalContent.scrollHeight;
    
    fileInput.onchange = (e) => {
        const file = e.target.files[0];
        if (!file) return;
        
        if (!file.type.match('image.*')) {
            addAIMessage('请选择有效的图片文件');
            return;
        }
        
        const reader = new FileReader();
        reader.onload = (event) => {
            previewImage.src = event.target.result;
            previewImage.classList.add('active');
            previewPlaceholder.style.display = 'none';
            
            const img = new Image();
            img.onload = function() {
                previewPlaceholder.innerHTML = `
                    <div>预览 (原始: ${img.width}×${img.height}, ${formatToHumanReadable(file.size)})</div>
                    <div style="font-size:0.8em;margin-top:8px;">点击"转换图片"按钮开始转换</div>
                `;
                previewPlaceholder.style.display = 'flex';
                previewPlaceholder.style.padding = '10px';
            };
            img.src = event.target.result;
        };
        reader.readAsDataURL(file);
    };
    
    formatSelect.addEventListener('change', () => {
        const downloadLink = document.getElementById('downloadLink');
        if (downloadLink) {
            downloadLink.download = `converted-image.${formatSelect.value}`;
        }
    });
}

// 转换图片
function convertImage(imgElement, format, quality, width, height) {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    
    let newWidth = parseInt(width) || imgElement.naturalWidth;
    let newHeight = parseInt(height) || imgElement.naturalHeight;
    
    if (width && !height) {
        const ratio = imgElement.naturalHeight / imgElement.naturalWidth;
        newHeight = Math.round(newWidth * ratio);
    } else if (height && !width) {
        const ratio = imgElement.naturalWidth / imgElement.naturalHeight;
        newWidth = Math.round(newHeight * ratio);
    }
    
    canvas.width = newWidth;
    canvas.height = newHeight;
    
    ctx.drawImage(imgElement, 0, 0, newWidth, newHeight);
    
    let mimeType;
    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/bmp';
            break;
        default:
            mimeType = 'image/jpeg';
    }
    
    let qualityValue = parseInt(quality) / 100;
    if (isNaN(qualityValue)) qualityValue = 0.8;
    
    canvas.toBlob((blob) => {
        const url = URL.createObjectURL(blob);
        const downloadLink = document.getElementById('downloadLink');
        const downloadBtn = document.getElementById('downloadBtn');
        
        if (downloadLink && downloadBtn) {
            downloadLink.href = url;
            downloadLink.download = `converted-image.${format}`;
            
            downloadBtn.style.display = 'inline-block';
            downloadBtn.onclick = () => {
                downloadLink.click();
                addAIMessage(`图片已转换完成 (${newWidth}×${newHeight}, ${formatToHumanReadable(blob.size)})`);
            };
        }
        
        const previewImage = document.querySelector('.image-preview');
        if (previewImage) {
            previewImage.src = url;
        }
        
        const previewPlaceholder = document.querySelector('.image-placeholder');
        if (previewPlaceholder) {
            previewPlaceholder.innerHTML = `
                <div>预览 (转换后: ${newWidth}×${newHeight}, ${formatToHumanReadable(blob.size)})</div>
                <div style="font-size:0.8em;margin-top:8px;">点击"下载图片"按钮保存结果</div>
            `;
        }
        
        addAIMessage(`图片已成功转换为 ${format.toUpperCase()} 格式!点击"下载图片"按钮保存结果。`);
    }, mimeType, qualityValue);
}

// 将字节大小转换为易读格式
function formatToHumanReadable(bytes) {
    if (bytes === 0) return '0 Bytes';
    const k = 1024;
    const sizes = ['Bytes', 'KB', 'MB', 'GB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}

// 显示命令历史
function toggleCommandHistory() {
    if (commandHistoryEl.style.display === 'none' || !commandHistoryEl.style.display) {
        commandHistoryEl.style.display = 'block';
        updateHistoryList();
    } else {
        commandHistoryEl.style.display = 'none';
    }
}

// 创建日历组件
function createCalendar() {
    const existingCalendar = document.querySelector('.calendar-container');
    if (existingCalendar) {
        existingCalendar.remove();
        return;
    }
    
    const now = new Date();
    let currentMonth = now.getMonth();
    let currentYear = now.getFullYear();
    let selectedDate = now;
    
    const calendarContainer = document.createElement('div');
    calendarContainer.className = 'calendar-container';
    
    // 日历头部
    const calendarHeader = document.createElement('div');
    calendarHeader.className = 'calendar-header';
    
    const calendarTitle = document.createElement('div');
    calendarTitle.className = 'calendar-title';
    
    const calendarNav = document.createElement('div');
    calendarNav.className = 'calendar-nav';
    
    const prevBtn = document.createElement('button');
    prevBtn.className = 'calendar-btn';
    prevBtn.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg>';
    prevBtn.addEventListener('click', () => {
        currentMonth--;
        if (currentMonth < 0) {
            currentMonth = 11;
            currentYear--;
        }
        renderCalendar();
    });
    
    const nextBtn = document.createElement('button');
    nextBtn.className = 'calendar-btn';
    nextBtn.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg>';
    nextBtn.addEventListener('click', () => {
        currentMonth++;
        if (currentMonth > 11) {
            currentMonth = 0;
            currentYear++;
        }
        renderCalendar();
    });
    
    const todayBtn = document.createElement('button');
    todayBtn.className = 'calendar-btn';
    todayBtn.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></svg>';
    todayBtn.addEventListener('click', () => {
        const today = new Date();
        currentMonth = today.getMonth();
        currentYear = today.getFullYear();
        selectedDate = today;
        renderCalendar();
    });
    
    calendarNav.appendChild(prevBtn);
    calendarNav.appendChild(todayBtn);
    calendarNav.appendChild(nextBtn);
    
    calendarHeader.appendChild(calendarTitle);
    calendarHeader.appendChild(calendarNav);
    
    // 日历内容
    const calendarContent = document.createElement('div');
    calendarContent.className = 'calendar-content';
    
    // 日历网格
    const calendarGrid = document.createElement('div');
    calendarGrid.className = 'calendar-grid';
    
    // 星期标题
    const weekdays = ['日', '一', '二', '三', '四', '五', '六'];
    weekdays.forEach(day => {
        const weekdayElem = document.createElement('div');
        weekdayElem.className = 'calendar-weekday';
        weekdayElem.textContent = day;
        calendarGrid.appendChild(weekdayElem);
    });
    
    // 日历事件区域
    const calendarEvents = document.createElement('div');
    calendarEvents.className = 'calendar-events';
    
    const eventsTitle = document.createElement('div');
    eventsTitle.className = 'calendar-events-title';
    eventsTitle.innerHTML = `
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
            <line x1="16" y1="2" x2="16" y2="6"></line>
            <line x1="8" y1="2" x2="8" y2="6"></line>
            <line x1="3" y1="10" x2="21" y2="10"></line>
        </svg>
        今日事件
    `;
    
    calendarEvents.appendChild(eventsTitle);
    
    // 添加到容器
    calendarContent.appendChild(calendarGrid);
    calendarContent.appendChild(calendarEvents);
    
    calendarContainer.appendChild(calendarHeader);
    calendarContainer.appendChild(calendarContent);
    
    terminalContent.appendChild(calendarContainer);
    
    // 渲染日历函数
    function renderCalendar() {
        const monthNames = ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'];
        calendarTitle.textContent = `${monthNames[currentMonth]} ${currentYear}`;
        
        const days = calendarGrid.querySelectorAll('.calendar-day');
        days.forEach(day => day.remove());
        
        const firstDay = new Date(currentYear, currentMonth, 1);
        const lastDay = new Date(currentYear, currentMonth + 1, 0);
        const prevMonthLastDay = new Date(currentYear, currentMonth, 0).getDate();
        const firstDayIndex = firstDay.getDay();
        const lastDate = lastDay.getDate();
        const today = new Date();
        
        for (let i = firstDayIndex; i > 0; i--) {
            const dayElem = document.createElement('div');
            dayElem.className = 'calendar-day other-month';
            dayElem.textContent = prevMonthLastDay - i + 1;
            calendarGrid.appendChild(dayElem);
        }
        
        for (let i = 1; i <= lastDate; i++) {
            const dayElem = document.createElement('div');
            dayElem.className = 'calendar-day';
            dayElem.textContent = i;
            
            if (i === today.getDate() && currentMonth === today.getMonth() && currentYear === today.getFullYear()) {
                dayElem.classList.add('today');
            }
            
            if (i === selectedDate.getDate() && currentMonth === selectedDate.getMonth() && currentYear === selectedDate.getFullYear()) {
                dayElem.classList.add('selected');
            }
            
            if (i === 1 && currentMonth === 10) {
                dayElem.classList.add('birthday');
                dayElem.setAttribute('title', '藤原生日');
            }
            
            dayElem.addEventListener('click', () => {
                const selectedDays = calendarGrid.querySelectorAll('.calendar-day.selected');
                selectedDays.forEach(day => day.classList.remove('selected'));
                
                dayElem.classList.add('selected');
                selectedDate = new Date(currentYear, currentMonth, i);
                updateEvents();
            });
            
            calendarGrid.appendChild(dayElem);
        }
        
        const daysNeeded = 42 - (firstDayIndex + lastDate);
        for (let i = 1; i <= daysNeeded; i++) {
            const dayElem = document.createElement('div');
            dayElem.className = 'calendar-day other-month';
            dayElem.textContent = i;
            calendarGrid.appendChild(dayElem);
        }
        
        updateEvents();
    }
    
    function updateEvents() {
        calendarEvents.innerHTML = '';
        
        const eventsTitle = document.createElement('div');
        eventsTitle.className = 'calendar-events-title';
        eventsTitle.innerHTML = `
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
                <line x1="16" y1="2" x2="16" y2="6"></line>
                <line x1="8" y1="2" x2="8" y2="6"></line>
                <line x1="3" y1="10" x2="21" y2="10"></line>
            </svg>
            ${selectedDate.getMonth() + 1}月${selectedDate.getDate()}日 事件
        `;
        
        calendarEvents.appendChild(eventsTitle);
        
        const dateStr = `${selectedDate.getFullYear()}-${(selectedDate.getMonth() + 1).toString().padStart(2, '0')}-${selectedDate.getDate().toString().padStart(2, '0')}`;
        const allEvents = JSON.parse(localStorage.getItem('calendarEvents') || '{}');
        const dayEvents = allEvents[dateStr] || [];
        
        if (selectedDate.getMonth() === 10 && selectedDate.getDate() === 1) {
            const birthdayEvent = document.createElement('div');
            birthdayEvent.className = 'calendar-event birthday-event';
            
            const eventDot = document.createElement('div');
            eventDot.className = 'calendar-event-dot';
            
            const eventContent = document.createElement('div');
            eventContent.className = 'calendar-event-content';
            eventContent.textContent = '藤原生日';
            
            const eventTime = document.createElement('div');
            eventTime.className = 'calendar-event-time';
            eventTime.textContent = '全天';
            
            birthdayEvent.appendChild(eventDot);
            birthdayEvent.appendChild(eventContent);
            birthdayEvent.appendChild(eventTime);
            
            calendarEvents.appendChild(birthdayEvent);
        }
        
        if (dayEvents.length === 0 && !(selectedDate.getMonth() === 10 && selectedDate.getDate() === 1)) {
            const noEvent = document.createElement('div');
            noEvent.className = 'calendar-event';
            noEvent.textContent = '今日没有事件';
            calendarEvents.appendChild(noEvent);
        } else {
            dayEvents.forEach(event => {
                const eventElem = document.createElement('div');
                eventElem.className = 'calendar-event';
                
                const eventDot = document.createElement('div');
                eventDot.className = 'calendar-event-dot';
                
                const eventContent = document.createElement('div');
                eventContent.className = 'calendar-event-content';
                eventContent.textContent = event.title;
                
                const eventTime = document.createElement('div');
                eventTime.className = 'calendar-event-time';
                eventTime.textContent = event.time || '';
                
                eventElem.appendChild(eventDot);
                eventElem.appendChild(eventContent);
                eventElem.appendChild(eventTime);
                
                calendarEvents.appendChild(eventElem);
            });
        }
        
        const addEventBtn = document.createElement('button');
        addEventBtn.className = 'image-btn';
        addEventBtn.style.marginTop = '8px';
        addEventBtn.style.padding = '6px 12px';
        addEventBtn.style.fontSize = '0.8em';
        addEventBtn.innerHTML = `
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                <line x1="12" y1="5" x2="12" y2="19"></line>
                <line x1="5" y1="12" x2="19" y2="12"></line>
            </svg>
            添加事件
        `;
        addEventBtn.addEventListener('click', () => {
            const eventTitle = prompt('请输入事件标题:');
            if (eventTitle) {
                const eventTime = prompt('请输入事件时间 (可选):');
                
                const allEvents = JSON.parse(localStorage.getItem('calendarEvents') || '{}');
                const dateStr = `${selectedDate.getFullYear()}-${(selectedDate.getMonth() + 1).toString().padStart(2, '0')}-${selectedDate.getDate().toString().padStart(2, '0')}`;
                
                if (!allEvents[dateStr]) {
                    allEvents[dateStr] = [];
                }
                
                allEvents[dateStr].push({
                    title: eventTitle,
                    time: eventTime || ''
                });
                
                localStorage.setItem('calendarEvents', JSON.stringify(allEvents));
                updateEvents();
            }
        });
        
        const eventActions = document.createElement('div');
        eventActions.className = 'calendar-actions';
        eventActions.appendChild(addEventBtn);
        
        calendarEvents.appendChild(eventActions);
    }
    
    renderCalendar();
}

// 创建天气组件
async function createWeather(city = '北京') {
    const existingWeather = document.querySelector('.weather-container');
    if (existingWeather) {
        existingWeather.remove();
    }
    
    const weatherContainer = document.createElement('div');
    weatherContainer.className = 'weather-container';
    
    // 天气头部
    const weatherHeader = document.createElement('div');
    weatherHeader.className = 'weather-header';
    
    const weatherLocation = document.createElement('div');
    weatherLocation.className = 'weather-location';
    weatherLocation.innerHTML = `
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path>
            <circle cx="12" cy="10" r="3"></circle>
        </svg>
        ${city}
    `;
    
    const weatherRefresh = document.createElement('button');
    weatherRefresh.className = 'weather-refresh';
    weatherRefresh.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="1 4 1 10 7 10"></polyline><polyline points="23 20 23 14 17 14"></polyline><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"></path></svg>';
    weatherRefresh.addEventListener('click', () => {
        fetchWeatherData(city);
    });
    
    weatherHeader.appendChild(weatherLocation);
    weatherHeader.appendChild(weatherRefresh);
    
    // 天气内容区域
    const weatherContent = document.createElement('div');
    weatherContent.className = 'weather-content';
    weatherContent.innerHTML = '<div class="ai-output">正在获取天气数据...</div>';
    
    // 天气搜索
    const weatherSearch = document.createElement('div');
    weatherSearch.className = 'weather-search';
    
    const searchInput = document.createElement('input');
    searchInput.type = 'text';
    searchInput.placeholder = '输入城市名称';
    searchInput.value = city;
    
    const searchButton = document.createElement('button');
    searchButton.innerHTML = `
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <circle cx="11" cy="11" r="8"></circle>
            <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
        </svg>
        搜索
    `;
    searchButton.addEventListener('click', () => {
        const newCity = searchInput.value.trim();
        if (newCity) {
            fetchWeatherData(newCity);
        }
    });
    
    searchInput.addEventListener('keypress', (e) => {
        if (e.key === 'Enter') {
            const newCity = searchInput.value.trim();
            if (newCity) {
                fetchWeatherData(newCity);
            }
        }
    });
    
    weatherSearch.appendChild(searchInput);
    weatherSearch.appendChild(searchButton);
    
    weatherContainer.appendChild(weatherHeader);
    weatherContainer.appendChild(weatherContent);
    weatherContainer.appendChild(weatherSearch);
    
    terminalContent.appendChild(weatherContainer);
    
    async function fetchWeatherData(cityName) {
        try {
            weatherContent.innerHTML = '<div class="ai-output">正在获取天气数据...</div>';
            weatherLocation.innerHTML = `
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                    <path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path>
                    <circle cx="12" cy="10" r="3"></circle>
                </svg>
                ${cityName}
            `;
            
            await new Promise(resolve => setTimeout(resolve, 1000));
            
            const weatherData = {
                city: cityName,
                current: {
                    temp: Math.floor(Math.random() * 15) + 15,
                    condition: ['晴', '多云', '阴', '小雨', '中雨'][Math.floor(Math.random() * 5)],
                    humidity: Math.floor(Math.random() * 30) + 40,
                    wind: Math.floor(Math.random() * 5) + 1,
                    feelsLike: Math.floor(Math.random() * 15) + 15,
                    pressure: Math.floor(Math.random() * 100) + 1000
                },
                forecast: [
                    {
                        date: '今天',
                        high: Math.floor(Math.random() * 10) + 20,
                        low: Math.floor(Math.random() * 10) + 10,
                        condition: ['晴', '多云', '阴', '小雨', '中雨'][Math.floor(Math.random() * 5)]
                    },
                    {
                        date: '明天',
                        high: Math.floor(Math.random() * 10) + 20,
                        low: Math.floor(Math.random() * 10) + 10,
                        condition: ['晴', '多云', '阴', '小雨', '中雨'][Math.floor(Math.random() * 5)]
                    },
                    {
                        date: '后天',
                        high: Math.floor(Math.random() * 10) + 20,
                        low: Math.floor(Math.random() * 10) + 10,
                        condition: ['晴', '多云', '阴', '小雨', '中雨'][Math.floor(Math.random() * 5)]
                    },
                    {
                        date: '周四',
                        high: Math.floor(Math.random() * 10) + 20,
                        low: Math.floor(Math.random() * 10) + 10,
                        condition: ['晴', '多云', '阴', '小雨', '中雨'][Math.floor(Math.random() * 5)]
                    },
                    {
                        date: '周五',
                        high: Math.floor(Math.random() * 10) + 20,
                        low: Math.floor(Math.random() * 10) + 10,
                        condition: ['晴', '多云', '阴', '小雨', '中雨'][Math.floor(Math.random() * 5)]
                    }
                ]
            };
            
            renderWeatherData(weatherData);
        } catch (error) {
            weatherContent.innerHTML = `<div class="ai-output">获取天气数据失败: ${error.message}</div>`;
        }
    }
    
    function renderWeatherData(data) {
        function getWeatherIcon(condition) {
            switch(condition) {
                case '晴':
                    return '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg>';
                case '多云':
                    return '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 10h-1.26A8 8 0 1 0 9 20h9a5 5 0 0 0 0-10z"></path></svg>';
                case '阴':
                    return '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 3H5a2 2 0 0 1 0-4h14v4"></path><path d="M3 5v14a2 2 0 0 0 2 2h16v-4"></path><path d="M18 17a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2Z"></path></svg>';
                case '小雨':
                    return '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 16.2A4.5 4.5 0 0 0 17.5 8h-1.8A7 7 0 1 0 4 14.9"></path><path d="M16 14v6"></path><path d="M8 14v6"></path><path d="M12 16v6"></path></svg>';
                case '中雨':
                    return '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 13v8"></path><path d="M8 13v8"></path><path d="M12 15v8"></path><path d="M20 16.58A5 5 0 0 0 18 7h-1.26A8 8 0 1 1 4 15.25"></path></svg>';
                default:
                    return '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg>';
            }
        }
        
        const currentWeatherHTML = `
            <div class="weather-current">
                <div class="weather-icon">${getWeatherIcon(data.current.condition)}</div>
                <div class="weather-temp">${data.current.temp}°</div>
                <div class="weather-desc">
                    <div class="weather-condition">${data.current.condition}</div>
                    <div class="weather-feels-like">体感温度: ${data.current.feelsLike}°</div>
                </div>
            </div>
            
            <div class="weather-details">
                <div class="weather-detail">
                    <div class="weather-detail-icon">
                        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                            <path d="M12 2.69l5.66 5.66a8 8 0 1 1-11.31 0z"></path>
                        </svg>
                    </div>
                    <div class="weather-detail-content">
                        <div class="weather-detail-title">湿度</div>
                        <div class="weather-detail-value">${data.current.humidity}%</div>
                    </div>
                </div>
                <div class="weather-detail">
                    <div class="weather-detail-icon">
                        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                            <path d="M9.59 4.59A2 2 0 1 1 11 8H2m10.59 11.41A2 2 0 1 0 14 16H2m15.73-8.27A2.5 2.5 0 1 1 19.5 12H2"></path>
                        </svg>
                    </div>
                    <div class="weather-detail-content">
                        <div class="weather-detail-title">风速</div>
                        <div class="weather-detail-value">${data.current.wind} m/s</div>
                    </div>
                </div>
                <div class="weather-detail">
                    <div class="weather-detail-icon">
                        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                            <path d="M12 9v3m0 0v3m0-3h3m-3 0H9m12 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0z"></path>
                        </svg>
                    </div>
                    <div class="weather-detail-content">
                        <div class="weather-detail-title">气压</div>
                        <div class="weather-detail-value">${data.current.pressure} hPa</div>
                    </div>
                </div>
                <div class="weather-detail">
                    <div class="weather-detail-icon">
                        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                            <circle cx="12" cy="12" r="10"></circle>
                            <line x1="12" y1="8" x2="12" y2="12"></line>
                            <line x1="12" y1="16" x2="12.01" y2="16"></line>
                        </svg>
                    </div>
                    <div class="weather-detail-content">
                        <div class="weather-detail-title">更新时间</div>
                        <div class="weather-detail-value">${new Date().toLocaleTimeString()}</div>
                    </div>
                </div>
            </div>
        `;
        
        let forecastHTML = '<div class="weather-forecast">';
        
        data.forecast.forEach(day => {
            forecastHTML += `
                <div class="forecast-day">
                    <div class="forecast-date">${day.date}</div>
                    <div class="forecast-icon">${getWeatherIcon(day.condition)}</div>
                    <div class="forecast-temp">
                        <div class="forecast-high">${day.high}°</div>
                        <div class="forecast-low">${day.low}°</div>
                    </div>
                </div>
            `;
        });
        
        forecastHTML += '</div>';
        weatherContent.innerHTML = currentWeatherHTML + forecastHTML;
    }
    
    fetchWeatherData(city);
}

// 清空终端
function clearTerminal() {
    const lines = document.querySelectorAll('.code-line');
    const commandHistoryElem = document.getElementById('commandHistory');
    const calendarContainer = document.querySelector('.calendar-container');
    const weatherContainer = document.querySelector('.weather-container');
    const videoContainer = document.querySelector('.video-container');
    const imageConverter = document.querySelector('.image-converter');
    
    lines.forEach(line => {
        if (!line.classList.contains('input-line')) {
            line.remove();
        }
    });
    
    if (commandHistoryElem && commandHistoryElem.style.display === 'block') {
        terminalContent.appendChild(commandHistoryElem);
    }

    if (calendarContainer) {
        terminalContent.appendChild(calendarContainer);
    }
    
    if (weatherContainer) {
        terminalContent.appendChild(weatherContainer);
    }
    
    if (videoContainer) {
        terminalContent.appendChild(videoContainer);
    }
    
    if (imageConverter) {
        terminalContent.appendChild(imageConverter);
    }
    
    // 重置行计数器
    lineCounter = 22;
}

// 处理用户命令
function processCommand(command) {
    const parts = command.split(' ');
    const cmd = parts[0].toLowerCase();
    const args = parts.slice(1).join(' ');
    
    // 添加到历史记录
    if (commandHistory[commandHistory.length - 1] !== command) {
        commandHistory.push(command);
        if (commandHistory.length > 50) {
            commandHistory.shift();
        }
        localStorage.setItem('commandHistory', JSON.stringify(commandHistory));
        updateHistoryList();
    }
    
    // 更新命令计数
    commandCount++;
    updateCommandCount();
    
    // 显示用户输入
    addUserMessage(command);
    
    switch(cmd) {
        case 'help':
            typeAIMessage(helpText);
            break;
            
        case 'clear':
            clearTerminal();
            break;
            
        case 'video':
            if (!args) {
                addAIMessage('请提供视频URL,例如: video https://v.qq.com/x/cover/mzc00200mp8er9d.html');
            } else {
                parseVideo(args);
            }
            break;
            
        case 'ai':
            if (!args) {
                addAIMessage('请输入您想对AI说的话');
            } else if (args.startsWith('--api')) {
                // 处理API切换命令
                const apiName = args.split(' ')[1];
                if (apiName) {
                    const selectedApi = aiApis.find(api => api.name.toLowerCase().includes(apiName.toLowerCase()));
                    if (selectedApi) {
                        currentApi = selectedApi;
                        if (selectedApi.requireKey) {
                            addAIMessage(`已切换到 ${selectedApi.name},请确保已设置API密钥。如需设置密钥,请使用命令: ai --set-key [您的密钥]`);
                        } else {
                            addAIMessage(`已切换到 ${selectedApi.name}`);
                        }
                    } else {
                        addAIMessage(`未找到API: ${apiName}。可用API: ${aiApis.map(api => api.name).join(', ')}`);
                    }
                } else {
                    addAIMessage(`当前API: ${currentApi.name}。可用API: ${aiApis.map(api => api.name).join(', ')}`);
                }
            } else if (args.startsWith('--set-key')) {
                const key = args.split(' ')[1];
                if (key) {
                    localStorage.setItem('deepSeekKey', key);
                    addAIMessage('DeepSeek API密钥已设置');
                } else {
                    addAIMessage('请提供API密钥');
                }
            } else {
                sendMessage(args);
            }
            break;
            
        case 'image':
            if (!args) {
                addAIMessage('请指定目标图片格式,例如: image png');
            } else {
                const validFormats = ['jpg', 'jpeg', 'png', 'webp', 'bmp'];
                if (validFormats.includes(args.toLowerCase())) {
                    createImageConverter(args.toLowerCase());
                } else {
                    addAIMessage(`不支持的目标格式: ${args}。支持格式: ${validFormats.join(', ')}`);
                }
            }
            break;
            
        case 'history':
            toggleCommandHistory();
            break;
            
        case 'theme':
            themeToggle.click();
            break;
            
        case 'calendar':
            createCalendar();
            break;
            
        case 'weather':
            createWeather(args || '北京');
            break;
            
        default:
            if (command.startsWith('http://') || command.startsWith('https://')) {
                parseVideo(command);
            } else if (command) {
                sendMessage(command);
            }
    }
    
    historyIndex = -1;
}

async function sendMessage(message) {
    if (!message) return;
    
    const thinkingLine = document.createElement('div');
    thinkingLine.className = 'code-line ai-response fade-in';
    
    const lineNumber = document.createElement('div');
    lineNumber.className = 'line-number';
    lineNumber.textContent = lineCounter++;
    
    const lineContent = document.createElement('div');
    lineContent.className = 'line-content';
    
    const thinkingText = document.createElement('span');
    thinkingText.innerHTML = '<span class="ai-prompt">藤原:</span> <span class="ai-message">正在思考中 </span>';
    
    const thinkingAnimation = document.createElement('div');
    thinkingAnimation.className = 'thinking-animation';
    
    for (let i = 0; i < 3; i++) {
        const dot = document.createElement('span');
        thinkingAnimation.appendChild(dot);
    }
    
    lineContent.appendChild(thinkingText);
    lineContent.appendChild(thinkingAnimation);
    thinkingLine.appendChild(lineNumber);
    thinkingLine.appendChild(lineContent);
    
    terminalContent.appendChild(thinkingLine);
    terminalContent.scrollTop = terminalContent.scrollHeight;
    
    try {
        let response;
        
        if (currentApi.requireKey) {
            let apiKey = localStorage.getItem('deepSeekKey');
            if (!apiKey) {
                apiKey = prompt(`请输入${currentApi.name}的API密钥:`);
                if (!apiKey) {
                    throw new Error('未提供API密钥');
                }
                localStorage.setItem('deepSeekKey', apiKey);
            }
            
            response = await currentApi.endpoint(apiKey, message);
        } else {
            response = await currentApi.endpoint('', message);
        }
        
        thinkingLine.remove();
        
        if (response.code === 200 && response.msg) {
            await typeAIMessage(response.msg);
        } else {
            addAIMessage(`AI回复失败: ${response.msg || '未知错误'}`);
        }
    } catch (error) {
        thinkingLine.remove();
        addAIMessage(`请求失败: ${error.message}`);
    }
}

// 键盘事件处理
commandInput.addEventListener('keydown', (e) => {
    if (e.key === 'Enter') {
        const command = commandInput.value.trim();
        if (command) {
            processCommand(command);
            commandInput.value = '';
        }
    } else if (e.key === 'ArrowUp' && commandHistory.length > 0) {
        e.preventDefault();
        
        if (historyIndex < commandHistory.length - 1) {
            historyIndex++;
            commandInput.value = commandHistory[commandHistory.length - 1 - historyIndex];
        }
    } else if (e.key === 'ArrowDown' && historyIndex > -1) {
        e.preventDefault();
        
        historyIndex--;
        if (historyIndex === -1) {
            commandInput.value = '';
        } else {
            commandInput.value = commandHistory[commandHistory.length - 1 - historyIndex];
        }
    }
});

// 快捷命令点击事件
document.querySelectorAll('.quick-cmd').forEach(cmd => {
    cmd.addEventListener('click', () => {
        const command = cmd.getAttribute('data-cmd');
        commandInput.value = command;
        commandInput.focus();
    });
});

// 命令面板关闭按钮
closeCommandPaletteBtn.addEventListener('click', () => {
    commandPalette.style.display = 'none';
});

// 初始化应用
init();
</script>
</body>
</html>
        
预览
控制台