fix(desktop): DeerFlow UI — ChatArea refactor + ai-elements + dead CSS cleanup

ChatArea retry button uses setInput instead of direct sendToGateway,
fix bootstrap spinner stuck for non-logged-in users,
remove dead CSS (aurora-title/sidebar-open/quick-action-chips),
add ai components (ReasoningBlock/StreamingText/ChatMode/ModelSelector/TaskProgress),
add ClassroomPlayer + ResizableChatLayout + artifact panel

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
iven
2026-04-02 19:24:44 +08:00
parent d40c4605b2
commit 28299807b6
70 changed files with 4938 additions and 618 deletions

View File

@@ -44,13 +44,7 @@ impl Worker for GenerateEmbeddingWorker {
}
};
// 2. 删除旧分块full refresh on each update
sqlx::query("DELETE FROM knowledge_chunks WHERE item_id = $1")
.bind(&args.item_id)
.execute(db)
.await?;
// 3. 分块
// 2. 分块
let chunks = crate::knowledge::service::chunk_content(&content, 512, 64);
if chunks.is_empty() {
@@ -58,13 +52,32 @@ impl Worker for GenerateEmbeddingWorker {
return Ok(());
}
// 4. 写入分块(带关键词继承
// 3. 在事务中删除旧分块 + 插入新分块(防止并发竞争条件
let mut tx = db.begin().await?;
// 锁定条目行防止并发 worker 同时处理同一条目
let locked: Option<(String,)> = sqlx::query_as(
"SELECT id FROM knowledge_items WHERE id = $1 FOR UPDATE"
)
.bind(&args.item_id)
.fetch_optional(&mut *tx)
.await?;
if locked.is_none() {
tx.rollback().await?;
tracing::warn!("GenerateEmbedding: item {} was deleted during processing", args.item_id);
return Ok(());
}
sqlx::query("DELETE FROM knowledge_chunks WHERE item_id = $1")
.bind(&args.item_id)
.execute(&mut *tx)
.await?;
for (idx, chunk) in chunks.iter().enumerate() {
let chunk_id = uuid::Uuid::new_v4().to_string();
// 为每个 chunk 提取额外关键词(简单策略:标题 + 继承关键词)
let mut chunk_keywords = keywords.clone();
// 从 chunk 内容提取高频词作为补充关键词
extract_chunk_keywords(chunk, &mut chunk_keywords);
sqlx::query(
@@ -76,10 +89,12 @@ impl Worker for GenerateEmbeddingWorker {
.bind(idx as i32)
.bind(chunk)
.bind(&chunk_keywords)
.execute(db)
.execute(&mut *tx)
.await?;
}
tx.commit().await?;
tracing::info!(
"GenerateEmbedding: item '{}' → {} chunks (keywords: {})",
title,

View File

@@ -8,7 +8,8 @@ use super::Worker;
#[derive(Debug, Serialize, Deserialize)]
pub struct UpdateLastUsedArgs {
pub token_id: String,
/// token_hash 用于 WHERE 条件匹配
pub token_hash: String,
}
pub struct UpdateLastUsedWorker;
@@ -23,9 +24,9 @@ impl Worker for UpdateLastUsedWorker {
async fn perform(&self, db: &PgPool, args: Self::Args) -> SaasResult<()> {
let now = chrono::Utc::now().to_rfc3339();
sqlx::query("UPDATE api_tokens SET last_used_at = $1 WHERE id = $2")
sqlx::query("UPDATE api_tokens SET last_used_at = $1 WHERE token_hash = $2")
.bind(&now)
.bind(&args.token_id)
.bind(&args.token_hash)
.execute(db)
.await?;
Ok(())