feat: 添加 start 命令,交互式选择小说和风格
This commit is contained in:
149
main.go
149
main.go
@@ -1,9 +1,12 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"embed"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/voocel/ainovel-cli/app"
|
||||
@@ -21,6 +24,12 @@ var referencesFS embed.FS
|
||||
var stylesFS embed.FS
|
||||
|
||||
func main() {
|
||||
// 处理 start 子命令
|
||||
if len(os.Args) > 1 && os.Args[1] == "start" {
|
||||
handleStartCommand()
|
||||
return
|
||||
}
|
||||
|
||||
style := envOr("NOVEL_STYLE", "default")
|
||||
refs := loadReferences(style)
|
||||
prompts := loadPrompts()
|
||||
@@ -87,6 +96,7 @@ func printHelp() {
|
||||
fmt.Println(`ainovel-cli - AI 小说生成工具
|
||||
|
||||
用法:
|
||||
ainovel-cli start 交互式开始:选择小说 → 选择风格 → 启动 TUI
|
||||
ainovel-cli [prompt] CLI 模式:直接生成小说
|
||||
ainovel-cli TUI 模式:启动交互界面
|
||||
|
||||
@@ -195,3 +205,142 @@ func envOr(key, fallback string) string {
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
||||
// handleStartCommand 处理 start 子命令:交互式选择小说和风格
|
||||
func handleStartCommand() {
|
||||
// Step 1: 选择小说
|
||||
novelName := selectNovel()
|
||||
if novelName == "" {
|
||||
fmt.Println("已取消")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// Step 2: 选择风格
|
||||
style := selectStyle()
|
||||
if style == "" {
|
||||
fmt.Println("已取消")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// 设置环境变量并启动 TUI
|
||||
os.Setenv("NOVEL_NAME", novelName)
|
||||
os.Setenv("NOVEL_STYLE", style)
|
||||
|
||||
refs := loadReferences(style)
|
||||
prompts := loadPrompts()
|
||||
styles := loadStyles()
|
||||
cfg := buildConfig(style)
|
||||
cfg.NovelName = novelName
|
||||
|
||||
if err := tui.Run(cfg, refs, prompts, styles); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// selectNovel 显示小说选择菜单
|
||||
func selectNovel() string {
|
||||
options := []string{"📝 创建新小说"}
|
||||
|
||||
// 扫描 output 目录下现有小说
|
||||
outputDir := "output"
|
||||
entries, err := ioutil.ReadDir(outputDir)
|
||||
novels := []string{}
|
||||
if err == nil {
|
||||
for _, e := range entries {
|
||||
if e.IsDir() && e.Name() != "." && e.Name() != ".." {
|
||||
novels = append(novels, e.Name())
|
||||
options = append(options, "📖 "+e.Name())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(options) == 1 {
|
||||
// 只有创建选项,直接输入新名称
|
||||
return promptNovelName()
|
||||
}
|
||||
|
||||
// 显示菜单
|
||||
fmt.Println("\n=== 选择小说或创建新的 ===")
|
||||
for i, opt := range options {
|
||||
fmt.Printf("%d. %s\n", i+1, opt)
|
||||
}
|
||||
|
||||
choice := promptChoice(len(options))
|
||||
if choice < 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
if choice == 0 {
|
||||
// 创建新小说
|
||||
return promptNovelName()
|
||||
}
|
||||
|
||||
// 返回现有小说名称
|
||||
return novels[choice-1]
|
||||
}
|
||||
|
||||
// selectStyle 显示风格选择菜单
|
||||
func selectStyle() string {
|
||||
styles := []struct {
|
||||
name string
|
||||
desc string
|
||||
}{
|
||||
{"default", "通用风格,叙事张弛有度,五感描写,对话自然"},
|
||||
{"fantasy", "奇幻冒险,世界观自然展开,魔法体系有代价感"},
|
||||
{"romance", "言情,情感递进有节奏,关系张力与内心描写并重"},
|
||||
{"suspense", "悬疑推理,多线叙事,信息差悬念,线索管理严谨"},
|
||||
}
|
||||
|
||||
fmt.Println("\n=== 选择小说风格 ===")
|
||||
for i, s := range styles {
|
||||
fmt.Printf("%d. %-10s %s\n", i+1, s.name, s.desc)
|
||||
}
|
||||
|
||||
choice := promptChoice(len(styles))
|
||||
if choice < 0 {
|
||||
// EOF 或错误,使用默认风格
|
||||
fmt.Println("使用默认风格...")
|
||||
return "default"
|
||||
}
|
||||
return styles[choice].name
|
||||
}
|
||||
|
||||
// promptNovelName 提示输入新小说名称
|
||||
func promptNovelName() string {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
fmt.Print("\n请输入小说名称 (默认: novel): ")
|
||||
name, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
return "novel"
|
||||
}
|
||||
name = strings.TrimSpace(name)
|
||||
if name == "" {
|
||||
return "novel"
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
// promptChoice 提示用户选择菜单项
|
||||
func promptChoice(maxChoice int) int {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
for {
|
||||
fmt.Print("\n请选择 (输入数字): ")
|
||||
input, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
// EOF 或其他错误,返回 -1 表示取消
|
||||
return -1
|
||||
}
|
||||
input = strings.TrimSpace(input)
|
||||
if input == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
choice, err := strconv.Atoi(input)
|
||||
if err != nil || choice < 1 || choice > maxChoice {
|
||||
fmt.Printf("❌ 请输入 1 到 %d 之间的数字\n", maxChoice)
|
||||
continue
|
||||
}
|
||||
return choice - 1 // 转换为 0-based 索引
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user