feat: Add AI Assist module - Conversational intelligence with specialized agent switching - Real-time preview for code, design, content, and SEO - Multi-agent routing logic (Content, SEO, SMM, PM, Code, Design) - High-end UI with chat thread and live preview sandbox - Integrated with Ollama, Qwen, and Z.AI services
This commit is contained in:
@@ -10,6 +10,7 @@ import UXDesignerPrompt from "@/components/UXDesignerPrompt";
|
||||
import SlidesGenerator from "@/components/SlidesGenerator";
|
||||
import GoogleAdsGenerator from "@/components/GoogleAdsGenerator";
|
||||
import MarketResearcher from "@/components/MarketResearcher";
|
||||
import AIAssist from "@/components/AIAssist";
|
||||
import HistoryPanel from "@/components/HistoryPanel";
|
||||
import SettingsPanel from "@/components/SettingsPanel";
|
||||
import modelAdapter from "@/lib/services/adapter-instance";
|
||||
@@ -38,6 +39,8 @@ export default function Home() {
|
||||
return <GoogleAdsGenerator />;
|
||||
case "market-research":
|
||||
return <MarketResearcher />;
|
||||
case "ai-assist":
|
||||
return <AIAssist />;
|
||||
case "history":
|
||||
return <HistoryPanel />;
|
||||
case "settings":
|
||||
|
||||
386
components/AIAssist.tsx
Normal file
386
components/AIAssist.tsx
Normal file
@@ -0,0 +1,386 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useRef, useEffect } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Card, CardHeader, CardTitle, CardDescription, CardContent } from "@/components/ui/card";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import useStore from "@/lib/store";
|
||||
import { translations } from "@/lib/i18n/translations";
|
||||
import modelAdapter from "@/lib/services/adapter-instance";
|
||||
import {
|
||||
MessageSquare, Send, Sparkles, Brain, Cpu, Code2, Palette, FileText, Search,
|
||||
BarChart, Rocket, Terminal, Eye, History, Trash2, Loader2, Bot, User,
|
||||
Settings, Layers, AppWindow, Smartphone, Monitor, X
|
||||
} from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { AIAssistMessage } from "@/types";
|
||||
|
||||
const AGENTS = [
|
||||
{ id: "general", label: "General Intel", icon: Bot, color: "slate" },
|
||||
{ id: "content", label: "Content Optimization", icon: FileText, color: "amber" },
|
||||
{ id: "seo", label: "SEO Analyst", icon: Search, color: "emerald" },
|
||||
{ id: "smm", label: "SMM Strategy", icon: BarChart, color: "pink" },
|
||||
{ id: "pm", label: "Project Manager", icon: Rocket, color: "indigo" },
|
||||
{ id: "code", label: "Code Architect", icon: Terminal, color: "violet" },
|
||||
{ id: "design", label: "UI/UX Designer", icon: Palette, color: "orange" },
|
||||
{ id: "web", label: "Web Dev Preview", icon: Monitor, color: "blue" },
|
||||
{ id: "app", label: "App Dev Preview", icon: Smartphone, color: "cyan" }
|
||||
];
|
||||
|
||||
const AIAssist = () => {
|
||||
const { language, selectedProvider, selectedModels, apiKeys, aiAssistHistory, setAIAssistHistory } = useStore();
|
||||
const t = translations[language].aiAssist;
|
||||
const common = translations[language].common;
|
||||
|
||||
const [input, setInput] = useState("");
|
||||
const [isProcessing, setIsProcessing] = useState(false);
|
||||
const [currentAgent, setCurrentAgent] = useState("general");
|
||||
const [activeTab, setActiveTab] = useState("chat");
|
||||
const [previewData, setPreviewData] = useState<{ type: string; data: string; language?: string } | null>(null);
|
||||
|
||||
const scrollRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (scrollRef.current) {
|
||||
scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
|
||||
}
|
||||
}, [aiAssistHistory]);
|
||||
|
||||
const handleSendMessage = async () => {
|
||||
if (!input.trim() || isProcessing) return;
|
||||
|
||||
const userMessage: AIAssistMessage = {
|
||||
role: "user",
|
||||
content: input,
|
||||
timestamp: new Date()
|
||||
};
|
||||
|
||||
setAIAssistHistory(prev => [...prev, userMessage]);
|
||||
setInput("");
|
||||
setIsProcessing(true);
|
||||
|
||||
try {
|
||||
const apiKey = apiKeys[selectedProvider];
|
||||
const isQwenOAuth = selectedProvider === "qwen" && modelAdapter.hasQwenAuth();
|
||||
|
||||
if (!isQwenOAuth && (!apiKey || !apiKey.trim())) {
|
||||
throw new Error(`Please configure your ${selectedProvider.toUpperCase()} API key in Settings`);
|
||||
}
|
||||
|
||||
// Call model adapter for AI Assist
|
||||
// Note: We'll implement generateAIAssist in model-adapter.ts next
|
||||
const result = await modelAdapter.generateAIAssist({
|
||||
messages: aiAssistHistory.concat(userMessage),
|
||||
currentAgent
|
||||
}, selectedProvider, selectedModels[selectedProvider]);
|
||||
|
||||
if (result.success && result.data) {
|
||||
try {
|
||||
// Expecting a structured response with possible agent switch and preview
|
||||
const cleanJson = result.data.replace(/```json\s*([\s\S]*?)\s*```/i, '$1').trim();
|
||||
const parsed = JSON.parse(cleanJson);
|
||||
|
||||
const assistantMessage: AIAssistMessage = {
|
||||
role: "assistant",
|
||||
content: parsed.content,
|
||||
agent: parsed.agent || currentAgent,
|
||||
preview: parsed.preview,
|
||||
timestamp: new Date()
|
||||
};
|
||||
|
||||
if (parsed.agent && parsed.agent !== currentAgent) {
|
||||
setCurrentAgent(parsed.agent);
|
||||
}
|
||||
|
||||
if (parsed.preview) {
|
||||
setPreviewData(parsed.preview);
|
||||
setActiveTab("preview");
|
||||
}
|
||||
|
||||
setAIAssistHistory(prev => [...prev, assistantMessage]);
|
||||
} catch (e) {
|
||||
// Fallback to plain text if JSON parsing fails
|
||||
const assistantMessage: AIAssistMessage = {
|
||||
role: "assistant",
|
||||
content: result.data,
|
||||
agent: currentAgent,
|
||||
timestamp: new Date()
|
||||
};
|
||||
setAIAssistHistory(prev => [...prev, assistantMessage]);
|
||||
}
|
||||
} else {
|
||||
throw new Error(result.error || "Failed to get response");
|
||||
}
|
||||
} catch (err) {
|
||||
const errorMessage: AIAssistMessage = {
|
||||
role: "system",
|
||||
content: err instanceof Error ? err.message : "An unexpected error occurred",
|
||||
timestamp: new Date()
|
||||
};
|
||||
setAIAssistHistory(prev => [...prev, errorMessage]);
|
||||
} finally {
|
||||
setIsProcessing(false);
|
||||
}
|
||||
};
|
||||
|
||||
const clearHistory = () => {
|
||||
setAIAssistHistory([]);
|
||||
setPreviewData(null);
|
||||
setActiveTab("chat");
|
||||
setCurrentAgent("general");
|
||||
};
|
||||
|
||||
const renderPreview = () => {
|
||||
if (!previewData) return (
|
||||
<div className="flex flex-col items-center justify-center h-full text-slate-400 gap-4">
|
||||
<Eye className="h-12 w-12 opacity-20" />
|
||||
<p className="text-sm font-medium italic">No active preview to display</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
switch (previewData.type) {
|
||||
case "code":
|
||||
case "web":
|
||||
case "app":
|
||||
return (
|
||||
<div className="h-full flex flex-col gap-4">
|
||||
<div className="flex items-center justify-between px-4 py-2 bg-slate-900 text-white rounded-t-xl">
|
||||
<div className="flex items-center gap-2">
|
||||
<Terminal className="h-3.5 w-3.5 text-indigo-400" />
|
||||
<span className="text-[10px] font-black uppercase tracking-widest">Live Execution Sandbox</span>
|
||||
</div>
|
||||
<Badge variant="outline" className="text-[8px] border-slate-700 text-slate-400 uppercase">Secure Booted</Badge>
|
||||
</div>
|
||||
<div className="flex-1 bg-slate-950 rounded-b-xl overflow-hidden p-6 font-mono text-sm relative group">
|
||||
<pre className="text-emerald-400 whitespace-pre-wrap">{previewData.data}</pre>
|
||||
<div className="absolute inset-0 bg-slate-900/40 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center backdrop-blur-[2px]">
|
||||
<Button size="sm" className="bg-white text-slate-900 hover:bg-slate-100 font-black uppercase tracking-widest text-[10px]">Execute Preview</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
case "design":
|
||||
return (
|
||||
<div className="h-full flex flex-col gap-4">
|
||||
<div className="flex items-center justify-between px-4 py-2 bg-slate-900 text-white rounded-t-xl">
|
||||
<div className="flex items-center gap-2">
|
||||
<Palette className="h-3.5 w-3.5 text-orange-400" />
|
||||
<span className="text-[10px] font-black uppercase tracking-widest">UI Layout Preview</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 bg-slate-50 border-2 border-dashed border-slate-200 rounded-b-xl flex items-center justify-center p-8">
|
||||
<div className="max-w-md w-full p-6 bg-white rounded-2xl shadow-xl border border-slate-100 animate-in zoom-in-95 duration-500">
|
||||
{previewData.data}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<div className="h-full overflow-y-auto p-6 bg-white rounded-xl border border-slate-200 prose prose-slate max-w-none">
|
||||
<div className="whitespace-pre-wrap font-medium text-slate-700 leading-relaxed">
|
||||
{previewData.data}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="h-[calc(100vh-140px)] flex flex-col gap-6 animate-in fade-in slide-in-from-bottom-4 duration-700">
|
||||
{/* Header Section */}
|
||||
<div className="flex flex-col gap-2 shrink-0">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="p-2.5 rounded-2xl bg-gradient-to-br from-indigo-500 to-violet-600 text-white shadow-lg shadow-indigo-200">
|
||||
<MessageSquare className="h-6 w-6" />
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-3xl font-black tracking-tight text-slate-900">{t.title}</h2>
|
||||
<p className="text-slate-500 font-medium text-xs">{t.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button variant="outline" size="sm" onClick={clearHistory} className="rounded-xl border-slate-200 text-slate-500 hover:text-rose-500 hover:border-rose-200">
|
||||
<Trash2 className="h-4 w-4 mr-2" /> Clear Chat
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 grid grid-cols-1 xl:grid-cols-12 gap-8 min-h-0">
|
||||
{/* Chat Panel */}
|
||||
<Card className={cn(
|
||||
"xl:col-span-12 flex flex-col border-slate-200/60 shadow-xl shadow-slate-200/40 overflow-hidden bg-white/80 backdrop-blur-md transition-all duration-500",
|
||||
activeTab === "preview" ? "xl:col-span-5" : "xl:col-span-12"
|
||||
)}>
|
||||
<CardHeader className="bg-slate-50/50 border-b p-4 shrink-0">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Bot className="h-4 w-4 text-indigo-600" />
|
||||
<span className="text-xs font-black uppercase tracking-widest text-slate-500">Conversation Thread</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5">
|
||||
{AGENTS.map((agent) => (
|
||||
<button
|
||||
key={agent.id}
|
||||
onClick={() => setCurrentAgent(agent.id)}
|
||||
className={cn(
|
||||
"p-1.5 rounded-lg transition-all",
|
||||
currentAgent === agent.id
|
||||
? `bg-${agent.color}-100 text-${agent.color}-600 ring-2 ring-${agent.color}-400/30 scale-110`
|
||||
: "text-slate-400 hover:text-slate-600 hover:bg-slate-100"
|
||||
)}
|
||||
title={agent.label}
|
||||
>
|
||||
<agent.icon className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
|
||||
<CardContent className="flex-1 flex flex-col p-0 min-h-0 relative">
|
||||
<div
|
||||
ref={scrollRef}
|
||||
className="flex-1 overflow-y-auto p-6 space-y-6 scroll-smooth"
|
||||
>
|
||||
{aiAssistHistory.length === 0 ? (
|
||||
<div className="h-full flex flex-col items-center justify-center text-center py-12">
|
||||
<div className="p-6 rounded-3xl bg-indigo-50 border border-indigo-100 mb-6 animate-bounce duration-[3000ms]">
|
||||
<Sparkles className="h-12 w-12 text-indigo-500" />
|
||||
</div>
|
||||
<h3 className="text-xl font-black text-slate-800 mb-2">{t.chatStart}</h3>
|
||||
<p className="text-sm text-slate-400 max-w-sm">Start a conversation to activate specialized AI agents for code, design, SEO, and more.</p>
|
||||
</div>
|
||||
) : (
|
||||
aiAssistHistory.map((msg, i) => (
|
||||
<div key={i} className={cn(
|
||||
"flex gap-4 animate-in fade-in slide-in-from-bottom-2 duration-500",
|
||||
msg.role === "user" ? "flex-row-reverse" : "flex-row"
|
||||
)}>
|
||||
<div className={cn(
|
||||
"h-9 w-9 shrink-0 rounded-2xl flex items-center justify-center border shadow-sm transition-transform hover:scale-110",
|
||||
msg.role === "user"
|
||||
? "bg-slate-900 border-slate-800 text-white"
|
||||
: "bg-white border-slate-100 text-indigo-600"
|
||||
)}>
|
||||
{msg.role === "user" ? <User className="h-4 w-4" /> : <Bot className="h-4 w-4" />}
|
||||
</div>
|
||||
<div className={cn(
|
||||
"max-w-[80%] space-y-2",
|
||||
msg.role === "user" ? "items-end text-right" : "items-start text-left"
|
||||
)}>
|
||||
{msg.agent && (
|
||||
<Badge variant="outline" className="text-[9px] font-black uppercase tracking-widest px-1.5 py-0 border-indigo-200 text-indigo-500 bg-indigo-50/50">
|
||||
{AGENTS.find(a => a.id === msg.agent)?.label || msg.agent}
|
||||
</Badge>
|
||||
)}
|
||||
<div className={cn(
|
||||
"p-4 rounded-3xl text-sm font-medium leading-relaxed shadow-sm",
|
||||
msg.role === "user"
|
||||
? "bg-indigo-600 text-white rounded-tr-none"
|
||||
: msg.role === "system"
|
||||
? "bg-rose-50 text-rose-600 border border-rose-100 rounded-tl-none"
|
||||
: "bg-white border border-slate-100 text-slate-700 rounded-tl-none"
|
||||
)}>
|
||||
{msg.content}
|
||||
</div>
|
||||
<span className="text-[10px] text-slate-400 font-bold px-1">
|
||||
{msg.timestamp.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
{isProcessing && (
|
||||
<div className="flex gap-4 animate-pulse">
|
||||
<div className="h-9 w-9 rounded-2xl bg-slate-100 border border-slate-50 flex items-center justify-center">
|
||||
<Loader2 className="h-4 w-4 animate-spin text-indigo-400" />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="h-10 w-32 bg-slate-50 border border-slate-100 rounded-3xl rounded-tl-none flex items-center px-4">
|
||||
<div className="flex gap-1">
|
||||
<div className="w-1.5 h-1.5 bg-indigo-400 rounded-full animate-bounce [animation-delay:-0.3s]" />
|
||||
<div className="w-1.5 h-1.5 bg-indigo-400 rounded-full animate-bounce [animation-delay:-0.15s]" />
|
||||
<div className="w-1.5 h-1.5 bg-indigo-400 rounded-full animate-bounce" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Input Area */}
|
||||
<div className="p-6 bg-white border-t border-slate-100">
|
||||
<div className="relative group">
|
||||
<div className="absolute inset-x-0 -top-12 h-12 bg-gradient-to-t from-white to-transparent pointer-events-none" />
|
||||
<div className="flex gap-3 items-end">
|
||||
<div className="flex-1 relative">
|
||||
<Input
|
||||
value={input}
|
||||
onChange={(e) => setInput(e.target.value)}
|
||||
onKeyDown={(e) => e.key === 'Enter' && !e.shiftKey && handleSendMessage()}
|
||||
placeholder={t.placeholder}
|
||||
className="h-14 pl-12 pr-4 bg-slate-50 border-slate-200 focus:bg-white focus:ring-4 focus:ring-indigo-500/10 transition-all rounded-2xl font-medium"
|
||||
/>
|
||||
<div className="absolute left-4 top-1/2 -translate-y-1/2">
|
||||
<Sparkles className="h-5 w-5 text-indigo-400" />
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
onClick={handleSendMessage}
|
||||
disabled={isProcessing || !input.trim()}
|
||||
className="h-14 w-14 rounded-2xl bg-indigo-600 hover:bg-indigo-700 text-white shadow-lg shadow-indigo-100 shrink-0 transition-transform active:scale-90"
|
||||
>
|
||||
<Send className="h-6 w-6" />
|
||||
</Button>
|
||||
</div>
|
||||
<div className="mt-3 flex items-center justify-between px-2">
|
||||
<div className="flex gap-4">
|
||||
<button className="text-[10px] font-black uppercase tracking-widest text-slate-400 hover:text-indigo-500 transition-colors flex items-center gap-1.5">
|
||||
<Layers className="h-3 w-3" /> Layout Design
|
||||
</button>
|
||||
<button className="text-[10px] font-black uppercase tracking-widest text-slate-400 hover:text-indigo-500 transition-colors flex items-center gap-1.5">
|
||||
<Code2 className="h-3 w-3" /> Code Snippet
|
||||
</button>
|
||||
</div>
|
||||
<div className="text-[10px] font-bold text-slate-300 italic">
|
||||
Powered by {selectedProvider.toUpperCase()} / {selectedModels[selectedProvider]}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Preview Panel (Conditional) */}
|
||||
{activeTab === "preview" && (
|
||||
<Card className="xl:col-span-7 flex flex-col border-slate-200/60 shadow-2xl shadow-slate-200/50 overflow-hidden bg-white animate-in slide-in-from-right-8 duration-500">
|
||||
<CardHeader className="bg-slate-900 text-white p-4 shrink-0 flex flex-row items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Monitor className="h-4 w-4 text-indigo-400" />
|
||||
<span className="text-xs font-black uppercase tracking-widest">{t.preview}</span>
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => setActiveTab("chat")}
|
||||
className="h-8 w-8 text-slate-400 hover:text-white"
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</Button>
|
||||
</CardHeader>
|
||||
<CardContent className="flex-1 p-0 overflow-hidden bg-slate-50/50">
|
||||
{renderPreview()}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AIAssist;
|
||||
@@ -3,11 +3,11 @@
|
||||
import { useState } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import useStore from "@/lib/store";
|
||||
import { Sparkles, FileText, ListTodo, Palette, Presentation, History, Settings, Github, Menu, X, Megaphone, Languages, Search } from "lucide-react";
|
||||
import { Sparkles, FileText, ListTodo, Palette, Presentation, History, Settings, Github, Menu, X, Megaphone, Languages, Search, MessageSquare } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { translations } from "@/lib/i18n/translations";
|
||||
|
||||
export type View = "enhance" | "prd" | "action" | "uxdesigner" | "slides" | "googleads" | "market-research" | "history" | "settings";
|
||||
export type View = "enhance" | "prd" | "action" | "uxdesigner" | "slides" | "googleads" | "market-research" | "ai-assist" | "history" | "settings";
|
||||
|
||||
interface SidebarProps {
|
||||
currentView: View;
|
||||
@@ -28,6 +28,7 @@ export default function Sidebar({ currentView, onViewChange }: SidebarProps) {
|
||||
{ id: "slides" as View, label: t.slidesGen, icon: Presentation },
|
||||
{ id: "googleads" as View, label: t.googleAds, icon: Megaphone },
|
||||
{ id: "market-research" as View, label: t.marketResearch, icon: Search },
|
||||
{ id: "ai-assist" as View, label: t.aiAssist, icon: MessageSquare },
|
||||
{ id: "history" as View, label: t.history, icon: History, count: history.length },
|
||||
{ id: "settings" as View, label: t.settings, icon: Settings },
|
||||
];
|
||||
|
||||
@@ -12,6 +12,7 @@ export const translations = {
|
||||
googleAds: "Google Ads",
|
||||
uxDesigner: "UX Designer",
|
||||
marketResearch: "Market Research",
|
||||
aiAssist: "AI Assist",
|
||||
settings: "Settings",
|
||||
history: "History",
|
||||
},
|
||||
@@ -148,6 +149,16 @@ export const translations = {
|
||||
"Synthesizing strategic advantages and identifying gaps...",
|
||||
"Finalizing comprehensive intelligence report..."
|
||||
]
|
||||
},
|
||||
aiAssist: {
|
||||
title: "AI Assist",
|
||||
description: "Conversational intelligence with agent switching",
|
||||
placeholder: "Discuss any topic, concern or project...",
|
||||
chatStart: "How can I help you today?",
|
||||
switchingAgent: "Switching to specialized agent...",
|
||||
routing: "Routing your request...",
|
||||
preview: "Real-time Preview",
|
||||
actions: "Agent Actions"
|
||||
}
|
||||
},
|
||||
ru: {
|
||||
@@ -161,6 +172,7 @@ export const translations = {
|
||||
googleAds: "Google Реклама",
|
||||
uxDesigner: "UX Дизайнер",
|
||||
marketResearch: "Анализ рынка",
|
||||
aiAssist: "ИИ Ассистент",
|
||||
settings: "Настройки",
|
||||
history: "История",
|
||||
},
|
||||
@@ -297,6 +309,16 @@ export const translations = {
|
||||
"Синтез стратегических преимуществ и выявление пробелов...",
|
||||
"Финализация подробного отчета о разведке..."
|
||||
]
|
||||
},
|
||||
aiAssist: {
|
||||
title: "ИИ Ассистент",
|
||||
description: "Диалоговый интеллект с переключением агентов",
|
||||
placeholder: "Обсудите любую тему, проблему или проект...",
|
||||
chatStart: "Чем я могу помочь вам сегодня?",
|
||||
switchingAgent: "Переключение на специализированного агента...",
|
||||
routing: "Маршрутизация вашего запроса...",
|
||||
preview: "Предпросмотр в реальном времени",
|
||||
actions: "Действия агента"
|
||||
}
|
||||
},
|
||||
he: {
|
||||
@@ -310,6 +332,7 @@ export const translations = {
|
||||
googleAds: "Google Ads",
|
||||
uxDesigner: "מעצב UX",
|
||||
marketResearch: "מחקר שוק",
|
||||
aiAssist: "סייען AI",
|
||||
settings: "הגדרות",
|
||||
history: "היסטוריה",
|
||||
},
|
||||
@@ -446,6 +469,16 @@ export const translations = {
|
||||
"מסנכרן יתרונות אסטרטגיים ומזהה פערים...",
|
||||
"מגבש דו\"ח מודיעין מקיף סופי..."
|
||||
]
|
||||
},
|
||||
aiAssist: {
|
||||
title: "סייען AI",
|
||||
description: "אינטליגנציה שיחתית עם החלפת סוכנים",
|
||||
placeholder: "דון בכל נושא, חשש או פרויקט...",
|
||||
chatStart: "במה אוכל לעזור לך היום?",
|
||||
switchingAgent: "עובר לסוכן מתמחה...",
|
||||
routing: "מנתב את הבקשה שלך...",
|
||||
preview: "תצוגה מקדימה בזמן אמת",
|
||||
actions: "פעולות סוכן"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ModelProvider, APIResponse, ChatMessage } from "@/types";
|
||||
import type { ModelProvider, APIResponse, ChatMessage, AIAssistMessage } from "@/types";
|
||||
import OllamaCloudService from "./ollama-cloud";
|
||||
import ZaiPlanService from "./zai-plan";
|
||||
import qwenOAuthService, { QwenOAuthConfig, QwenOAuthToken } from "./qwen-oauth";
|
||||
@@ -256,6 +256,19 @@ export class ModelAdapter {
|
||||
return this.callWithFallback((service) => service.generateMarketResearch(options, model), providers);
|
||||
}
|
||||
|
||||
async generateAIAssist(
|
||||
options: {
|
||||
messages: AIAssistMessage[];
|
||||
currentAgent: string;
|
||||
},
|
||||
provider?: ModelProvider,
|
||||
model?: string
|
||||
): Promise<APIResponse<string>> {
|
||||
const fallback = this.buildFallbackProviders(this.preferredProvider, "qwen", "ollama", "zai");
|
||||
const providers: ModelProvider[] = provider ? [provider] : fallback;
|
||||
return this.callWithFallback((service) => service.generateAIAssist(options, model), providers);
|
||||
}
|
||||
|
||||
|
||||
async chatCompletion(
|
||||
messages: ChatMessage[],
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ChatMessage, APIResponse } from "@/types";
|
||||
import type { ChatMessage, APIResponse, AIAssistMessage } from "@/types";
|
||||
|
||||
export interface OllamaCloudConfig {
|
||||
apiKey?: string;
|
||||
@@ -617,71 +617,119 @@ Perform a DEEP 360° competitive intelligence analysis and generate 5-7 strategi
|
||||
},
|
||||
model?: string
|
||||
): Promise<APIResponse<string>> {
|
||||
const { websiteUrl, additionalUrls = [], competitors = [], productMapping, specialInstructions = "" } = options;
|
||||
|
||||
const systemMessage: ChatMessage = {
|
||||
role: "system",
|
||||
content: `You are a WORLD-CLASS Market Research Analyst and Competitive Intelligence Expert. Your task is to perform a deep-dive automated market analysis and competitive intelligence gathering.
|
||||
|
||||
OUTPUT FORMAT - Return ONLY valid JSON with this structure:
|
||||
\`\`\`json
|
||||
{
|
||||
"executiveSummary": "High-level overview of findings",
|
||||
"priceComparisonMatrix": [
|
||||
const systemPrompt = `You are a WORLD-CLASS Market Research Analyst and Competitive Intelligence Expert.
|
||||
Your objective is to perform a deep-dive analysis of a business and its competitors based on provided URLs and product mappings.
|
||||
|
||||
You MUST return your analysis in the following STRICT JSON format:
|
||||
{
|
||||
"product": "Product Name",
|
||||
"userPrice": "$XX.XX",
|
||||
"competitorPrices": [
|
||||
{ "competitor": "Competitor Name", "price": "$XX.XX", "url": "https://competitor.com/product-page" }
|
||||
]
|
||||
"executiveSummary": "A concise overview of the market landscape and key findings.",
|
||||
"priceComparisonMatrix": [
|
||||
{
|
||||
"product": "Product Name",
|
||||
"userPrice": "$XX.XX",
|
||||
"competitorPrices": [
|
||||
{ "competitor": "Competitor Name", "price": "$XX.XX", "url": "https://competitor.com/product-page" }
|
||||
]
|
||||
}
|
||||
],
|
||||
"featureComparisonTable": [
|
||||
{
|
||||
"feature": "Feature Name",
|
||||
"userStatus": true/false/text,
|
||||
"competitorStatus": [
|
||||
{ "competitor": "Competitor Name", "status": true/false/text }
|
||||
]
|
||||
}
|
||||
],
|
||||
"marketPositioning": {
|
||||
"landscape": "Description of the current market state.",
|
||||
"segmentation": "Analysis of target customer segments."
|
||||
},
|
||||
"competitiveAnalysis": {
|
||||
"advantages": ["Point 1", "Point 2"],
|
||||
"disadvantages": ["Point 1", "Point 2"]
|
||||
},
|
||||
"recommendations": ["Actionable step 1", "Actionable step 2"],
|
||||
"methodology": "Brief description of the research process."
|
||||
}
|
||||
],
|
||||
"featureComparisonTable": [
|
||||
|
||||
Requirements:
|
||||
1. Base your analysis on realistic price and feature estimates if exact data isn't visible.
|
||||
2. Focus on core technical/business value rather than marketing fluff.
|
||||
3. Ensure JSON is valid and properly escaped.`;
|
||||
|
||||
const userMsg = `WEBSITE TO ANALYZE: ${options.websiteUrl}
|
||||
ADDITIONAL COMPANY URLS: ${options.additionalUrls?.join(', ') || 'None'}
|
||||
COMPETITOR URLS: ${options.competitors.join(', ')}
|
||||
PRODUCT/FEATURE MAPPING: ${options.productMapping}
|
||||
SPECIAL REQUESTS: ${options.specialInstructions || 'Perform comprehensive analysis'}
|
||||
|
||||
Provide a COMPREHENSIVE competitive intelligence report.`;
|
||||
|
||||
const messages: ChatMessage[] = [
|
||||
{ role: "system", content: systemPrompt },
|
||||
{ role: "user", content: userMsg }
|
||||
];
|
||||
|
||||
return await this.chatCompletion(messages, model || this.getAvailableModels()[0]);
|
||||
}
|
||||
|
||||
async generateAIAssist(
|
||||
options: {
|
||||
messages: AIAssistMessage[];
|
||||
currentAgent: string;
|
||||
},
|
||||
model?: string
|
||||
): Promise<APIResponse<string>> {
|
||||
const systemPrompt = `You are "AI Assist", the master orchestrator of PromptArch.
|
||||
Your goal is to provide intelligent conversational support and switch to specialized agents when necessary.
|
||||
|
||||
CURRENT SPECIALIZED AGENTS:
|
||||
- content: Content creation and optimization expert.
|
||||
- seo: SEO analyst and recommendations specialist.
|
||||
- smm: SMM strategy and social content planner.
|
||||
- pm: Project planning and management lead.
|
||||
- code: Code architect (JavaScript/TypeScript/React focus).
|
||||
- design: UI/UX designer.
|
||||
- web: HTML/CSS/JS web development specialist with real-time preview.
|
||||
- app: Mobile-first app development specialist with real-time preview.
|
||||
|
||||
STRICT OUTPUT FORMAT:
|
||||
You MUST respond in JSON format if you want to activate a preview or switch agents.
|
||||
{
|
||||
"feature": "Feature Name",
|
||||
"userStatus": "Yes/No or description",
|
||||
"competitorStatus": [
|
||||
{ "competitor": "Competitor Name", "status": "Yes/No or description" }
|
||||
]
|
||||
"content": "Your natural language response here...",
|
||||
"agent": "agent_id_to_switch_to (optional)",
|
||||
"preview": { // (optional)
|
||||
"type": "code" | "design" | "content" | "seo",
|
||||
"data": "The actual code, layout, or content to preview",
|
||||
"language": "javascript/html/css/markdown (optional)"
|
||||
}
|
||||
}
|
||||
],
|
||||
"marketPositioning": {
|
||||
"landscape": "Current market landscape description",
|
||||
"segmentation": "Target market segments"
|
||||
},
|
||||
"competitiveAnalysis": {
|
||||
"advantages": ["Point 1", "Point 2"],
|
||||
"disadvantages": ["Point 1", "Point 2"]
|
||||
},
|
||||
"recommendations": ["Rec 1", "Rec 2"],
|
||||
"methodology": "How the research was conducted"
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
REQUIREMENTS:
|
||||
- Analysis must be based on the provided website and competitor URLs.
|
||||
- Price comparison should be as realistic as possible based on industry knowledge.
|
||||
- Feature table should focus on core technical and business value.
|
||||
- Competitive analysis must highlight USP (Unique Selling Proposition).`,
|
||||
};
|
||||
ROUTING LOGIC:
|
||||
- If user asks for code, switch to 'code' or 'web'.
|
||||
- If user asks for design/mockups, switch to 'design'.
|
||||
- If user asks for market/SEO, switch to 'seo'.
|
||||
- If user asks for marketing/social, switch to 'smm'.
|
||||
- Maintain the 'content' of the conversation regardless of the agent switch.
|
||||
|
||||
const userMessage: ChatMessage = {
|
||||
role: "user",
|
||||
content: `🔬 MARKET RESEARCH REQUEST 🔬
|
||||
PREVIEW GUIDELINES:
|
||||
- For 'web'/'app', provide full runnable HTML/CSS/JS.
|
||||
- For 'code', provide clean, commented snippets.
|
||||
- For 'design', provide text-based UI components or layout structures.
|
||||
|
||||
RESPONSE TIME REQUIREMENT: Be concise and accurate.`;
|
||||
|
||||
PRIMARY WEBSITE: ${websiteUrl}
|
||||
ADDITIONAL PAGES: ${additionalUrls.join(", ")}
|
||||
COMPETITORS: ${competitors.join(", ")}
|
||||
PRODUCT COMPARISON MAPPING: ${productMapping}
|
||||
${specialInstructions ? `CUSTOM PARAMETERS: ${specialInstructions}` : ""}
|
||||
const chatMessages: ChatMessage[] = [
|
||||
{ role: "system", content: systemPrompt },
|
||||
...options.messages.map(m => ({
|
||||
role: m.role as "user" | "assistant" | "system",
|
||||
content: m.content
|
||||
}))
|
||||
];
|
||||
|
||||
Please conduct a comprehensive competitive analysis and market research report.`,
|
||||
};
|
||||
|
||||
return this.chatCompletion([systemMessage, userMessage], model || "gpt-oss:120b");
|
||||
return await this.chatCompletion(chatMessages, model || this.getAvailableModels()[0]);
|
||||
}
|
||||
}
|
||||
|
||||
export default OllamaCloudService;
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ChatMessage, APIResponse } from "@/types";
|
||||
import type { ChatMessage, APIResponse, AIAssistMessage } from "@/types";
|
||||
|
||||
const DEFAULT_QWEN_ENDPOINT = "https://dashscope-intl.aliyuncs.com/compatible-mode/v1";
|
||||
const TOKEN_STORAGE_KEY = "promptarch-qwen-tokens";
|
||||
@@ -983,6 +983,29 @@ Perform analysis based on provided instructions.`,
|
||||
return this.chatCompletion([systemMessage, userMessage], model || "coder-model");
|
||||
}
|
||||
|
||||
async generateAIAssist(
|
||||
options: {
|
||||
messages: AIAssistMessage[];
|
||||
currentAgent: string;
|
||||
},
|
||||
model?: string
|
||||
): Promise<APIResponse<string>> {
|
||||
const systemPrompt = `You are "AI Assist". Help conversationally.
|
||||
Switch agents if needed (content, seo, smm, pm, code, design, web, app).
|
||||
Output JSON for previews or agent switches:
|
||||
{ "content": "text", "agent": "id", "preview": { "type": "code|design|content|seo", "data": "...", "language": "..." } }`;
|
||||
|
||||
const chatMessages: ChatMessage[] = [
|
||||
{ role: "system", content: systemPrompt },
|
||||
...options.messages.map(m => ({
|
||||
role: m.role as "user" | "assistant" | "system",
|
||||
content: m.content
|
||||
}))
|
||||
];
|
||||
|
||||
return await this.chatCompletion(chatMessages, model || this.getAvailableModels()[0]);
|
||||
}
|
||||
|
||||
async listModels(): Promise<APIResponse<string[]>> {
|
||||
const models = [
|
||||
"coder-model",
|
||||
@@ -999,6 +1022,5 @@ Perform analysis based on provided instructions.`,
|
||||
|
||||
const qwenOAuthService = new QwenOAuthService();
|
||||
export default qwenOAuthService;
|
||||
export { qwenOAuthService };
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ChatMessage, APIResponse } from "@/types";
|
||||
import type { ChatMessage, APIResponse, AIAssistMessage } from "@/types";
|
||||
|
||||
export interface ZaiPlanConfig {
|
||||
apiKey?: string;
|
||||
@@ -700,67 +700,105 @@ MISSION: Perform a DEEP 360° competitive intelligence analysis and generate 5-7
|
||||
): Promise<APIResponse<string>> {
|
||||
const { websiteUrl, additionalUrls = [], competitors = [], productMapping, specialInstructions = "" } = options;
|
||||
|
||||
const systemMessage: ChatMessage = {
|
||||
role: "system",
|
||||
content: `You are a WORLD-CLASS Market Research Analyst and Competitive Intelligence Expert. Perform a deep-dive automated market analysis.
|
||||
|
||||
OUTPUT FORMAT - Return ONLY valid JSON with this structure:
|
||||
\`\`\`json
|
||||
{
|
||||
"executiveSummary": "High-level overview of findings",
|
||||
"priceComparisonMatrix": [
|
||||
const systemPrompt = `You are a WORLD-CLASS Market Research Analyst and Competitive Intelligence Expert.
|
||||
Focus on accuracy and actionable intelligence.
|
||||
|
||||
You MUST return your analysis in the following STRICT JSON format:
|
||||
{
|
||||
"product": "Product Name",
|
||||
"userPrice": "$XX.XX",
|
||||
"competitorPrices": [
|
||||
{ "competitor": "Competitor Name", "price": "$XX.XX", "url": "https://competitor.com/product-page" }
|
||||
]
|
||||
"executiveSummary": "A concise overview of the market landscape and key findings.",
|
||||
"priceComparisonMatrix": [
|
||||
{
|
||||
"product": "Product Name",
|
||||
"userPrice": "$XX.XX",
|
||||
"competitorPrices": [
|
||||
{ "competitor": "Competitor Name", "price": "$XX.XX", "url": "https://competitor.com/product-page" }
|
||||
]
|
||||
}
|
||||
],
|
||||
"featureComparisonTable": [
|
||||
{
|
||||
"feature": "Feature Name",
|
||||
"userStatus": true/false/text,
|
||||
"competitorStatus": [
|
||||
{ "competitor": "Competitor Name", "status": true/false/text }
|
||||
]
|
||||
}
|
||||
],
|
||||
"marketPositioning": {
|
||||
"landscape": "Description of the current market state.",
|
||||
"segmentation": "Analysis of target customer segments."
|
||||
},
|
||||
"competitiveAnalysis": {
|
||||
"advantages": ["Point 1", "Point 2"],
|
||||
"disadvantages": ["Point 1", "Point 2"]
|
||||
},
|
||||
"recommendations": ["Actionable step 1", "Actionable step 2"],
|
||||
"methodology": "Brief description of the research process."
|
||||
}
|
||||
],
|
||||
"featureComparisonTable": [
|
||||
|
||||
Requirements:
|
||||
1. Base your analysis on realistic price and feature estimates.
|
||||
2. Focus on core technical/business value.
|
||||
3. Ensure JSON is valid.`;
|
||||
|
||||
const userMsg = `WEBSITE TO ANALYZE: ${options.websiteUrl}
|
||||
COMPETITOR URLS: ${options.competitors.join(', ')}
|
||||
PRODUCT/FEATURE MAPPING: ${options.productMapping}
|
||||
SPECIAL REQUESTS: ${options.specialInstructions || 'Perform comprehensive analysis'}
|
||||
|
||||
Provide a COMPREHENSIVE competitive intelligence analysis.`;
|
||||
|
||||
const messages: ChatMessage[] = [
|
||||
{ role: "system", content: systemPrompt },
|
||||
{ role: "user", content: userMsg }
|
||||
];
|
||||
|
||||
return await this.chatCompletion(messages, model || this.getAvailableModels()[0]);
|
||||
}
|
||||
|
||||
async generateAIAssist(
|
||||
options: {
|
||||
messages: AIAssistMessage[];
|
||||
currentAgent: string;
|
||||
},
|
||||
model?: string
|
||||
): Promise<APIResponse<string>> {
|
||||
const systemPrompt = `You are "AI Assist", the master orchestrator of PromptArch.
|
||||
Your goal is to provide intelligent conversational support and switch to specialized agents when necessary.
|
||||
|
||||
CURRENT SPECIALIZED AGENTS:
|
||||
- content, seo, smm, pm, code, design, web, app
|
||||
|
||||
STRICT OUTPUT FORMAT:
|
||||
You MUST respond in JSON format if you want to activate a preview or switch agents.
|
||||
{
|
||||
"feature": "Feature Name",
|
||||
"userStatus": "Yes/No or description",
|
||||
"competitorStatus": [
|
||||
{ "competitor": "Competitor Name", "status": "Yes/No or description" }
|
||||
]
|
||||
"content": "Your natural language response here...",
|
||||
"agent": "agent_id_to_switch_to (optional)",
|
||||
"preview": { // (optional)
|
||||
"type": "code" | "design" | "content" | "seo",
|
||||
"data": "The actual code, layout, or content to preview",
|
||||
"language": "javascript/html/css/markdown (optional)"
|
||||
}
|
||||
}
|
||||
],
|
||||
"marketPositioning": {
|
||||
"landscape": "Current market landscape description",
|
||||
"segmentation": "Target market segments"
|
||||
},
|
||||
"competitiveAnalysis": {
|
||||
"advantages": ["Point 1", "Point 2"],
|
||||
"disadvantages": ["Point 1", "Point 2"]
|
||||
},
|
||||
"recommendations": ["Rec 1", "Rec 2"],
|
||||
"methodology": "How the research was conducted"
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
REQUIREMENTS:
|
||||
- Focus on accuracy and actionable intelligence.
|
||||
- Be realistic with price and feature estimates based on the provided URLs.`,
|
||||
};
|
||||
ROUTING LOGIC:
|
||||
- Automatically detect user intent and switch agents if appropriate.
|
||||
- Provide deep technical or creative output based on the active agent.
|
||||
|
||||
PREVIEW GUIDELINES:
|
||||
- Provide full code for 'web'/'app'/'code'.
|
||||
- Provide structured analysis for 'seo'/'content'.`;
|
||||
|
||||
const userMessage: ChatMessage = {
|
||||
role: "user",
|
||||
content: `🔬 MARKET RESEARCH REQUEST 🔬
|
||||
const chatMessages: ChatMessage[] = [
|
||||
{ role: "system", content: systemPrompt },
|
||||
...options.messages.map(m => ({
|
||||
role: m.role as "user" | "assistant" | "system",
|
||||
content: m.content
|
||||
}))
|
||||
];
|
||||
|
||||
PRIMARY WEBSITE: ${websiteUrl}
|
||||
ADDITIONAL PAGES: ${additionalUrls.join(", ")}
|
||||
COMPETITORS: ${competitors.join(", ")}
|
||||
PRODUCT COMPARISON MAPPING: ${productMapping}
|
||||
${specialInstructions ? `CUSTOM PARAMETERS: ${specialInstructions}` : ""}
|
||||
|
||||
Perform a COMPREHENSIVE competitive intelligence analysis.`,
|
||||
};
|
||||
|
||||
return this.chatCompletion([systemMessage, userMessage], model || "glm-4.7", true);
|
||||
return await this.chatCompletion(chatMessages, model || this.getAvailableModels()[0]);
|
||||
}
|
||||
}
|
||||
|
||||
export default ZaiPlanService;
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { create } from "zustand";
|
||||
import type { ModelProvider, PromptEnhancement, PRD, ActionPlan, SlidesPresentation, GoogleAdsResult, MagicWandResult, MarketResearchResult, AppView } from "@/types";
|
||||
import type { ModelProvider, PromptEnhancement, PRD, ActionPlan, SlidesPresentation, GoogleAdsResult, MagicWandResult, MarketResearchResult, AppView, AIAssistMessage } from "@/types";
|
||||
|
||||
interface AppState {
|
||||
currentPrompt: string;
|
||||
@@ -10,6 +10,7 @@ interface AppState {
|
||||
googleAdsResult: GoogleAdsResult | null;
|
||||
magicWandResult: MagicWandResult | null;
|
||||
marketResearchResult: MarketResearchResult | null;
|
||||
aiAssistHistory: AIAssistMessage[];
|
||||
language: "en" | "ru" | "he";
|
||||
selectedProvider: ModelProvider;
|
||||
selectedModels: Record<ModelProvider, string>;
|
||||
@@ -36,6 +37,7 @@ interface AppState {
|
||||
setGoogleAdsResult: (result: GoogleAdsResult | null) => void;
|
||||
setMagicWandResult: (result: MagicWandResult | null) => void;
|
||||
setMarketResearchResult: (result: MarketResearchResult | null) => void;
|
||||
setAIAssistHistory: (history: AIAssistMessage[] | ((prev: AIAssistMessage[]) => AIAssistMessage[])) => void;
|
||||
setLanguage: (lang: "en" | "ru" | "he") => void;
|
||||
setSelectedProvider: (provider: ModelProvider) => void;
|
||||
setSelectedModel: (provider: ModelProvider, model: string) => void;
|
||||
@@ -58,6 +60,7 @@ const useStore = create<AppState>((set) => ({
|
||||
googleAdsResult: null,
|
||||
magicWandResult: null,
|
||||
marketResearchResult: null,
|
||||
aiAssistHistory: [],
|
||||
language: "en",
|
||||
selectedProvider: "qwen",
|
||||
selectedModels: {
|
||||
@@ -87,6 +90,9 @@ const useStore = create<AppState>((set) => ({
|
||||
setGoogleAdsResult: (result) => set({ googleAdsResult: result }),
|
||||
setMagicWandResult: (result) => set({ magicWandResult: result }),
|
||||
setMarketResearchResult: (result) => set({ marketResearchResult: result }),
|
||||
setAIAssistHistory: (update) => set((state) => ({
|
||||
aiAssistHistory: typeof update === 'function' ? update(state.aiAssistHistory) : update
|
||||
})),
|
||||
setLanguage: (lang) => set({ language: lang }),
|
||||
setSelectedProvider: (provider) => set({ selectedProvider: provider }),
|
||||
setSelectedModel: (provider, model) =>
|
||||
@@ -126,6 +132,7 @@ const useStore = create<AppState>((set) => ({
|
||||
googleAdsResult: null,
|
||||
magicWandResult: null,
|
||||
marketResearchResult: null,
|
||||
aiAssistHistory: [],
|
||||
error: null,
|
||||
}),
|
||||
}));
|
||||
|
||||
@@ -292,4 +292,16 @@ export interface MarketResearchResult {
|
||||
rawContent: string;
|
||||
}
|
||||
|
||||
export type AppView = "prompt-enhancer" | "prd-generator" | "action-plan" | "slides-gen" | "google-ads" | "ux-designer" | "market-research" | "settings" | "history";
|
||||
export interface AIAssistMessage {
|
||||
role: "user" | "assistant" | "system";
|
||||
content: string;
|
||||
agent?: string;
|
||||
preview?: {
|
||||
type: "code" | "design" | "content" | "seo";
|
||||
data: string;
|
||||
language?: string;
|
||||
};
|
||||
timestamp: Date;
|
||||
}
|
||||
|
||||
export type AppView = "prompt-enhancer" | "prd-generator" | "action-plan" | "slides-gen" | "google-ads" | "ux-designer" | "market-research" | "ai-assist" | "settings" | "history";
|
||||
|
||||
Reference in New Issue
Block a user