169 lines
4.6 KiB
TypeScript
169 lines
4.6 KiB
TypeScript
import { FileText, Code2, Globe, Atom, Terminal } from "lucide-react";
|
|
import type { LucideIcon } from "lucide-react";
|
|
|
|
import { useCanvasGraphContext } from "../../contexts/CanvasGraphContext";
|
|
import { createDraftArtifact } from "../../utils/artifact";
|
|
import type { ProgrammingLanguageOptions } from "../../types/artifact";
|
|
|
|
const HTML_TEMPLATE = `<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>页面标题</title>
|
|
<style>
|
|
body { font-family: sans-serif; margin: 2rem; color: #333; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>Hello World</h1>
|
|
<p>在这里开始编写你的页面</p>
|
|
</body>
|
|
</html>`;
|
|
|
|
const REACT_TEMPLATE = `import React from "react";
|
|
|
|
interface Props {
|
|
// 在这里定义 props
|
|
}
|
|
|
|
export default function MyComponent({}: Props) {
|
|
return (
|
|
<div>
|
|
<h1>My Component</h1>
|
|
<p>在这里开始编写你的组件</p>
|
|
</div>
|
|
);
|
|
}`;
|
|
|
|
const PYTHON_TEMPLATE = `def main():
|
|
"""主函数"""
|
|
print("Hello, World!")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()`;
|
|
|
|
interface QuickStartOption {
|
|
icon: LucideIcon;
|
|
title: string;
|
|
description: string;
|
|
type: "text" | "code";
|
|
templateTitle: string;
|
|
content: string;
|
|
language?: ProgrammingLanguageOptions;
|
|
}
|
|
|
|
const QUICK_START_OPTIONS: QuickStartOption[] = [
|
|
{
|
|
icon: FileText,
|
|
title: "空白文档",
|
|
description: "从空白 Markdown 文档开始",
|
|
type: "text",
|
|
templateTitle: "未命名文档",
|
|
content: "",
|
|
},
|
|
{
|
|
icon: Code2,
|
|
title: "空白代码",
|
|
description: "从空白代码文件开始",
|
|
type: "code",
|
|
templateTitle: "未命名代码",
|
|
content: "",
|
|
language: "python",
|
|
},
|
|
{
|
|
icon: Globe,
|
|
title: "HTML 页面",
|
|
description: "基础 HTML 页面模板",
|
|
type: "code",
|
|
templateTitle: "HTML 页面",
|
|
content: HTML_TEMPLATE,
|
|
language: "html",
|
|
},
|
|
{
|
|
icon: Atom,
|
|
title: "React 组件",
|
|
description: "TypeScript React 组件模板",
|
|
type: "code",
|
|
templateTitle: "React 组件",
|
|
content: REACT_TEMPLATE,
|
|
language: "typescript",
|
|
},
|
|
{
|
|
icon: Terminal,
|
|
title: "Python 脚本",
|
|
description: "Python 入口脚本模板",
|
|
type: "code",
|
|
templateTitle: "Python 脚本",
|
|
content: PYTHON_TEMPLATE,
|
|
language: "python",
|
|
},
|
|
];
|
|
|
|
interface CanvasEmptyStateProps {
|
|
initialType?: "text" | "code" | null;
|
|
}
|
|
|
|
export function CanvasEmptyState({ initialType }: CanvasEmptyStateProps) {
|
|
const { setArtifact } = useCanvasGraphContext();
|
|
|
|
function handleSelect(option: QuickStartOption) {
|
|
setArtifact(
|
|
createDraftArtifact(option.type, {
|
|
title: option.templateTitle,
|
|
content: option.content,
|
|
language: option.language,
|
|
}),
|
|
);
|
|
}
|
|
|
|
const filtered =
|
|
initialType
|
|
? QUICK_START_OPTIONS.filter(
|
|
(o) => !initialType || o.type === initialType || o.type === "text",
|
|
)
|
|
: QUICK_START_OPTIONS;
|
|
|
|
return (
|
|
<div className="h-full flex flex-col items-center justify-center px-6">
|
|
<div className="w-full max-w-sm space-y-6">
|
|
<div className="text-center space-y-1">
|
|
<p className="text-sm font-medium">选择模板快速开始</p>
|
|
<p className="text-xs text-muted-foreground">
|
|
或直接在左侧聊天区发送消息
|
|
</p>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
{QUICK_START_OPTIONS.map((option) => {
|
|
const Icon = option.icon;
|
|
const isHighlighted = initialType && option.type === initialType && option.content === "";
|
|
return (
|
|
<button
|
|
key={option.title}
|
|
onClick={() => handleSelect(option)}
|
|
className={`w-full flex items-center gap-3 rounded-lg border px-4 py-3 text-left transition-all hover:border-primary/50 hover:bg-accent/40 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring ${
|
|
isHighlighted
|
|
? "border-primary/40 bg-primary/5"
|
|
: "bg-card"
|
|
}`}
|
|
>
|
|
<div className="w-8 h-8 rounded-md bg-muted flex items-center justify-center shrink-0">
|
|
<Icon className="w-4 h-4 text-muted-foreground" />
|
|
</div>
|
|
<div className="min-w-0">
|
|
<p className="text-sm font-medium truncate">{option.title}</p>
|
|
<p className="text-xs text-muted-foreground truncate">
|
|
{option.description}
|
|
</p>
|
|
</div>
|
|
</button>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|