Skip to content

Repository files navigation

灵感剧本规划器 · Inspiration Script Planner

把碎片化的灵感,长成一份能往下拍的剧本大纲。

Demo AI Studio React TypeScript Vite Tailwind Gemini DeepSeek License


⚠️ 先读这一条,能省你十分钟

设置面板里的第一个选项「默认 (Gemini Flash) — 使用内置的免费试用通道」在线上部署与本地自建环境里都不可用。 源码写的是 process.env.API_KEY,而 Vite 会把 process.env 替换成空对象,于是传给 SDK 的 apiKey 是 undefined, Google SDK 会当场抛错:An API Key must be set when running in a browser。

要用它,请在「配置 AI」里切到 Google Gemini (自选 Key) 或 DeepSeek (自选 Key) 并填入自己的 Key。 详见 AI 服务商与模型。


目录


项目简介

灵感剧本规划器是一个纯前端的 AI 写作辅助工作台,专门解决「脑子里有画面、有几句话、有几个角色,但串不成一个故事」这一步。

它不替你写正文,而是替你完成从碎片到骨架的跃迁:

  1. 你把手里的零散素材丢进去(世界观、角色人设、关键情节、想要的调性);
  2. 它让模型把同一批素材往三个截然不同的方向各推一遍,产出三个候选方案;
  3. 你挑一个最顺眼的,它就变成一份可逐字编辑的大纲(标题、一句话梗概、故事简介、核心冲突、幕结构);
  4. 进入结构编辑器后,你可以针对单个情节点让 AI 细化、拆分,或者让它顺着上下文继续往下写几个节点。

产出物是一份结构化的幕/节点清单,可以导出成 TXT 带走,也可以把改好的 TXT 导回来覆盖。

关于仓库名 -

这个仓库的地址是 github.com/kuroshio4396/-,仓库名就是一个连字符。这是发布时的疏忽,不是刻意设计—— 在 GitHub 上它并不会带来功能问题(URL 里 - 是安全字符),但确实不利于检索与分享。项目真正的名字是 灵感剧本规划器。

从代码里能看到它的来历:metadata.json 与 package.json 里的名字分别是 Copy of Copy of 灵感剧本规划器 版本3 与 copy-of-copy-of-灵感剧本规划器-版本3, 页面里也残留着 Google AI Studio 的工程链接——它是从 AI Studio 的 aistudio-repository-template 模板里导出的项目,作者复制、迭代了若干版之后直接推送到了这里。


核心特性

  • 四步线性工作流 —— 灵感输入 → 构思规划 → 剧本大纲 → 结构编辑器,顶部导航可随时回退到上一步。
  • 一次给三个方向 —— 同一个素材,模型会刻意分化成三份切入点不同的方案,而不是三个措辞不同的一版。
  • 「都不满意?重做」 —— 不满意时点一下,会把已看过的标题全部列为禁用,逼模型换完全不同的方向或反转, 而且这些禁用标题会跨多轮累积。
  • 不挑输入形态 —— 可以手填,也可以直接上传 .docx / .txt,由 AI 自动抽取世界观、角色、剧情梗概与主题并回填表单。
  • 五档角色定位 —— 主角 / 主要配角 / 反派BOSS / 次要配角 / 群演,角色卡片可增删,取舍会自动进入提示词。
  • 大纲逐字可编辑 —— 标题、一句话梗概、故事简介、核心冲突都是输入框,不是只读文本。
  • 单节点 AI 操作 —— 结构编辑器里每个节点都带「细化」(扩写细节、加画面感与冲突)与「拆分」(拆成 2–4 个连续节点)两个按钮。
  • AI 续写 —— 带上全部已有节点当上下文,一次接着往下生成 2–3 个新节点。
  • TXT 往返 —— 导出的大纲可以直接改完再导回来;导入器认得 第1幕 / 第三章 / Act 1 / Chapter 1 / Scene 1 等多种标题写法。
  • 两条自带 Key 通道 —— Google Gemini 与 DeepSeek(OpenAI 兼容),配置存在本地浏览器,服务端不需要任何东西。

四步工作流

灵感输入 ──▶ 构思规划 ──▶ 剧本大纲 ──▶ 剧本结构编辑器
① 灵感输入
   世界观 / 角色人设 / 剧情片段 / 主题 —— 或直接上传 docx·txt 让 AI 自动抽取并回填
   │
   │   analyzeFileContent(仅「AI 导入」时调用)
   ▼
