@cloud411716/fancy-webnovel 2.2.0 → 3.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/README.md +65 -0
  2. package/client.js +201 -15
  3. package/cordis.patch.yml +5 -1
  4. package/docs/douyin-plugin.md +118 -0
  5. package/docs/material-library/README.md +404 -0
  6. package/docs/material-library/import.js +437 -0
  7. package/docs/material-library/prompt.md +370 -0
  8. package/docs/material-library/queries.surql +94 -0
  9. package/docs/material-library/sample.jsonl +14 -0
  10. package/docs/material-library/schema.surql +46 -0
  11. package/docs/mcp-profile.example.yml +24 -0
  12. package/docs/story-graph-design.md +602 -0
  13. package/docs/story-graph-validation.md +81 -0
  14. package/docs/workspace-mcp.md +284 -0
  15. package/index.js +12 -7
  16. package/lib/atomic-write.js +109 -0
  17. package/lib/chapter-order.js +100 -0
  18. package/lib/confirmation.js +80 -0
  19. package/lib/db-init.js +123 -0
  20. package/lib/deployed-state.js +11 -11
  21. package/lib/direction-labels.js +265 -0
  22. package/lib/error-codes.js +31 -31
  23. package/lib/infra.js +29 -4
  24. package/lib/material-db.js +149 -0
  25. package/lib/path-guard.js +150 -0
  26. package/lib/projects.js +400 -0
  27. package/lib/scraper-utils.js +140 -0
  28. package/lib/surreal-server.js +338 -0
  29. package/lib/topic-decision.js +860 -0
  30. package/lib/topic-fields.js +251 -0
  31. package/lib/topic-schema.js +225 -0
  32. package/lib/workspace.js +501 -0
  33. package/package.json +31 -7
  34. package/plugins/fancy-analyze/analyze-definitions.md +519 -0
  35. package/plugins/fancy-analyze/index.js +1128 -77
  36. package/plugins/fancy-analyze/ingest-coverage.js +107 -0
  37. package/plugins/fancy-analyze/schema-analyze.md +355 -107
  38. package/plugins/fancy-analyze/scripts/append-summary-chapter.js +612 -0
  39. package/plugins/fancy-analyze/summary-progress.js +71 -7
  40. package/plugins/fancy-analyze/templates.js +437 -139
  41. package/plugins/fancy-bootstrap/index.js +276 -131
  42. package/plugins/fancy-bootstrap/scripts/init.js +59 -25
  43. package/plugins/fancy-bootstrap/templates.js +35 -18
  44. package/plugins/fancy-db/index.js +172 -0
  45. package/plugins/fancy-douyin/deploy.js +134 -0
  46. package/plugins/fancy-douyin/index.js +551 -0
  47. package/plugins/fancy-douyin/prompts/01-analysis-prompt.md +84 -0
  48. package/plugins/fancy-douyin/scripts/crawl-note.cjs +194 -0
  49. package/plugins/fancy-douyin/scripts/lib.cjs +197 -0
  50. package/plugins/fancy-douyin/scripts/scan-account.cjs +128 -0
  51. package/plugins/fancy-douyin/scripts/start-chrome.cjs +203 -0
  52. package/plugins/fancy-douyin/state.js +197 -0
  53. package/plugins/fancy-douyin/templates.js +127 -0
  54. package/plugins/fancy-export/exporter.js +805 -0
  55. package/plugins/fancy-export/index.js +63 -0
  56. package/plugins/fancy-import/import-driver.js +29 -6
  57. package/plugins/fancy-import/index.js +13 -5
  58. package/plugins/fancy-import/prompts/01-import-prompt.md +39 -14
  59. package/plugins/fancy-import/templates.js +17 -8
  60. package/plugins/fancy-material/exporter.js +153 -0
  61. package/plugins/fancy-material/genres.js +256 -0
  62. package/plugins/fancy-material/index.js +835 -0
  63. package/plugins/fancy-material/inventory.js +145 -0
  64. package/plugins/fancy-material/kinds.js +313 -0
  65. package/plugins/fancy-material/queue.js +55 -0
  66. package/plugins/fancy-material/scripts/query.js +254 -0
  67. package/plugins/fancy-material/templates.js +258 -0
  68. package/plugins/fancy-material/tool-deploy.js +138 -0
  69. package/plugins/fancy-material/validate.js +149 -0
  70. package/plugins/fancy-project/index.js +133 -0
  71. package/plugins/fancy-scan/existing-block.js +100 -0
  72. package/plugins/fancy-scan/index.js +410 -21
  73. package/plugins/fancy-scan/prompts/01-analysis-prompt.md +302 -0
  74. package/plugins/fancy-scan/scripts/append-block.js +725 -0
  75. package/plugins/fancy-scan/scripts/report-validate.js +824 -0
  76. package/plugins/fancy-scan/scripts/run-scan.js +169 -22
  77. package/plugins/fancy-scan/scripts/scrapers/fanqie-rank-scraper.js +145 -16
  78. package/plugins/fancy-scan/scripts/scrapers/jjwxc-rank-scraper.js +179 -49
  79. package/plugins/fancy-scan/scripts/scrapers/qimao-rank-scraper.js +38 -20
  80. package/plugins/fancy-scan/templates/blocks.js +155 -0
  81. package/plugins/fancy-scan/templates/platforms.js +372 -0
  82. package/plugins/fancy-scan/templates/report-schema.js +90 -0
  83. package/plugins/fancy-scan/templates.js +545 -146
  84. package/plugins/fancy-scan/tool-deploy.js +335 -0
  85. package/plugins/fancy-scan/wait-turn.js +46 -0
  86. package/plugins/fancy-story-builder/db/README.md +106 -0
  87. package/plugins/fancy-story-builder/db/client.js +228 -0
  88. package/plugins/fancy-story-builder/db/commands.js +145 -0
  89. package/plugins/fancy-story-builder/db/ids.js +39 -0
  90. package/plugins/fancy-story-builder/db/import.js +798 -0
  91. package/plugins/fancy-story-builder/db/project.js +299 -0
  92. package/plugins/fancy-story-builder/db/query.js +361 -0
  93. package/plugins/fancy-story-builder/db/schema.js +417 -0
  94. package/plugins/fancy-story-builder/db/stage-sync.js +238 -0
  95. package/plugins/fancy-story-builder/flesh-output.js +19 -6
  96. package/plugins/fancy-story-builder/index.js +4105 -396
  97. package/plugins/fancy-story-builder/patch-driver.js +390 -0
  98. package/plugins/fancy-story-builder/prompts/01-skeleton-prompt.md +29 -19
  99. package/plugins/fancy-story-builder/prompts/02-flesh-prompt.md +72 -10
  100. package/plugins/fancy-story-builder/prompts/03-rhythm-prompt.md +40 -5
  101. package/plugins/fancy-story-builder/prompts/04-writing-prompt.md +37 -0
  102. package/plugins/fancy-story-builder/purpose-template.md +42 -66
  103. package/plugins/fancy-story-builder/references/formulas-skeleton.json +229 -56
  104. package/plugins/fancy-story-builder/references/genre-profiles.json +3514 -0
  105. package/plugins/fancy-story-builder/references/glossary.md +15 -6
  106. package/plugins/fancy-story-builder/references/index.md +97 -6
  107. package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/00-index.md +1 -1
  108. package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/07-audit.md +25 -7
  109. package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/01-character.md +5 -1
  110. package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/03-relations.md +6 -2
  111. package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/05-audit.md +15 -5
  112. package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/00-index.md +1 -1
  113. package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/01-positioning.md +2 -1
  114. package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/03-entities.md +1 -1
  115. package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/06-audit.md +19 -1
  116. package/plugins/fancy-story-builder/schema-setting.json +1100 -0
  117. package/plugins/fancy-story-builder/schema-setting.md +201 -293
  118. package/plugins/fancy-story-builder/scripts/brief-validate.js +164 -0
  119. package/plugins/fancy-story-builder/scripts/content-quality.js +4 -1
  120. package/plugins/fancy-story-builder/scripts/flesh-validate.js +135 -12
  121. package/plugins/fancy-story-builder/scripts/graph-utils.js +146 -47
  122. package/plugins/fancy-story-builder/scripts/reference-log.js +110 -0
  123. package/plugins/fancy-story-builder/scripts/rhythm-validate.js +100 -4
  124. package/plugins/fancy-story-builder/scripts/schema-pages.js +2668 -0
  125. package/plugins/fancy-story-builder/scripts/semantic-lint.js +5 -1
  126. package/plugins/fancy-story-builder/scripts/skeleton-validate.js +158 -17
  127. package/plugins/fancy-story-builder/scripts/stage-batches.js +36 -2
  128. package/plugins/fancy-story-builder/scripts/stage-contract.js +12 -0
  129. package/plugins/fancy-story-builder/scripts/story-artifacts.js +933 -0
  130. package/plugins/fancy-story-builder/scripts/upstream-query.js +1 -1
  131. package/plugins/fancy-story-builder/scripts/v2/baseline.js +112 -0
  132. package/plugins/fancy-story-builder/scripts/v2/cast.js +517 -0
  133. package/plugins/fancy-story-builder/scripts/v2/context.js +434 -0
  134. package/plugins/fancy-story-builder/scripts/v2/contracts.js +493 -0
  135. package/plugins/fancy-story-builder/scripts/v2/core.js +419 -0
  136. package/plugins/fancy-story-builder/scripts/v2/engine.js +1068 -0
  137. package/plugins/fancy-story-builder/scripts/v2/export-markdown.js +794 -0
  138. package/plugins/fancy-story-builder/scripts/v2/genre.js +195 -0
  139. package/plugins/fancy-story-builder/scripts/v2/knowledge.js +321 -0
  140. package/plugins/fancy-story-builder/scripts/v2/macro.js +409 -0
  141. package/plugins/fancy-story-builder/scripts/v2/manifest.js +538 -0
  142. package/plugins/fancy-story-builder/scripts/v2/migrate.js +137 -0
  143. package/plugins/fancy-story-builder/scripts/v2/prompts.js +1335 -0
  144. package/plugins/fancy-story-builder/scripts/v2/rhythm.js +618 -0
  145. package/plugins/fancy-story-builder/scripts/v2/scale.js +843 -0
  146. package/plugins/fancy-story-builder/scripts/v2/snapshot.js +121 -0
  147. package/plugins/fancy-story-builder/scripts/v2/stages.js +273 -0
  148. package/plugins/fancy-story-builder/scripts/v2/writing.js +216 -0
  149. package/plugins/fancy-story-builder/storydb/README.md +271 -0
  150. package/plugins/fancy-story-builder/storydb/cli.js +458 -0
  151. package/plugins/fancy-story-builder/storydb/client.js +351 -0
  152. package/plugins/fancy-story-builder/storydb/compile.js +452 -0
  153. package/plugins/fancy-story-builder/storydb/context.js +288 -0
  154. package/plugins/fancy-story-builder/storydb/definitions.js +699 -0
  155. package/plugins/fancy-story-builder/storydb/driver.js +106 -0
  156. package/plugins/fancy-story-builder/storydb/dsh-tools.js +313 -0
  157. package/plugins/fancy-story-builder/storydb/export.js +175 -0
  158. package/plugins/fancy-story-builder/storydb/ids.js +31 -0
  159. package/plugins/fancy-story-builder/storydb/impact.js +170 -0
  160. package/plugins/fancy-story-builder/storydb/import.js +559 -0
  161. package/plugins/fancy-story-builder/storydb/mcp.js +99 -0
  162. package/plugins/fancy-story-builder/storydb/prompts.js +952 -0
  163. package/plugins/fancy-story-builder/storydb/stage-command.js +379 -0
  164. package/plugins/fancy-story-builder/storydb/stage.js +426 -0
  165. package/plugins/fancy-story-builder/storydb/store.js +891 -0
  166. package/plugins/fancy-story-builder/storydb/tools.js +592 -0
  167. package/plugins/fancy-story-builder/storydb/validation.js +183 -0
  168. package/plugins/fancy-story-builder/templates/v2/10-scale/scale-template.json +144 -0
  169. package/plugins/fancy-story-builder/templates/v2/20-macro/emotion-template.json +15 -0
  170. package/plugins/fancy-story-builder/templates/v2/20-macro/endings-template.json +16 -0
  171. package/plugins/fancy-story-builder/templates/v2/20-macro/lines-template.json +28 -0
  172. package/plugins/fancy-story-builder/templates/v2/20-macro/roles-template.json +10 -0
  173. package/plugins/fancy-story-builder/templates/v2/20-macro/story-template.json +19 -0
  174. package/plugins/fancy-story-builder/templates/v2/20-macro/volumes-template.json +24 -0
  175. package/plugins/fancy-story-builder/templates/v2/20-macro/world-template.json +24 -0
  176. package/plugins/fancy-story-builder/templates/v2/30-cast/characters-template.json +49 -0
  177. package/plugins/fancy-story-builder/templates/v2/30-cast/climaxes-template.json +14 -0
  178. package/plugins/fancy-story-builder/templates/v2/30-cast/conflicts-template.json +30 -0
  179. package/plugins/fancy-story-builder/templates/v2/30-cast/factions-template.json +18 -0
  180. package/plugins/fancy-story-builder/templates/v2/30-cast/foreshadows-template.json +29 -0
  181. package/plugins/fancy-story-builder/templates/v2/30-cast/information-template.json +29 -0
  182. package/plugins/fancy-story-builder/templates/v2/30-cast/items-template.json +13 -0
  183. package/plugins/fancy-story-builder/templates/v2/30-cast/locations-template.json +12 -0
  184. package/plugins/fancy-story-builder/templates/v2/30-cast/payoffs-template.json +16 -0
  185. package/plugins/fancy-story-builder/templates/v2/30-cast/relations-template.json +44 -0
  186. package/plugins/fancy-story-builder/templates/v2/40-rhythm/chapter-map-template.json +81 -0
  187. package/plugins/fancy-story-builder/templates/v2/40-rhythm/outline-template.json +162 -0
  188. package/plugins/fancy-story-builder/templates/v2/50-writing/ledger-template.json +46 -0
  189. package/plugins/fancy-story-builder/templates/v2/60-deslop/report-template.json +10 -0
  190. package/plugins/fancy-story-builder/templates//350/212/202/345/245/217//346/227/266/345/272/217/346/230/240/345/260/204/346/250/241/346/235/277.json +267 -44
  191. package/plugins/fancy-story-builder/templates//350/241/200/350/202/211//344/270/226/347/225/214/350/247/202/350/247/204/345/210/231/346/250/241/346/235/277.json +121 -19
  192. package/plugins/fancy-story-builder/templates//350/241/200/350/202/211//345/205/263/351/224/256/351/201/223/345/205/267/346/250/241/346/235/277.json +129 -21
  193. package/plugins/fancy-story-builder/templates//350/241/200/350/202/211//345/205/266/344/273/226/345/256/236/344/275/223/346/250/241/346/235/277.json +62 -0
  194. package/plugins/fancy-story-builder/templates//350/241/200/350/202/211//345/206/262/347/252/201/346/250/241/346/235/277.json +76 -17
  195. package/plugins/fancy-story-builder/templates//350/241/200/350/202/211//345/211/247/346/203/205/346/250/241/346/235/277.json +56 -8
  196. package/plugins/fancy-story-builder/templates//350/241/200/350/202/211//345/212/277/345/212/233/346/250/241/346/235/277.json +63 -13
  197. package/plugins/fancy-story-builder/templates//350/241/200/350/202/211//345/234/260/347/202/271/346/250/241/346/235/277.json +34 -6
  198. package/plugins/fancy-story-builder/templates//350/241/200/350/202/211//347/273/223/345/261/200/346/250/241/346/235/277.json +81 -18
  199. package/plugins/fancy-story-builder/templates//350/241/200/350/202/211//350/247/222/350/211/262/346/250/241/346/235/277.json +202 -42
  200. package/plugins/fancy-story-builder/templates//351/252/250/346/236/266//345/205/263/347/263/273/346/250/241/346/235/277.json +160 -27
  201. package/plugins/fancy-story-builder/templates//351/252/250/346/236/266//345/256/236/344/275/223/346/250/241/346/235/277.json +217 -41
  202. package/plugins/fancy-story-builder/templates//351/252/250/346/236/266//346/241/206/346/236/266/346/250/241/346/235/277.json +215 -41
  203. package/plugins/fancy-story-builder/templates.js +272 -74
  204. package/plugins/fancy-story-builder/wiki-references.js +429 -93
  205. package/plugins/fancy-story-builder/writing-driver.js +336 -9
  206. package/plugins/fancy-topic/index.js +414 -61
  207. package/plugins/fancy-topic/prompts/01-exploration-prompt.md +115 -0
  208. package/plugins/fancy-topic/scripts/save-suggestion.js +219 -0
  209. package/plugins/fancy-topic/scripts/select-topic.js +106 -63
  210. package/plugins/fancy-topic/templates.js +225 -122
  211. package/plugins/fancy-topic/tool-deploy.js +200 -0
  212. package/lib/safe-rm.js +0 -94
