feat(desktop): wire template welcome_message + quick_commands to chat UI
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
- Add welcomeMessage/quickCommands fields to Clone interface - Persist template welcome/quick data via updateClone after creation - FirstConversationPrompt: prefer template-provided welcome message over dynamically generated one - FirstConversationPrompt: render template quick_commands as chips instead of hardcoded QUICK_ACTIONS when available - Tighten assign/unassign template endpoint permissions from model:read to relay:use (self-service operation for all authenticated users)
This commit is contained in:
@@ -148,7 +148,7 @@ pub async fn assign_template(
|
|||||||
Extension(ctx): Extension<AuthContext>,
|
Extension(ctx): Extension<AuthContext>,
|
||||||
Json(req): Json<AssignTemplateRequest>,
|
Json(req): Json<AssignTemplateRequest>,
|
||||||
) -> SaasResult<Json<AgentTemplateInfo>> {
|
) -> SaasResult<Json<AgentTemplateInfo>> {
|
||||||
check_permission(&ctx, "model:read")?;
|
check_permission(&ctx, "relay:use")?;
|
||||||
|
|
||||||
let result = service::assign_template_to_account(
|
let result = service::assign_template_to_account(
|
||||||
&state.db, &ctx.account_id, &req.template_id,
|
&state.db, &ctx.account_id, &req.template_id,
|
||||||
@@ -174,7 +174,7 @@ pub async fn unassign_template(
|
|||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Extension(ctx): Extension<AuthContext>,
|
Extension(ctx): Extension<AuthContext>,
|
||||||
) -> SaasResult<Json<serde_json::Value>> {
|
) -> SaasResult<Json<serde_json::Value>> {
|
||||||
check_permission(&ctx, "model:read")?;
|
check_permission(&ctx, "relay:use")?;
|
||||||
service::unassign_template(&state.db, &ctx.account_id).await?;
|
service::unassign_template(&state.db, &ctx.account_id).await?;
|
||||||
Ok(Json(serde_json::json!({"ok": true})))
|
Ok(Json(serde_json::json!({"ok": true})))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
Microscope,
|
Microscope,
|
||||||
Layers,
|
Layers,
|
||||||
GraduationCap,
|
GraduationCap,
|
||||||
|
MessageSquare,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { cn } from '../lib/utils';
|
import { cn } from '../lib/utils';
|
||||||
import {
|
import {
|
||||||
@@ -63,7 +64,9 @@ export function FirstConversationPrompt({
|
|||||||
ultra: '多代理协作,全能力调度',
|
ultra: '多代理协作,全能力调度',
|
||||||
};
|
};
|
||||||
|
|
||||||
const welcomeMessage = generateWelcomeMessage({
|
// Use template-provided welcome message if available, otherwise generate dynamically
|
||||||
|
const welcomeMessage = clone.welcomeMessage
|
||||||
|
|| generateWelcomeMessage({
|
||||||
userName: clone.userName,
|
userName: clone.userName,
|
||||||
agentName: clone.nickname || clone.name,
|
agentName: clone.nickname || clone.name,
|
||||||
emoji: clone.emoji,
|
emoji: clone.emoji,
|
||||||
@@ -149,9 +152,31 @@ export function FirstConversationPrompt({
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Quick action chips — DeerFlow-style horizontal colored pills */}
|
{/* Quick action chips — template-provided or DeerFlow-style defaults */}
|
||||||
<div className="flex items-center justify-center gap-2 flex-wrap">
|
<div className="flex items-center justify-center gap-2 flex-wrap">
|
||||||
{QUICK_ACTIONS.map((action, index) => {
|
{clone.quickCommands && clone.quickCommands.length > 0
|
||||||
|
? clone.quickCommands.map((cmd, index) => (
|
||||||
|
<motion.button
|
||||||
|
key={cmd.label}
|
||||||
|
initial={{ opacity: 0, y: 8 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
transition={{ delay: 0.3 + index * 0.05, duration: 0.2 }}
|
||||||
|
onClick={() => onSelectSuggestion?.(cmd.command)}
|
||||||
|
className={cn(
|
||||||
|
'flex items-center gap-2 px-4 py-2',
|
||||||
|
'bg-white dark:bg-gray-800',
|
||||||
|
'border border-gray-200 dark:border-gray-700',
|
||||||
|
'rounded-full text-sm text-gray-600 dark:text-gray-300',
|
||||||
|
'hover:border-gray-300 dark:hover:border-gray-600',
|
||||||
|
'hover:bg-gray-50 dark:hover:bg-gray-750',
|
||||||
|
'transition-all duration-150'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<MessageSquare className="w-4 h-4 text-primary" />
|
||||||
|
<span>{cmd.label}</span>
|
||||||
|
</motion.button>
|
||||||
|
))
|
||||||
|
: QUICK_ACTIONS.map((action, index) => {
|
||||||
const ActionIcon = action.icon;
|
const ActionIcon = action.icon;
|
||||||
return (
|
return (
|
||||||
<motion.button
|
<motion.button
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ export interface Clone {
|
|||||||
notes?: string; // 用户备注
|
notes?: string; // 用户备注
|
||||||
onboardingCompleted?: boolean; // 是否完成首次引导
|
onboardingCompleted?: boolean; // 是否完成首次引导
|
||||||
source_template_id?: string; // 模板来源 ID
|
source_template_id?: string; // 模板来源 ID
|
||||||
|
welcomeMessage?: string; // 模板预设欢迎语
|
||||||
|
quickCommands?: Array<{ label: string; command: string }>; // 模板预设快捷命令
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UsageStats {
|
export interface UsageStats {
|
||||||
@@ -226,10 +228,29 @@ export const useAgentStore = create<AgentStore>((set, get) => ({
|
|||||||
console.warn('Failed to persist temperature/max_tokens:', e);
|
console.warn('Failed to persist temperature/max_tokens:', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Persist welcome_message + quick_commands as clone metadata
|
||||||
|
if (template.welcome_message || (template.quick_commands && template.quick_commands.length > 0)) {
|
||||||
|
try {
|
||||||
|
await client.updateClone(cloneId, {
|
||||||
|
welcomeMessage: template.welcome_message || '',
|
||||||
|
quickCommands: template.quick_commands || [],
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Failed to persist welcome_message/quick_commands:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await get().loadClones();
|
await get().loadClones();
|
||||||
return result?.clone;
|
|
||||||
|
// Merge template welcome/quick data into the returned clone for immediate use
|
||||||
|
const createdClone = result?.clone as Record<string, unknown> | undefined;
|
||||||
|
if (createdClone) {
|
||||||
|
if (template.welcome_message) createdClone.welcomeMessage = template.welcome_message;
|
||||||
|
if (template.quick_commands?.length) createdClone.quickCommands = template.quick_commands;
|
||||||
|
}
|
||||||
|
return createdClone as Clone | undefined;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
set({ error: String(error) });
|
set({ error: String(error) });
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|||||||
Reference in New Issue
Block a user