② 构思规划
   generateScriptPlans —— 一次产出 3 个方向迥异的候选方案;不满意可「重做」换一批
   │                      (已看过的标题会累积进禁用名单)
   ▼
③ 剧本大纲
   标题 / 一句话梗概 / 故事简介 / 核心冲突 全部可编辑;下方是幕结构预览
   │
   │   ← 这一屏与上一屏都不调用 AI,纯本地编辑
   ▼
④ 剧本结构编辑器
   refineScriptAct(逐节点细化) · splitScriptAct(逐节点拆分) · extendScriptStructure(AI 续写)
   + 删除节点 / 添加空白节点 / 导出TXT / 导入TXT(全量覆盖)

四个视图由 AppView 一个状态机驱动:input → selection → outline → structure_editor。 只有「灵感输入 → 构思规划」这一跳必须调用 AI;之后的编辑全部是本地操作,不消耗任何额度。


界面与用法详解

第一步 · 灵感输入

四个字段,全部选填但有门槛:

  • 世界观 / 故事背景 —— 多行文本。占位提示:「例如:赛博朋克风格的古代长安,或是被洪水淹没的未来地球...」
  • 角色人设 —— 动态卡片列表。每张卡有「角色名称」「角色定位下拉框」「角色描述」三部分; 默认角色定位是 主角,可选项为 主角 / 主要配角 / 反派BOSS / 次要配角 / 群演。 列表为空时显示虚线占位框引导你去点「添加角色」。
  • 初步剧情片段 / 关键情节 —— 多行文本。占位提示:「例如:主角在雨夜捡到一把会说话的枪,或者结尾必须是悲剧...」
  • 核心主题 / 风格偏好(选填) —— 单行文本。占位提示:「例如:关于复仇,黑色幽默,克苏鲁神话...」

门槛:点「生成剧本规划」时,如果四个字段全部为空,会弹 请输入至少一项灵感内容。 并中止。 只要有一个字段有内容就放行——所以只写一句主题也能开工。

「AI 导入 (Word/Txt)」按钮:选择 .docx 或 .txt 后,前端先抽文本再喂给模型做结构化抽取。

  • .docx 走 mammoth.js(1.6.0,从 cdnjs 加载)取纯文本;
  • .txt 直接 file.text() 读取;
  • 抽完之后把结果合并进表单:worldSetting / plotSnippets / theme 只在该字段当前为空时写入 (用的是 analysis.x || prev.x),而 角色是追加——所以重复导入同一份文件会得到重复角色,需要手动删。
  • 文件内容会被截断到 100000 字符再送去分析(超出部分静默丢弃)。
  • 导入过程中按钮变成「AI分析中...」并禁用,同时也会禁用「生成剧本规划」。

第二步 · 构思规划

顶部有一句提示:「AI为您生成了以下几个发展方向。选择一个最吸引你的加入大纲。」

三个方案以卡片网格呈现(宽屏 3 列 / 中屏 2 列 / 手机 1 列),每张卡片展示:

  • 标题 + 右上角的风格标签(tone,如「赛博朋克黑色电影」)
  • 一句话梗概(logline,左侧带竖线的斜体引用样式)
  • 主要冲突(mainConflict)
  • 简介(synopsis,最多显示 4 行,超出用 line-clamp-4 截断)

卡片底部是「加入剧本大纲」。

右上角两个动作:

  • 返回修改灵感 —— 回到第一步,已填内容都还在。
  • 都不满意?重做 —— 触发生成。它会把你这次看到的三个标题连同之前所有轮次已经拒过的标题 一起塞进提示词的 avoidTitles,并明确要求「尝试完全不同的方向或反转」。 效果是连续点几次「重做」,方向会越来越偏。重新从第一步生成(非重做)时,禁用列表会被清空。

第三步 · 剧本大纲

选定方案后进入。上半部是渐变深蓝的头部,风格标签、标题与一句话梗概都是可直接改的输入框 (悬停时出现下划线提示可编辑);右上角有「删除/重置」——会二次确认,然后把大纲清空并退回第二步。