package/README.md ADDED
@@ -0,0 +1,65 @@
1
+ # fancy-webnovel
2
+
3
+ DeepSeek Harness(DSH)网文创作插件组合包。它把工作区初始化、扫榜、选题、拆书、故事构建、素材库和抖音图文采集注册成 `/fancy-*` 命令。
4
+
5
+ ## 环境
6
+
7
+ - Node.js >= 18
8
+ - DSH `>=0.1.5-rc.2 <0.2.0 || >=0.2.2-rc.3 <0.3.0`
9
+ - 需要图形桌面时(首次抖音登录、番茄 OCR 截图)要能打开 Chrome
10
+
11
+ ## 安装
12
+
13
+ ```bash
14
+ dsh plugin --profile web add @cloud411716/fancy-webnovel
15
+ ```
16
+
17
+ 安装后确认:
18
+
19
+ ```bash
20
+ dsh --profile web --dump-config
21
+ ```
22
+
23
+ 应能看到 `# == @cloud411716/fancy-webnovel` 这一层。
24
+
25
+ 如果 pnpm 报:
26
+
27
+ ```text
28
+ ERR_PNPM_IGNORED_BUILDS Ignored build scripts: tesseract.js@7.0.0
29
+ ```
30
+
31
+ 编辑 profile 的 `pnpm-workspace.yaml`:
32
+
33
+ ```yaml
34
+ allowBuilds:
35
+ tesseract.js: true
36
+ ```
37
+
38
+ 然后重新执行安装。`tesseract.js` 的 postinstall 只是 OpenCollective 提示;不允许构建时 OCR 模式不可用,但插件其余功能不受影响。
39
+
40
+ ## 依赖解析
41
+
42
+ 依赖声明在 `dependencies` / `peerDependencies` 中。用 `dsh plugin add` 从 npm 安装时,
43
+ 第三方依赖会进入 profile 的 `node_modules`,而不是插件包目录;Node 的向上查找会从
44
+ 插件包解析到这些依赖。插件自带的 `node_modules` 只用于本地 checkout 和测试,不是运行前提。
45
+
46
+ 需要与宿主共享实例的 DSH 包使用 `peerDependencies`(并保留 `devDependencies` 供本地测试),
47
+ 不会被插件重复安装。
48
+
49
+ ## 主要命令
50
+
51
+ ```text
52
+ /fancy-bootstrap
53
+ /fancy-scan
54
+ /fancy-topic
55
+ /fancy-analyze
56
+ /fancy-import
57
+ /fancy-export
58
+ /fancy-project
59
+ /fancy-db
60
+ /fancy-material
61
+ /fancy-douyin
62
+ /fancy-build
63
+ ```
64
+
65
+ 具体用法见各命令的 `/fancy-* help` 和 `docs/`。
package/client.js CHANGED
@@ -32,8 +32,9 @@
32
32
  * 2) 注册一个自己的 conversation-node Definition(kind = "fancy-notice"),
