Преглед на файлове

feat: 更新 README 和 ProductionView 组件,增强用户界面和交互体验

JJSun преди 3 месеца
родител
ревизия
8e9a1f5c81

+ 28 - 2
Co-creation-projects/JJason-DeepCastAgent/README.md

@@ -64,9 +64,14 @@ DeepCast 旨在解决现代人在海量碎片化信息中难以获取深度知
 │  └─ requirements.txt             # pip 依赖清单
 ├─ frontend/                       # 前端应用(Vue 3 + Vite + TypeScript)
 │  ├─ src/
-│  │  ├─ App.vue                   #   根组件(主界面
+│  │  ├─ App.vue                   #   根组件(状态管理 & 事件路由
 │  │  ├─ main.ts                   #   Vue 应用入口
-│  │  ├─ style.css                 #   全局样式(Tailwind CSS)
+│  │  ├─ style.css                 #   全局样式(Tailwind CSS + DaisyUI)
+│  │  ├─ components/               #   页面组件
+│  │  │  ├─ SetupView.vue          #     主题输入 & 启动界面
+│  │  │  ├─ ProductionView.vue     #     制作流程(进度步骤 + 终端日志)
+│  │  │  ├─ PlayerView.vue         #     黑胶唱片播放器 & 报告阅读器
+│  │  │  └─ TerminalLog.vue        #     macOS 风格实时日志终端
 │  │  └─ services/
 │  │     └─ api.ts                 #   SSE 流式通信(fetch + ReadableStream)
 │  ├─ index.html                   # HTML 入口
@@ -151,6 +156,8 @@ npm run dev
 
 ## 📖 使用示例
 
+### 通过 Web 界面
+
 在前端界面输入你想研究的主题,例如:
 
 > "量子计算在 2024 年有哪些重大突破?"
@@ -163,6 +170,25 @@ DeepCast 将依次执行:
 4. **生成脚本**:将报告转化为 Xiayu 和 Liwa 的对话。
 5. **合成音频**:调用 TTS 生成并拼接成最终的 MP3 文件。
 
+### 通过 Python 代码
+
+```python
+from agent import DeepResearchAgent
+from config import Configuration
+
+config = Configuration.from_env()
+agent = DeepResearchAgent(config=config)
+
+# 流式模式 —— 逐步获取每个阶段的进度事件
+for event in agent.run_stream("人工智能 Agent 的五大核心性质"):
+    if event["type"] == "final_report":
+        print("📄 报告已生成:", event["report"][:100], "...")
+    elif event["type"] == "podcast_ready":
+        print("🎙️ 播客已就绪:", event["file"])
+    elif event["type"] == "log":
+        print(event["message"])
+```
+
 ## 🎯 项目亮点
 
 - **从文字到声音的跨越**:不仅提供干货,更提供沉浸式的听觉体验。

+ 70 - 8
Co-creation-projects/JJason-DeepCastAgent/frontend/src/components/ProductionView.vue

@@ -92,9 +92,9 @@
                   <div class="pipeline-step-content">
                     <div class="flex items-center gap-2">
                       <span class="text-base" :class="{ 'animate-float': isStepActive(step.id) }">{{ step.icon }}</span>
-                      <span class="text-sm font-semibold" :class="isStepActive(step.id) ? 'text-white' : isStepCompleted(step.id) ? 'text-gray-300' : 'text-gray-500'">{{ step.label }}</span>
+                      <span class="step-label text-sm font-semibold" :class="isStepActive(step.id) ? 'text-white' : isStepCompleted(step.id) ? 'text-gray-300' : 'text-gray-500'">{{ step.label }}</span>
                     </div>
-                    <p class="text-[11px] mt-0.5 ml-7" :class="isStepActive(step.id) ? 'text-gray-400' : 'text-gray-600'">{{ step.desc }}</p>
+                    <p class="step-desc text-[11px] mt-0.5 ml-7" :class="isStepActive(step.id) ? 'text-gray-400' : 'text-gray-600'">{{ step.desc }}</p>
                   </div>
                 </div>
               </div>