正文两部分:故事简介(可编辑,约 10 行高的文本域)与核心冲突(可编辑,约 6 行高)。 再下面是剧本结构规划的只读预览:显示节点总数,并列出前 3 个节点的标题,超出时显示「...还有 N 个节点」。 真正改结构要点「进入结构编辑器」。

第四步 · 剧本结构编辑器

这是整个应用里最重的一屏。每个节点是一张卡片:

  • 左上角是序号徽章 + 可编辑的节点标题;
  • 大号文本域是 节点描述,可自由编辑,右下角可拖拽调整高度;
  • 工具条平时半透明,鼠标悬停整张卡片时才显现(触屏设备上常驻),包含三个按钮:
    • 细化 —— 带着「前情回顾」(当前节点之前的全部节点)让 AI 大幅丰富这一节的细节, 要求增加画面感、动作描述、潜在对话冲突或心理活动。返回的富文本直接覆盖该节点描述。
    • 拆分 —— 把当前节点拆成 2–4 个时间上连续的更细节点,拆分结果原地替换该节点。
    • 删除 —— 二次确认后移除。
  • AI 处理期间,工具条替换为「AI处理中...」的转圈徽章,同时全局只允许一个 AI 操作在跑 (用 processingIndex 挡并发)。

底部是固定的操作条(position: fixed,所以页面留了 pb-24 的下边距):

  • 添加空白节点 —— 追加一个标题为「新情节节点」的空节点。没有拖拽排序,顺序即数组顺序, 想调整位置只能删掉重建。
  • AI 继续生成剧情 —— 用完整上下文(世界观、角色、全部已有节点)续写 2–3 个新节点并追加到末尾。 每点一次都会再往下长一截,适合「滚雪球」式推进。

左上角还有 导出TXT 与 导入TXT (覆盖)。

导入 / 导出 TXT 的格式约定

导出的 TXT 长这样(handleExportTxt,文件名 ${标题}-structure.txt):

=== 剧本大纲: 雨夜拾枪 ===
风格: 冷硬黑色电影
核心冲突: 主角必须在复仇与自保之间选一个

=== 剧情结构详情 ===

第 1 幕: 雨夜
-------------------
主角在天桥下捡到一把会说话的枪,枪声称自己记得所有被杀者的名字。

===================

第 2 幕: 第一次交易
-------------------
...

导入(handleImportTxt)会在覆盖前弹窗警告「此操作将【彻底清空】当前编辑器内的所有节点……建议先导出备份」。 确认后按正则在全文里找节点标题,规则如下:

  • 认得的标题写法:第1幕 第 2 节 第三章 第 4 场(中文数字与阿拉伯数字都行)、 Act 1、Chapter 2、Scene 3,且允许前面带 ### 之类的 Markdown 井号;
  • 标题后可以用 : 或 : 接副标题;
  • 第一个标题之前若还有正文,会被装成一个标题为「前言/背景信息」的节点,不会丢;
  • 每个节点的描述 = 从该标题后到下一个标题之间的全部内容,--- 与 === 分隔线会被自动清掉;
  • 一个标题都没匹配上时,整份文件会被塞进单个节点,标题写作「导入的内容」;
  • 导入完成后弹 已覆盖现有内容,成功导入 N 个剧情节点!。

注意:导入只替换节点列表。导出文件头部的「风格」「核心冲突」两行,以及根本没被导出的 「一句话梗概」「故事简介」,导入时都不会被读回——它们仍以页面上现有的值为准。 也就是说 TXT 往返对节点是无损的,对大纲头部字段不是。


AI 服务商与模型

点右上角「配置 AI」打开设置面板,三选一。配置只存在浏览器 localStorage,读取键名 ai_script_settings。

通道 面板上的说明文案 代码实际用的模型 需要自己的 Key
默认 默认 (Gemini Flash) —— 使用内置的免费试用通道 gemini-3-flash-preview 否(但取不到 Key,见下)
Google Gemini Google Gemini (自选 Key) —— 调用 gemini-2.5-pro 模型 gemini-2.0-flash 是
DeepSeek DeepSeek (自选 Key) —— 调用 deepseek-chat 模型 deepseek-chat 是

⚠️ 「默认」通道在自部署环境下不可用

services/geminiService.ts 里默认通道是这么写的:

if (settings.provider === 'default') {
  const ai = new GoogleGenAI({ apiKey: process.env.API_KEY });
  const response = await ai.models.generateContent({ model: "gemini-3-flash-preview", ... });
}