33
33
  * 匹配 command/run + command/done,只对 fancy-* 命令产出节点。
34
34
  * 它的 kind 不是 "command" → 聊天区被点亮 → 结果可见。
35
- * 节点本身**不渲染任何内容**(display:none):结果由 DSH 原生的命令卡片
36
- * (GenericCommandCard)渲染,本来就完整,不需要我们再画一遍。
35
+ * 完成结果在 command/done 的位置渲染成展开的绿/红边框卡片:
36
+ * 原生命令卡默认收成一行、需要点击展开,这里直接把多行内容铺开,
37
+ * 让阶段完成/失败和 help/query 等结果都一眼可见。
37
38
  *
38
39
  * 3) 空白会话里聊天区不挂载 → chat target 永远不会自动激活 →
39
40
  * conversationPhase(dsh-client-ui-conversation/lib/client.js:14011)
@@ -61,8 +62,13 @@ window.__ModuleLoader__.load({
61
62
  "fancy-analyze",
62
63
  "fancy-import",
63
64
  "fancy-scan",
64
- "fancy-story-builder",
65
- "fancy-topic"
65
+ "fancy-build",
66
+ "fancy-export",
67
+ "fancy-topic",
68
+ "fancy-db",
69
+ "fancy-project",
70
+ "fancy-material",
71
+ "fancy-douyin"
66
72
  ];
67
73
 
68
74
  /** 复刻 dsh-client-ui-chat 的 contextLocation(那边是包内私有函数)。 */
@@ -70,7 +76,12 @@ window.__ModuleLoader__.load({
70
76
  return context.start?.location ?? context.matches[0]?.location ?? { kind: "unresolved" };
71
77
  }
72
78
 
73
- /** 把 command/run + command/done 折算成一条 fancy 通知。 */
79
+ /**
80
+ * 把 command/run + command/done 折算成一条 fancy 通知。
81
+ *
82
+ * 所有 fancy-* 命令的完成结果都锚定到 command/done 的位置,
83
+ * 用展开的多行卡片显示;DSH 原生命令卡仍保留在命令开始处。
84
+ */
74
85
  var noticeDefinition = {
75
86
  kind: NOTICE_KIND,
76
87
  target: "chat",
@@ -83,7 +94,9 @@ window.__ModuleLoader__.load({
83
94
  return {
84
95
  name: match.event.data.name ?? "",
85
96
  args: match.event.data.args ?? null,
86
- outcome: null
97
+ outcome: null,
98
+ doneSeq: null,
99
+ doneLocation: null
87
100
  };
88
101
  },
89
102
  update: function (context, match) {
@@ -94,7 +107,9 @@ window.__ModuleLoader__.load({
94
107
  outcome: {
95
108
  kind: match.event.data.kind,
96
109
  text: match.event.data.text ?? ""
97
- }
110
+ },
111
+ doneSeq: match.event.seq,
112
+ doneLocation: match.location ?? null
98
113
  };
99
114
  },
100
115
  buildViewNode: function (context) {
@@ -106,8 +121,12 @@ window.__ModuleLoader__.load({
106
121
  kind: NOTICE_KIND,
107
122
  id: context.id,
108
123
  target: "chat",
109
- anchorSeq: context.startSeq ?? context.matches[0].event.seq,
110
- location: contextLocation(context),
124
+ // 完成通知锚在 command/done 的位置,才会跟在最后一批 assistant
125
+ // 消息之后;否则会被几十条逐批消息顶到上面,用户看不到。
126
+ anchorSeq: state.doneSeq ?? context.startSeq ?? context.matches[0].event.seq,
127
+ // 用 command/done 的 location,通知才会落在最后一个 turn/step 里;
128
+ // 沿用命令开始时的 location 会把它重新拽回第一个 turn。
129
+ location: state.doneLocation ?? contextLocation(context),
111
130
  visibility: "visible",
112
131
  data: state
113
132
  };
@@ -115,16 +134,171 @@ window.__ModuleLoader__.load({
115
134
  };
116
135
 
117
136
  /**
118
- * 点亮用的隐形节点。真正的可见内容由 DSH 原生命令卡片渲染,
119
- * 这里只留一个 data 钩子,方便自动化测试断言。
137
+ * 所有 fancy-* 命令的结果都渲染成展开的带边框卡片。
138
+ *
139
+ * 和 DSH 原生命令卡的区别:原生命令卡默认收成一行,要点一下才展开;
140
+ * 这里直接用 pre-wrap 保留换行,并把成功/失败用绿/红边框区分。
141
+ * 仍然只读 command/run + command/done,不写会话、不唤醒 agent。
120
142
  */
121
143
  function FancyNoticeNodeView(props) {
122
144
  var data = props?.node?.data ?? {};
123
- return react.createElement("div", {
145
+ var outcome = data.outcome;
146
+ var known = FANCY_COMMANDS.indexOf(data.name) !== -1;
147
+ var baseProps = {
124
148
  "data-fancy-notice": data.name ?? "",
125
- "data-fancy-notice-kind": data.outcome?.kind ?? "",
126
- style: { display: "none" }
127
- });
149
+ "data-fancy-notice-kind": outcome?.kind ?? ""
150
+ };
151
+
152
+ if (outcome === null || outcome === undefined || !known) {
153
+ return react.createElement("div", {
154
+ ...baseProps,
155
+ style: { display: "none" }
156
+ });
157
+ }
158
+
159
+ var failed = outcome.kind === "error";
160
+ var accent = failed
161
+ ? "var(--dsw-alias-state-error-primary, #d92d20)"
162
+ : "var(--dsw-alias-state-success-primary, #12b76a)";
163
+ var background = failed
164
+ ? "rgba(217, 45, 32, 0.08)"
165
+ : "rgba(18, 183, 106, 0.08)";
166
+ var args = String(data.args ?? "").trim();
167
+ var label = (data.name ?? "") + (args === "" ? "" : " " + args);
168
+
169
+ return react.createElement("div", {
170
+ ...baseProps,
171
+ role: "status",
172
+ "aria-live": "polite",
173
+ style: {
174
+ margin: "12px 0",
175
+ padding: "12px 14px",
176
+ border: "1px solid " + accent,
177
+ borderLeftWidth: "4px",
178
+ borderRadius: "10px",
179
+ background: background,
180
+ lineHeight: 1.6,
181
+ fontSize: "13px",
182
+ maxHeight: "60vh",
183
+ overflow: "auto"
184
+ }
185
+ },
186
+ react.createElement("div", {
187
+ key: "command",
188
+ style: {
189
+ marginBottom: "6px",
190
+ color: "var(--dsw-alias-label-secondary, inherit)",
191
+ fontSize: "12px",
192
+ opacity: 0.75
193
+ }
194
+ }, label),
195
+ react.createElement("div", {
196
+ key: "body",
197
+ style: {
198
+ whiteSpace: "pre-wrap",
199
+ wordBreak: "break-word"
200
+ }
201
+ }, outcome.text || (failed ? "命令失败" : "命令完成")));
202
+ }
203
+
204
+ var RUNNING_STYLE_ID = "@cloud411716/fancy-webnovel/running";
205
+
206
+ /**
207
+ * 执行中提示的动画样式。
208
+ *
209
+ * 用 CSS 动画而不是 JS 定时器:不触发 React 重渲染,也不会因为
210
+ * 命令跑了几十分钟而堆积定时器。原地转圈 + 三点呼吸足够让用户
211
+ * 知道任务还在跑。
212
+ */
213
+ function ensureRunningStyle() {
214
+ if (typeof document === "undefined" || document.head === undefined) return;
215
+ if (document.querySelector("style[data-plugin-css=" + JSON.stringify(RUNNING_STYLE_ID) + "]") !== null) return;
216
+ var tag = document.createElement("style");
217
+ tag.dataset.plugin = "@cloud411716/fancy-webnovel";
218
+ tag.dataset.pluginCss = RUNNING_STYLE_ID;
219
+ tag.textContent = [
220
+ "@keyframes fancy-command-spin{to{transform:rotate(360deg)}}",
221
+ "@keyframes fancy-command-dot{0%,80%,100%{opacity:.25}40%{opacity:1}}"
222
+ ].join("");
223
+ document.head.appendChild(tag);
224
+ }
225
+
226
+ /**
227
+ * 原生命令卡的替代:命令执行期间显示一条轻量记录;命令完成后返回
228
+ * null,把“执行过这个命令”的记录交给 command/done 位置的展开卡片。
229
+ *
230
+ * 这样不会出现「顶部一张收起卡 + 底部一张展开卡」的重复记录。
231
+ * 执行中的转圈和三点是 CSS 动画,原地变化,证明任务没卡死。
232
+ */
233
+ function FancyCommandRecordView(props) {
234
+ var node = props?.node;
235
+ if (node === undefined || node === null) return null;
236
+ if (node.outcome !== null && node.outcome !== undefined) return null;
237
+ var args = String(node.args ?? "").trim();
238
+ var label = (node.name ?? "命令") + (args === "" ? "" : " " + args);
239
+ return react.createElement("div", {
240
+ "data-fancy-command-running": node.name ?? "",
241
+ role: "status",
242
+ "aria-live": "polite",
243
+ style: {
244
+ display: "flex",
245
+ alignItems: "center",
246
+ gap: "8px",
247
+ margin: "4px 0",
248
+ padding: "6px 10px",
249
+ border: "1px solid var(--dsw-alias-border-l2, rgba(127, 127, 127, 0.25))",
250
+ borderRadius: "8px",
251
+ color: "var(--dsw-alias-label-secondary, inherit)",
252
+ fontSize: "13px",
253
+ lineHeight: 1.5
254
+ }
255
+ },
256
+ react.createElement("span", {
257
+ key: "spinner",
258
+ "data-fancy-command-spinner": "true",
259
+ "aria-hidden": "true",
260
+ style: {
261
+ display: "inline-block",
262
+ width: "12px",
263
+ height: "12px",
264
+ border: "2px solid currentColor",
265
+ borderTopColor: "transparent",
266
+ borderRadius: "50%",
267
+ animation: "fancy-command-spin 0.8s linear infinite",
268
+ flexShrink: 0
269
+ }
270
+ }),
271
+ react.createElement("span", { key: "label" }, label + " 执行中"),
272
+ react.createElement("span", {
273
+ key: "dots",
274
+ "data-fancy-command-dots": "true",
275
+ "aria-hidden": "true",
276
+ style: { display: "inline-flex", gap: "2px" }
277
+ },
278
+ react.createElement("span", {
279
+ key: "dot-0",
280
+ style: {
281
+ animation: "fancy-command-dot 1.2s ease-in-out infinite",
282
+ animationDelay: "0s",
283
+ opacity: 0.25
284
+ }
285
+ }, "."),
286
+ react.createElement("span", {
287
+ key: "dot-1",
288
+ style: {
289
+ animation: "fancy-command-dot 1.2s ease-in-out infinite",
290
+ animationDelay: "0.15s",
291
+ opacity: 0.25
292
+ }
293
+ }, "."),
294
+ react.createElement("span", {
295
+ key: "dot-2",
296
+ style: {
297
+ animation: "fancy-command-dot 1.2s ease-in-out infinite",
298
+ animationDelay: "0.3s",
299
+ opacity: 0.25
300
+ }
301
+ }, ".")));
128
302
  }
129
303
 
130
304
  /**
@@ -132,6 +306,7 @@ window.__ModuleLoader__.load({
132
306
  * @param ctx - client root context.
133
307
  */
134
308
  function apply(ctx) {
309
+ ensureRunningStyle();
135
310
  ctx.uiConversation.events.register(noticeDefinition);
136
311
 
137
312
  // 注册我们这个 kind 的渲染器。不注册的话 DSH 会走 JsonBlock 兜底,
@@ -143,6 +318,17 @@ window.__ModuleLoader__.load({
143
318
  }, FancyNoticeNodeView);
144
319
  });
145
320
 
321
+ // 接管 fancy-* 的原生命令卡:执行期间显示一条轻量记录,完成后隐藏,
322
+ // 避免和 command/done 位置的展开卡片重复。
323
+ FANCY_COMMANDS.forEach(function (name) {
324
+ ctx.slots.inject("conversation.chat.commandview", function () {
325
+ return ctx.slots.register({
326
+ name: "conversation.chat.commandview",
327
+ key: name
328
+ }, FancyCommandRecordView);
329
+ });
330
+ });
331
+
146
332
  /*
147
333
  * 强制激活当前会话的 chat target。
148
334
  *
package/cordis.patch.yml CHANGED
@@ -1,4 +1,8 @@
1
+ # fancy-webnovel plugin entry.
2
+ #
3
+ # DSH also gets native tools (fancy_workspace / fancy_db / fancy_story_*), so
4
+ # the MCP client bridge is optional. To expose the portable MCP server as
5
+ # mcp__fancy__* tools, see docs/mcp-profile.example.yml.
1
6
  - insert:
2
7
  - id: fancy-webnovel
3
8
  name: "@cloud411716/fancy-webnovel"
4
- from: "./index.js"
@@ -0,0 +1,118 @@
1
+ # fancy-douyin 抖音图文采集
2
+
3
+ 把记录在案的抖音账号主页下的图文作品,逐篇截图、交给 LLM 提取素材,并写入
4
+ 工作区素材库(`source=other`,kind 不受内置列表限制)。
5
+
6
+ ## 命令
7
+
8
+ ```text
9
+ /fancy-douyin add <主页地址>
10
+ /fancy-douyin list
11
+ /fancy-douyin remove <序号|地址>
12
+ /fancy-douyin
13
+ /fancy-douyin help
14
+ ```
15
+
16
+ `add` 只接受账号主页,例如:
17
+
18
+ ```text
19
+ https://www.douyin.com/user/MS4wLjABAAAA...
20
+ ```
21
+
22
+ 单篇作品地址、视频地址和短链会被拒绝。
23
+
24
+ ## 无参数运行
25
+
26
+ ```text
27
+ 依次扫描账号主页
28
+ → 收集所有 /note/ 图文作品
29
+ → 跳过 crawled.json 中已成功入库的作品
30
+ → 打开作品,下载轮播原图并转成 JPG(避免滚动时截到半张图)
31
+ → 给 LLM 发送截图路径和素材提取提示词
32
+ → LLM 写 JSONL
33
+ → 校验并写入 material_library(source=other)
34
+ → 记录作品 ID
35
+ → 继续下一篇
36
+ ```
37
+
38
+ 失败的作品不会写入采集记录,下次运行会重试。
39
+
40
+ ## 登录与验证
41
+
42
+ Chrome 使用工作区内的独立 profile:
43
+
44
+ ```text
45
+ <工作区>/.fancy/douyin/chrome-profile/
46
+ ```
47
+
48
+ cookies 会额外镜像到:
49
+
50
+ ```text
51
+ <工作区>/.fancy/douyin/login-state.json
52
+ ```
53
+
54
+ 遇到登录框、滑块、图形验证或短信验证时,插件会弹窗:
55
+
56
+ ```text
57
+ 已登陆 / 取消
58
+ ```
59
+
60
+ 请在打开的 Chrome 窗口里手动完成登录或验证,然后点「已登陆」。插件不会自动
61
+ 绕过验证,也不会保存账号密码。
62
+
63
+ 运行模式:
64
+
65
+ - 第一次运行、或没有有效登录态时:有头 Chrome,方便手动登录;本次会继续用有头跑完。
66
+ - login-state.json 里有抖音登录 cookie 时:后续运行自动使用无头 Chrome。
67
+ - 无头运行中如果登录态失效:插件会自动关闭无头 Chrome,切换到有头 Chrome
68
+ 让你登录,登录完成后再继续。
69
+ - 每次运行结束都会关闭 Chrome;登录态留在 profile 和 login-state.json 中。
70
+
71
+ ## 数据目录
72
+
73
+ ```text
74
+ <工作区>/.fancy/douyin/
75
+ ├── accounts.json # 账号主页记录
76
+ ├── crawled.json # 已成功入库的作品 ID
77
+ ├── login-state.json # cookies 镜像(0600)
78
+ ├── chrome-profile/ # Chrome 登录态
79
+ ├── chrome.json # CDP 端口、profile 与 headed/headless 模式
80
+ └── output/<时间>/ # 截图、note.json、materials.jsonl
81
+ ```
82
+
83
+ 浏览器脚本部署在:
84
+
85
+ ```text
86
+ <工作区>/.fancy-tools/fancy-douyin/
87
+ ├── start-chrome.cjs
88
+ ├── scan-account.cjs
89
+ ├── crawl-note.cjs
90
+ ├── lib.cjs
91
+ └── package.json
92
+ ```
93
+
94
+ ## 素材格式
95
+
96
+ 每篇作品的 LLM 输出写到:
97
+
98
+ ```text
99
+ <作品目录>/materials.jsonl
100
+ ```
101
+
102
+ 字段:
103
+
104
+ ```text
105
+ id / kind / name / tags / genres / note / source
106
+ ```
107
+
108
+ `source` 固定为 `other`。`kind` 可以是任意非空字符串;如果内容能归入内置
109
+ material kind,优先使用内置 kind。插件会强制把 source 改成 other,然后运行
110
+ `.fancy-tools/fancy-material/validate.js` 校验,再写入数据库。
111
+
112
+ ## 注意
113
+
114
+ - 只采集个人研究/已授权账号,不要批量抓取或二次分发受版权保护的图片和文案。
115
+ - 截图不是页面视口截图,而是下载每页轮播原图后再转成 JPEG,所以不会因为
116
+ 滚动、懒加载或页面动画截到残缺图片。
117
+ - 抖音 DOM/类名会变,`scan-account.cjs` 和 `crawl-note.cjs` 的选择器可能需要维护。
118
+ - 每次运行结束会自动关闭 Chrome;登录态保存在 profile 和 login-state.json 中,下次运行自动复用。