@@ -261,7 +261,7 @@ function isStepPending(stepId: ProductionStage) {
   background: linear-gradient(90deg, #3b82f6, #8b5cf6, #06b6d4);
   background-size: 200% 100%;
   animation: shimmer 2s ease-in-out infinite;
-  transition: width 0.8s cubic-bezier(0.4, 0, 0.2, 1);
+  transition: width 1.2s cubic-bezier(0.22, 1, 0.36, 1);
   border-radius: 0 2px 2px 0;
 }
 
@@ -309,13 +309,22 @@ function isStepPending(stepId: ProductionStage) {
   padding: 10px 8px;
   border-radius: 10px;
   position: relative;
-  transition: all 0.3s ease;
+  border: 1px solid transparent;
+  margin: 0;
+  transition:
+    background 0.6s cubic-bezier(0.4, 0, 0.2, 1),
+    border-color 0.6s cubic-bezier(0.4, 0, 0.2, 1),
+    opacity 0.6s cubic-bezier(0.4, 0, 0.2, 1),
+    padding 0.5s cubic-bezier(0.4, 0, 0.2, 1),
+    margin 0.5s cubic-bezier(0.4, 0, 0.2, 1),
+    transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
 }
 .pipeline-step--active {
   background: rgba(59, 130, 246, 0.08);
-  border: 1px solid rgba(59, 130, 246, 0.15);
+  border-color: rgba(59, 130, 246, 0.15);
   margin: 0 -4px;
   padding: 10px 12px;
+  transform: scale(1.02);
 }
 .pipeline-step--completed {
   opacity: 0.85;
@@ -328,14 +337,34 @@ function isStepPending(stepId: ProductionStage) {
   top: 38px;
   width: 2px;
   height: calc(100% - 10px);
-  background: rgba(255, 255, 255, 0.06);
   border-radius: 1px;
   z-index: 1;
+  overflow: hidden;
+}
+/* Use a pseudo-element to animate the fill from top to bottom */
+.pipeline-connector::before {
+  content: '';
+  position: absolute;
+  inset: 0;
+  background: rgba(255, 255, 255, 0.06);
+  transition: opacity 0.6s ease;
 }
-.pipeline-connector--completed {
+.pipeline-connector::after {
+  content: '';
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 0%;
   background: linear-gradient(180deg, #3b82f6, #8b5cf6);
+  border-radius: 1px;
+  transition: height 0.8s cubic-bezier(0.4, 0, 0.2, 1);
+}
+.pipeline-connector--completed::after {
+  height: 100%;
 }
-.pipeline-connector--active {
+.pipeline-connector--active::after {
+  height: 60%;
   background: linear-gradient(180deg, #3b82f6 0%, rgba(59, 130, 246, 0.15) 100%);
 }
 
@@ -351,19 +380,33 @@ function isStepPending(stepId: ProductionStage) {
   position: relative;
   z-index: 2;
   margin-top: 1px;
+  background: rgba(255, 255, 255, 0.04);
+  border: 1.5px solid rgba(255, 255, 255, 0.1);
+  box-shadow: none;
+  transition:
+    background 0.5s cubic-bezier(0.4, 0, 0.2, 1),
+    border-color 0.5s cubic-bezier(0.4, 0, 0.2, 1),
+    border-width 0.3s ease,
+    box-shadow 0.6s cubic-bezier(0.4, 0, 0.2, 1),
+    transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
 }
 .pipeline-step--completed .pipeline-indicator {
   background: linear-gradient(135deg, #3b82f6, #8b5cf6);
+  border-color: transparent;
   box-shadow: 0 2px 8px rgba(59, 130, 246, 0.3);
+  transform: scale(1);
+  animation: indicator-pop 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
 }
 .pipeline-step--active .pipeline-indicator {
   background: rgba(59, 130, 246, 0.15);
   border: 2px solid #3b82f6;
   box-shadow: 0 0 12px rgba(59, 130, 246, 0.25);
+  animation: indicator-glow-in 0.6s ease;
 }
 .pipeline-step--pending .pipeline-indicator {
   background: rgba(255, 255, 255, 0.04);
   border: 1.5px solid rgba(255, 255, 255, 0.1);
+  box-shadow: none;
 }
 
 /* ── Active Pulse Dot ── */
@@ -381,6 +424,9 @@ function isStepPending(stepId: ProductionStage) {
   flex: 1;
   min-width: 0;
 }
+.step-label, .step-desc {
+  transition: color 0.5s ease;
+}
 
 /* ── Status Chip ── */
 .pipeline-status-chip {
@@ -392,6 +438,10 @@ function isStepPending(stepId: ProductionStage) {
   background: rgba(59, 130, 246, 0.08);
   border: 1px solid rgba(59, 130, 246, 0.12);
   color: #93c5fd;
+  transition:
+    background 0.5s ease,
+    border-color 0.5s ease,
+    color 0.5s ease;
 }
 .pipeline-status-chip--done {
   background: rgba(16, 185, 129, 0.08);
@@ -496,4 +546,16 @@ function isStepPending(stepId: ProductionStage) {
   0% { background-position: 200% 0; }
   100% { background-position: -200% 0; }
 }
+
+@keyframes indicator-pop {
+  0% { transform: scale(0.6); opacity: 0.5; }
+  50% { transform: scale(1.2); }
+  100% { transform: scale(1); opacity: 1; }
+}
+
+@keyframes indicator-glow-in {
+  0% { box-shadow: 0 0 0 rgba(59, 130, 246, 0); }
+  50% { box-shadow: 0 0 20px rgba(59, 130, 246, 0.4); }
+  100% { box-shadow: 0 0 12px rgba(59, 130, 246, 0.25); }
+}
 </style>