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

@@ -17,7 +17,6 @@
* - saas-errors.ts — SaaSApiError class
* - saas-session.ts — session persistence (load/save/clear)
* - saas-auth.ts — login/register/TOTP methods (mixin)
* - saas-admin.ts — admin panel API methods (mixin)
* - saas-relay.ts — relay tasks, chat completion, usage (mixin)
* - saas-prompt.ts — prompt OTA methods (mixin)
* - saas-telemetry.ts — telemetry reporting methods (mixin)
@@ -96,26 +95,6 @@ import type {
SaaSErrorResponse,
RelayTaskInfo,
UsageStats,
ProviderInfo,
CreateProviderRequest,
UpdateProviderRequest,
ModelInfo,
CreateModelRequest,
UpdateModelRequest,
AccountApiKeyInfo,
CreateApiKeyRequest,
AccountPublic,
UpdateAccountRequest,
PaginatedResponse,
TokenInfo,
CreateTokenRequest,
OperationLogInfo,
DashboardStats,
RoleInfo,
CreateRoleRequest,
UpdateRoleRequest,
PermissionTemplate,
CreateTemplateRequest,
PromptCheckResult,
PromptTemplateInfo,
PromptVersionInfo,
@@ -128,7 +107,7 @@ import { createLogger } from './logger';
const saasLog = createLogger('saas-client');
import { installAuthMethods } from './saas-auth';
import { installAdminMethods } from './saas-admin';
import { installRelayMethods } from './saas-relay';
import { installPromptMethods } from './saas-prompt';
import { installTelemetryMethods } from './saas-telemetry';
@@ -140,6 +119,25 @@ export class SaaSClient {
private baseUrl: string;
private token: string | null = null;
/**
* Refresh mutex: shared Promise to prevent concurrent token refresh.
* When multiple requests hit 401 simultaneously, they all await the same
* refresh Promise instead of triggering N parallel refresh calls.
*/
private _refreshPromise: Promise<string> | null = null;
/**
* Thread-safe token refresh — coalesces concurrent refresh attempts into one.
* First caller triggers the actual refresh; subsequent callers await the same Promise.
*/
async refreshMutex(): Promise<string> {
if (this._refreshPromise) return this._refreshPromise;
this._refreshPromise = this.refreshToken().finally(() => {
this._refreshPromise = null;
});
return this._refreshPromise;
}
constructor(baseUrl: string) {
this.baseUrl = baseUrl.replace(/\/+$/, '');
}
@@ -237,7 +235,7 @@ export class SaaSClient {
// 401: 尝试刷新 Token 后重试 (防止递归)
if (response.status === 401 && !this._isAuthEndpoint(path) && !_isRefreshRetry) {
try {
const newToken = await this.refreshToken();
const newToken = await this.refreshMutex();
if (newToken) {
return this.request<T>(method, path, body, timeoutMs, true);
}
@@ -394,7 +392,7 @@ export class SaaSClient {
* Used for template selection during onboarding.
*/
async fetchAvailableTemplates(): Promise<AgentTemplateAvailable[]> {
return this.request<AgentTemplateAvailable[]>('GET', '/agent-templates/available');
return this.request<AgentTemplateAvailable[]>('GET', '/api/v1/agent-templates/available');
}
/**
@@ -402,13 +400,12 @@ export class SaaSClient {
* Returns all fields needed to create an agent from template.
*/
async fetchTemplateFull(id: string): Promise<AgentTemplateFull> {
return this.request<AgentTemplateFull>('GET', `/agent-templates/${id}/full`);
return this.request<AgentTemplateFull>('GET', `/api/v1/agent-templates/${id}/full`);
}
}
// === Install mixin methods ===
installAuthMethods(SaaSClient);
installAdminMethods(SaaSClient);
installRelayMethods(SaaSClient);
installPromptMethods(SaaSClient);
installTelemetryMethods(SaaSClient);
@@ -429,57 +426,6 @@ export interface SaaSClient {
verifyTotp(code: string): Promise<TotpResultResponse>;
disableTotp(password: string): Promise<TotpResultResponse>;
// --- Admin: Providers (saas-admin.ts) ---
listProviders(): Promise<ProviderInfo[]>;
getProvider(id: string): Promise<ProviderInfo>;
createProvider(data: CreateProviderRequest): Promise<ProviderInfo>;
updateProvider(id: string, data: UpdateProviderRequest): Promise<ProviderInfo>;
deleteProvider(id: string): Promise<void>;
// --- Admin: Models (saas-admin.ts) ---
listModelsAdmin(providerId?: string): Promise<ModelInfo[]>;
getModel(id: string): Promise<ModelInfo>;
createModel(data: CreateModelRequest): Promise<ModelInfo>;
updateModel(id: string, data: UpdateModelRequest): Promise<ModelInfo>;
deleteModel(id: string): Promise<void>;
// --- Admin: API Keys (saas-admin.ts) ---
listApiKeys(providerId?: string): Promise<AccountApiKeyInfo[]>;
createApiKey(data: CreateApiKeyRequest): Promise<AccountApiKeyInfo>;
rotateApiKey(id: string, newKeyValue: string): Promise<void>;
revokeApiKey(id: string): Promise<void>;
// --- Admin: Accounts (saas-admin.ts) ---
listAccounts(params?: { page?: number; page_size?: number; role?: string; status?: string; search?: string }): Promise<PaginatedResponse<AccountPublic>>;
getAccount(id: string): Promise<AccountPublic>;
updateAccount(id: string, data: UpdateAccountRequest): Promise<AccountPublic>;
updateAccountStatus(id: string, status: 'active' | 'disabled' | 'suspended'): Promise<void>;
// --- Admin: Tokens (saas-admin.ts) ---
listTokens(): Promise<TokenInfo[]>;
createToken(data: CreateTokenRequest): Promise<TokenInfo>;
revokeToken(id: string): Promise<void>;
// --- Admin: Logs (saas-admin.ts) ---
listOperationLogs(params?: { page?: number; page_size?: number }): Promise<OperationLogInfo[]>;
// --- Admin: Dashboard (saas-admin.ts) ---
getDashboardStats(): Promise<DashboardStats>;
// --- Admin: Roles (saas-admin.ts) ---
listRoles(): Promise<RoleInfo[]>;
getRole(id: string): Promise<RoleInfo>;
createRole(data: CreateRoleRequest): Promise<RoleInfo>;
updateRole(id: string, data: UpdateRoleRequest): Promise<RoleInfo>;
deleteRole(id: string): Promise<void>;
// --- Admin: Permission Templates (saas-admin.ts) ---
listPermissionTemplates(): Promise<PermissionTemplate[]>;
getPermissionTemplate(id: string): Promise<PermissionTemplate>;
createPermissionTemplate(data: CreateTemplateRequest): Promise<PermissionTemplate>;
deletePermissionTemplate(id: string): Promise<void>;
applyPermissionTemplate(templateId: string, accountIds: string[]): Promise<{ ok: boolean; applied_count: number }>;
// --- Relay (saas-relay.ts) ---
listRelayTasks(query?: { status?: string; page?: number; page_size?: number }): Promise<RelayTaskInfo[]>;
getRelayTask(taskId: string): Promise<RelayTaskInfo>;