vite.config.ts 里只有一个 react() 插件,没有 define、也没有 loadEnv。 Vite 会把 process.env 替换成一个空对象——在线上产物里可以直接看到 var hR={}; ... new Mh({apiKey:hR.API_KEY}) ...,也就是 apiKey: undefined。 而 Google SDK 的浏览器实现里有一句硬校验:

if (t.apiKey == null) throw new Error("An API Key must be set when running in a browser");

结论:走「默认」通道时,请求还没发出去就会抛异常,被 App.tsx 兜住并弹出 生成失败,请检查网络设置、API Key 是否正确,或稍后再试。。 面板上「使用内置的免费试用通道」这句文案与实现不符——在 AI Studio 自己的托管环境里 这个变量是平台注入的,一旦导出到 Vercel / 本地就失效了。

所以要真正跑起来,请选自带 Key 的通道。 想修的话,最小改法是在 vite.config.ts 里补:

export default defineConfig({
  plugins: [react()],
  define: { 'process.env.API_KEY': JSON.stringify(process.env.API_KEY ?? '') },
});

(注意:这样会把 Key 打进前端产物并公开。纯前端应用要藏 Key,就得加一个后端转发, 参见本账号的 Gemini-3.5-trigger 是怎么用 Vercel Serverless 做服务端转发的。)

两条通道的 JSON 约束方式不同

这是本项目里最值得看的一段设计:结构化输出靠的是两套完全不同的机制。

  • Gemini 通道用 SDK 原生的 responseSchema(services/geminiService.ts 里定义了 4 个 Schema: RESPONSE_SCHEMA 三个方案、ACTS_SCHEMA 节点数组、REFINE_SCHEMA 单节点细化、ANALYSIS_SCHEMA 文档解析), 同时把 responseMimeType 设成 application/json。由模型侧保证 JSON 合法,前端不用做容错解析。
  • DeepSeek 通道没有 schema 可用,只能靠两手:
    1. 在系统提示里追加「请务必以严格的 JSON 格式返回结果,不要包含 Markdown 代码块标记(如 ```json)」;
    2. 请求体带上 response_format: { type: "json_object" };
    3. 前端再兜一层 text.replace(/```json/g,'').replace(/```/g,'').trim() 之后才 JSON.parse。

这里有个已知冲突:提示词向 DeepSeek 要的是 JSON 数组([{...}]), 但 response_format 的 json_object 模式只保证返回对象。两者同时下发可能互相干扰, 属于该通道稳定性不如 Gemini 的原因之一。


技术栈

  • React 19.2 —— 全函数组件 + Hooks,没有引入路由(视图切换靠 AppView 状态机),也没有全局状态库。
  • TypeScript 5.8 —— tsconfig.json 开了 isolatedModules、moduleDetection: force、skipLibCheck, 并配了 @/* 路径别名;noEmit: true,编译交给 Vite。
  • Vite 7 —— 构建与开发服务器,仅一个 @vitejs/plugin-react 插件。
  • Tailwind CSS(Play CDN) —— 通过 https://cdn.tailwindcss.com 运行时常量, 不是 PostCSS 构建;自定义配置(字体栈、fadeIn 关键帧)写在 index.html 的内联 tailwind.config 里。
  • @google/genai 1.35 —— 官方新一代 SDK(GoogleGenAI + Type.Type.* Schema 类型)。
  • mammoth.js 1.6.0 —— 仅在浏览器端用于 .docx 取纯文本,从 cdnjs 加载,用 declare var mammoth: any 声明。
  • Noto Sans SC —— 从 Google Fonts 加载,字体栈再回落 PingFang SC / 微软雅黑。
  • 无后端、无数据库、无账号体系 —— 全部计算在浏览器里完成。

index.html 里还有一份 importmap,把 react / react-dom / @google/genai / vite 指到 esm.sh。这是 AI Studio 模板留下的浏览器原生 ESM 兜底方案,与 Vite 打包并存,正常构建时不影响产物。


快速开始

环境要求

  • Node.js 18 以上(Vite 7 要求)
  • 一个 Gemini 或 DeepSeek 的 API Key

本地运行

# 1. 取代码
git clone https://github.com/kuroshio4396/-.git
cd ./-        # 仓库名就是一个连字符,目录名就是它

# 2. 装依赖
npm install

# 3. 起开发服务器
npm run dev

打开终端提示的地址(Vite 默认 http://localhost:5173),点右上角「配置 AI」, 选 Google Gemini (自选 Key) 或 DeepSeek (自选 Key) 并粘贴 Key,保存即可。

可用的 npm 脚本

npm run dev        # 开发服务器(vite)
npm run build      # 生产构建(vite build),产物在 dist/
npm run preview    # 本地预览构建产物(vite preview)

项目没有配置 lint / test / typecheck 脚本。

关于 .env.local

模板自带的 README 让你在 .env.local 里配 GEMINI_API_KEY——对本项目无效。 .gitignore 确实忽略了 *.local,但代码里读的是 process.env.API_KEY(名字不同), 而且 vite.config.ts 没有把任何环境变量注入前端。照那个说明做不会有任何效果, 请直接用设置面板填 Key。

一个容易忽略的细节:仓库名带连字符

git clone 下来的目录名就是 -,直接 cd - 在多数 shell 里会被解释成「切回上一个目录」。 用 cd ./- 或 cd -- -。


部署

项目是纯静态站点,构建产物 dist/ 可以丢到任何静态托管上。

以 Vercel 为例:

  1. 在 Vercel 里 Import 这个仓库;
  2. Framework Preset 选 Vite(Vercel 通常能自动识别),Build Command npm run build,Output Directory dist;
  3. 不需要配置任何环境变量(配了也不生效,见上文);
  4. 部署完成后,打开站点 → 配置 AI → 填自己的 Key。

仓库里没有 vercel.json,也没有 .env.example。线上那份 demo 就是按上述方式部署的。

其他静态托管(Netlify / Cloudflare Pages / GitHub Pages)同理,把输出目录指向 dist 即可。 注意 Tailwind 与 mammoth 是从 CDN 加载的,站点会依赖外网——离线环境下样式与 docx 解析都会失效。


内部实现

五个 AI 能力,都收敛在一个函数里

services/geminiService.ts 导出五个业务函数,它们全部经由内部私有函数 callAI() 转发:

函数 干什么 回给谁
generateScriptPlans 生成 3 个候选方案(可带 avoidTitles 禁用名单) 构思规划页
extendScriptStructure 顺着已有节点续写 2–3 个新节点 结构编辑器「AI 继续生成剧情」
refineScriptAct 细化单个节点,返回一段新的 description 节点的「细化」按钮
splitScriptAct 把单个节点拆成 2–4 个连续节点 节点的「拆分」按钮
analyzeFileContent 从上传的 docx/txt 里抽取四个字段 灵感输入页的「AI 导入」

callAI(prompt, settings, schema?, systemInstruction?) 按 settings.provider 分流:

  • 系统提示默认值:你是一个专业的创意写作AI助手。
  • 所有提示词都明确要求「语言必须是【简体中文】」,所以产出不会中英混杂。
  • 角色列表通过 formatCharacters() 统一格式化成 1. [主角] 张三: 描述 的多行块; 角色为空时会替换成「未提供,请自行发挥」,把决定权交回模型。

提示词里几个值得注意的处理

  • 三个方向必须分化:要求「每个方案必须有独特的切入点(例如:方案1侧重悬疑,方案2侧重情感,方案3侧重动作)」。
  • 「重做」时才注入禁用名单,并在提示词里说明这是用户点了重做。首次生成不带这个段落。
  • 细化时提供「前情回顾」:把目标节点之前的全部节点按顺序列出当上下文; 若目标就是第一个节点,则替换为「这是故事的开篇。」。
  • 拆分要求时间连续:明确「拆分后的节点必须保持时间上的连续性,能够平滑地替换原节点」。
  • 文档解析要求推断角色定位:模型要自己判断每个角色属于主角 / 配角 / 反派BOSS / 群演。

ID 生成策略不统一

  • 方案与 AI 生成的节点用 ${Date.now()}-${index} 一类的时间戳 ID;
  • 编辑器里手动「添加空白节点」与 TXT 导入的节点用 Math.random().toString(36).substr(2, 9) (substr 已废弃,建议换 slice)。

两套 ID 混在同一个数组里,功能上没问题(只用作 React key),但排查时会略绕。


数据结构

types.ts 只有 40 来行,是整个应用的数据契约:

类型 字段
CharacterRole 联合类型:主角 / 主要配角 / 反派BOSS / 次要配角 / 群演
Character id · name · description · role
UserInputs worldSetting · characters: Character[] · plotSnippets · theme
ScriptAct id? · title · description
ScriptPlan id · title · logline · synopsis · tone · acts: ScriptAct[] · mainConflict
AppView input / selection / outline / structure_editor
AIProvider default / gemini / deepseek
AISettings provider · apiKey · baseUrl?

AISettings.baseUrl 是为「OpenAI 兼容端点」预留的字段,但设置面板并不提供编辑入口, 代码里也从未读取它——目前是死字段。


项目结构

.
├── index.html                    # 入口 HTML:Tailwind Play CDN、mammoth CDN、importmap、字体、内联 tailwind.config
├── index.tsx                     # React 挂载点(StrictMode)
├── App.tsx                       # 顶层:AppView 状态机 + 全局状态 + 设置持久化 + 页头/页脚
├── types.ts                      # 全部接口与联合类型
├── metadata.json                 # AI Studio 元数据(name / description)
├── package.json                  # 依赖与三个 npm 脚本
├── tsconfig.json                 # TS 配置(noEmit、@/* 别名)
├── vite.config.ts                # Vite 配置(只有 react 插件)
├── .gitignore
├── components/
│   ├── InputForm.tsx             # 第一步:四字段表单 + 角色卡列表 + AI 导入
│   ├── PlanSelection.tsx         # 第二步:三方案卡片网格 + 重做
│   ├── OutlineView.tsx           # 第三步:可编辑大纲 + 幕结构预览
│   ├── StructureEditor.tsx       # 第四步:节点编辑 + 细化/拆分/续写 + TXT 导入导出
│   └── SettingsModal.tsx         # AI 通道与 Key 配置弹窗
└── services/
    └── geminiService.ts          # 4 个 Schema + callAI 分流 + 5 个业务函数

数据与隐私

  • Key 存在浏览器里,明文。 使用 localStorage.setItem('ai_script_settings', JSON.stringify({provider, apiKey})), 没有加密也没有过期。设置面板的提示是准确的:「您的 Key 仅存储在本地浏览器中,不会上传至任何第三方服务器」—— 但要注意它会被直接发给 Google 或 DeepSeek(这本来就是它存在的意义)。 共用电脑上使用时,记得用完清掉。
  • 你的灵感与大纲不落任何盘。 输入内容、角色、生成的方案、改过的大纲全都只活在 React 内存状态里, 刷新页面即全部丢失,没有草稿自动保存、没有历史版本。写到大纲阶段后请及时「导出TXT」。
  • 没有后端。 自带 Key 时浏览器直连 generativelanguage.googleapis.com 或 api.deepseek.com, 中间没有任何属于本项目的服务器。
  • 没有任何埋点。 页面里只有 Tailwind CDN、mammoth CDN、Google Fonts 和自身的 JS 产物, 没有统计脚本、没有 cookie、没有第三方 SDK。
  • 上传的文件不出浏览器之外。 docx 由 mammoth 在本地解成纯文本, 只有抽取后的文本会被送去模型;原文件不会被上传。

使用技巧

  • 先跑通通道再投入内容。 第一次用,先随便填一句主题、点生成,确认返回正常再认真填。 否则填了半天才发现通道没配好,白写。
  • 「重做」是好东西,别只点一次。 禁用列表会累积,第三、四轮往往才出现真正意外的方向。
  • 把「重做」当灵感发散器用:即使对前三版都不满意,它们的标题与简介本身也能帮你确认自己不想要什么。
  • 结构编辑器里「拆分」比「细化」更值钱。 三个阶段性的节点太粗时,拆一次就能得到可直接开写的颗粒度; 「细化」适合已经想清楚、只缺描写的节点。
  • 先导出再大改。 TXT 导入是全量覆盖且没有撤销,改之前先导出留个底。
  • 要换电脑或换浏览器,记得手动搬 Key——localStorage 不会跟着走。
  • .txt 请用 UTF-8 编码保存。 读取走的是 File.text(),GBK 编码的老文本会读成乱码。
  • 移动端体验有限:节点工具条在触屏上虽常驻,但底部固定操作条会占掉一部分屏幕高度, 建议在桌面浏览器里做重度编辑。

常见问题

点了「生成剧本规划」,弹出「生成失败,请检查网络设置、API Key 是否正确,或稍后再试。」

最可能的原因是你还在「默认」通道上。这个通道在自部署环境下拿不到 Key,必然失败。

点右上角「配置 AI」,切到 Google Gemini (自选 Key) 或 DeepSeek (自选 Key),填入自己的 Key 再试。 如果已经切了通道还是失败,依次检查:Key 是否复制完整(前后别带空格)、Key 是否还有余额/配额、 所选通道与 Key 的归属是否匹配(别把 DeepSeek 的 Key 填进 Gemini 通道)。

为什么设置里写「调用 gemini-2.5-pro 模型」,抓包却看到别的模型名?

因为面板文案与实现不一致。源码里 Gemini 通道写死的是 gemini-2.0-flash, 页脚显示的「Powered by Gemini 2.5 Pro」也同样只是文案。 geminiService.ts 里留着一串注释,说明作者原本想用 2.5-pro,但担心 SDK 解析不到该模型名而保守地留在了 flash。 实际请求以代码为准:默认通道是 gemini-3-flash-preview,自选 Key 的 Gemini 通道是 gemini-2.0-flash。

刷新页面后我填的内容和大纲都没了

这是当前设计,不是 bug——只有 AI 配置会被持久化,输入与生成结果都只在内存里。 在结构编辑器里点「导出TXT」可以把节点存下来。

上传了 docx,结果角色出现了两份

导入时角色是追加而非去重合并。同一个文件导入两次就会有重复项,手动删掉多余的即可。 如果只想让 AI 补世界观和剧情、不想重复角色,导入前先把角色卡清空。

「AI 导入」报「导入失败,请重试或检查文件格式(支持 .txt 和 .docx)」

先确认扩展名确实是 .docx(老的 .doc 是另一种二进制格式,mammoth 处理不了), .txt 请用 UTF-8 保存。另外这个提示也会在模型调用失败时出现—— 因为导入流程包含一次 AI 分析,通道没配好同样会走到这里。

导入 TXT 之后,大纲的标题/简介/核心冲突没变

符合预期。导入只替换节点列表,不读取导出文件里的「风格」「核心冲突」等行, 更不会恢复从未被导出的「一句话梗概」与「故事简介」。这些字段以页面上的现值先为准。

提示里说「最多返回 2-3 个节点」,为什么有时一次给了 4 个?

提示词只是软约束,模型不保证严格遵守数量。返回几个就追加几个,多余或不足都可以手工增删。 同理「拆成 2-4 个」也不保证。

为什么点开「配置 AI」时,Key 输入框有时候不显示?

选「默认」通道时设计上就隐藏 Key 输入框(该通道本意是不需要 Key)。 一旦切到 Gemini 或 DeepSeek,输入框会立刻出现。

节点顺序不对,怎么拖动?

不支持拖拽排序。 顺序就是数组顺序,只能删除后重新添加。 如果想精确定序,可以「导出TXT」→ 在文本编辑器里调整段落顺序 → 「导入TXT (覆盖)」。

这个项目会替我把正文也写出来吗?

不会。它的产出止于幕/节点级的情节描述,定位是「大纲与结构工具」。 节点描述的颗粒度通常在几句话到一小段之间,适合作为正式写作的施工图。


已知限制与工程遗留

以下是读代码时确认存在的问题,如实列出,均未在本仓库中擅自修改:

功能性

  1. 「默认(内置)」通道完全不可用 —— process.env.API_KEY 在 Vite 构建下取不到值,SDK 直接抛错。 要么修 vite.config.ts 的 define(但会公开 Key),要么加服务端转发,要么把这个选项从面板上去掉。
  2. 面板文案与实现不符 —— 设置里写「调用 gemini-2.5-pro」,实际是 gemini-2.0-flash;页脚的「Gemini 2.5 Pro」同理。
  3. DeepSeek 通道的 JSON 约束自相矛盾 —— 提示词要数组,response_format 要对象。
  4. 无任何持久化 —— 输入与大纲刷新即丢,也没有自动草稿。
  5. TXT 导出不完整 —— 不包含「一句话梗概」与「故事简介」,往返只能保住节点。
  6. AI 导入的角色不去重 —— 重复导入产生重复角色卡。
  7. 无拖拽排序 —— 节点顺序只能通过删建或 TXT 往返调整。
  8. 无撤销/重做 —— 尤其 TXT 导入是破坏性覆盖,误操作只能靠事先导出。
  9. 并发保护只有一半 —— 全局只挡一个「细化/拆分」,但「AI 继续生成剧情」与「AI 导入」之间没有互斥。
  10. 长文档静默截断 —— 超过 100000 字符的部分被丢弃且不提示。
  11. txt 读取无编码探测 —— File.text() 按 UTF-8 解,GBK 文件会乱码。
  12. 离线不可用 —— Tailwind 与 mammoth 都走 CDN,样式与 docx 解析依赖外网。

工程性

  1. 错误处理全部靠 alert —— 没有错误边界(Error Boundary),也没有行内错误提示; 模型返回的非法 JSON 会以原始 SyntaxError 冒泡。
  2. 框架外依赖链脆弱 —— 核心样式来自 CDN 的 Tailwind Play CDN(官方明确不建议用于生产), 一旦该 CDN 不可达,界面会退化成无样式 HTML。
  3. package.json 有重复与版本冲突 —— @vitejs/plugin-react 同时列在 dependencies(^5.1.2) 与 devDependencies(^5.0.0);vite 同样重复(^7.3.1 与 ^6.2.0)。装上之后实际生效的是哪一版取决于 npm 的解析结果。
  4. 包名是构建工具生成的 —— name 为 copy-of-copy-of-灵感剧本规划器-版本3,带有明显的复制痕迹。
  5. AISettings.baseUrl 是死字段 —— 类型里有,面板不给改,代码不读。
  6. ID 生成策略混用 —— 时间戳与 Math.random().substr(2,9) 并存,substr 已废弃。
  7. 导入正则有笔误 —— 字符类写成了 [0-90-9一二三四五六七八九十百](0-9 重复了一次),功能正常但不严谨。
  8. 没有测试、没有 lint、没有 CI —— 三个 npm 脚本只覆盖 dev/build/preview。
  9. index.html 里的 importmap 与 Vite 打包并存 —— AI Studio 模板遗留,属于历史包袱。
  10. declare var mammoth: any —— 依赖 CDN 全局变量且完全放弃类型,typeof mammoth === 'undefined' 才做检查。

其他

  1. 仓库名为 -,不利检索;
  2. 没有 LICENSE 文件(见下节)。

版本历史

全部提交集中在 2026-01-09,共 3 次,作者 kuroshio4396:

提交 时间 (UTC) 说明
883d63e 2026-01-09 08:04 Initial commit
52187db 2026-01-09 08:07 feat: Initialize project structure and dependencies
e727898 2026-01-09 15:42 feat: Update dependencies and fix Vite build

从提交信息可以看出:项目是当天从 Google AI Studio 导出后直接推上来的, 第三次提交修过一次 Vite 构建配置(index.html 里那句 「关键修复:添加入口文件引用,确保 Vite 能打包 TypeScript 代码」的注释应该就来自那一次)。 此后没有新的提交,本文档是该仓库的第一次内容性更新。


许可证

本仓库目前没有 LICENSE 文件,因此默认「保留所有权利」——他人不能合法地复制、修改或分发。

如果你是作者并希望放开,最简单的做法是在仓库根目录添加一个 LICENSE 文件:

  1. 打开 https://choosealicense.com/ 选一个协议(开源常用 MIT / Apache-2.0,中文项目也常用 MPL-2.0);
  2. 新建 LICENSE 文件,把协议全文粘进去,填好年份与署名;
  3. 提交后 GitHub 会自动在仓库页右侧识别并显示协议名。

需要注意,package.json 里也没有 license 字段,建议一并补上以保持一致。


致谢


灵感剧本规划器 · 把碎片化的灵感,长成一份能往下拍的剧本大纲

About

灵感剧本规划器 —— 把碎片化灵感长成完整剧本大纲的 AI 工作台。四步流程:填世界观 / 角色 / 剧情片段(或上传 docx·txt 由 AI 自动抽取)→ 一次生成 3 个方向迥异的候选方案 → 选定后可逐字编辑大纲 → 结构编辑器里对单个节点 AI 细化、拆分、续写,并支持 TXT 导入导出。可接 Gemini / DeepSeek 通道。

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages