@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.
- package/README.md +65 -0
- package/client.js +201 -15
- package/cordis.patch.yml +5 -1
- package/docs/douyin-plugin.md +118 -0
- package/docs/material-library/README.md +404 -0
- package/docs/material-library/import.js +437 -0
- package/docs/material-library/prompt.md +370 -0
- package/docs/material-library/queries.surql +94 -0
- package/docs/material-library/sample.jsonl +14 -0
- package/docs/material-library/schema.surql +46 -0
- package/docs/mcp-profile.example.yml +24 -0
- package/docs/story-graph-design.md +602 -0
- package/docs/story-graph-validation.md +81 -0
- package/docs/workspace-mcp.md +284 -0
- package/index.js +12 -7
- package/lib/atomic-write.js +109 -0
- package/lib/chapter-order.js +100 -0
- package/lib/confirmation.js +80 -0
- package/lib/db-init.js +123 -0
- package/lib/deployed-state.js +11 -11
- package/lib/direction-labels.js +265 -0
- package/lib/error-codes.js +31 -31
- package/lib/infra.js +29 -4
- package/lib/material-db.js +149 -0
- package/lib/path-guard.js +150 -0
- package/lib/projects.js +400 -0
- package/lib/scraper-utils.js +140 -0
- package/lib/surreal-server.js +338 -0
- package/lib/topic-decision.js +860 -0
- package/lib/topic-fields.js +251 -0
- package/lib/topic-schema.js +225 -0
- package/lib/workspace.js +501 -0
- package/package.json +31 -7
- package/plugins/fancy-analyze/analyze-definitions.md +519 -0
- package/plugins/fancy-analyze/index.js +1128 -77
- package/plugins/fancy-analyze/ingest-coverage.js +107 -0
- package/plugins/fancy-analyze/schema-analyze.md +355 -107
- package/plugins/fancy-analyze/scripts/append-summary-chapter.js +612 -0
- package/plugins/fancy-analyze/summary-progress.js +71 -7
- package/plugins/fancy-analyze/templates.js +437 -139
- package/plugins/fancy-bootstrap/index.js +276 -131
- package/plugins/fancy-bootstrap/scripts/init.js +59 -25
- package/plugins/fancy-bootstrap/templates.js +35 -18
- package/plugins/fancy-db/index.js +172 -0
- package/plugins/fancy-douyin/deploy.js +134 -0
- package/plugins/fancy-douyin/index.js +551 -0
- package/plugins/fancy-douyin/prompts/01-analysis-prompt.md +84 -0
- package/plugins/fancy-douyin/scripts/crawl-note.cjs +194 -0
- package/plugins/fancy-douyin/scripts/lib.cjs +197 -0
- package/plugins/fancy-douyin/scripts/scan-account.cjs +128 -0
- package/plugins/fancy-douyin/scripts/start-chrome.cjs +203 -0
- package/plugins/fancy-douyin/state.js +197 -0
- package/plugins/fancy-douyin/templates.js +127 -0
- package/plugins/fancy-export/exporter.js +805 -0
- package/plugins/fancy-export/index.js +63 -0
- package/plugins/fancy-import/import-driver.js +29 -6
- package/plugins/fancy-import/index.js +13 -5
- package/plugins/fancy-import/prompts/01-import-prompt.md +39 -14
- package/plugins/fancy-import/templates.js +17 -8
- package/plugins/fancy-material/exporter.js +153 -0
- package/plugins/fancy-material/genres.js +256 -0
- package/plugins/fancy-material/index.js +835 -0
- package/plugins/fancy-material/inventory.js +145 -0
- package/plugins/fancy-material/kinds.js +313 -0
- package/plugins/fancy-material/queue.js +55 -0
- package/plugins/fancy-material/scripts/query.js +254 -0
- package/plugins/fancy-material/templates.js +258 -0
- package/plugins/fancy-material/tool-deploy.js +138 -0
- package/plugins/fancy-material/validate.js +149 -0
- package/plugins/fancy-project/index.js +133 -0
- package/plugins/fancy-scan/existing-block.js +100 -0
- package/plugins/fancy-scan/index.js +410 -21
- package/plugins/fancy-scan/prompts/01-analysis-prompt.md +302 -0
- package/plugins/fancy-scan/scripts/append-block.js +725 -0
- package/plugins/fancy-scan/scripts/report-validate.js +824 -0
- package/plugins/fancy-scan/scripts/run-scan.js +169 -22
- package/plugins/fancy-scan/scripts/scrapers/fanqie-rank-scraper.js +145 -16
- package/plugins/fancy-scan/scripts/scrapers/jjwxc-rank-scraper.js +179 -49
- package/plugins/fancy-scan/scripts/scrapers/qimao-rank-scraper.js +38 -20
- package/plugins/fancy-scan/templates/blocks.js +155 -0
- package/plugins/fancy-scan/templates/platforms.js +372 -0
- package/plugins/fancy-scan/templates/report-schema.js +90 -0
- package/plugins/fancy-scan/templates.js +545 -146
- package/plugins/fancy-scan/tool-deploy.js +335 -0
- package/plugins/fancy-scan/wait-turn.js +46 -0
- package/plugins/fancy-story-builder/db/README.md +106 -0
- package/plugins/fancy-story-builder/db/client.js +228 -0
- package/plugins/fancy-story-builder/db/commands.js +145 -0
- package/plugins/fancy-story-builder/db/ids.js +39 -0
- package/plugins/fancy-story-builder/db/import.js +798 -0
- package/plugins/fancy-story-builder/db/project.js +299 -0
- package/plugins/fancy-story-builder/db/query.js +361 -0
- package/plugins/fancy-story-builder/db/schema.js +417 -0
- package/plugins/fancy-story-builder/db/stage-sync.js +238 -0
- package/plugins/fancy-story-builder/flesh-output.js +19 -6
- package/plugins/fancy-story-builder/index.js +4105 -396
- package/plugins/fancy-story-builder/patch-driver.js +390 -0
- package/plugins/fancy-story-builder/prompts/01-skeleton-prompt.md +29 -19
- package/plugins/fancy-story-builder/prompts/02-flesh-prompt.md +72 -10
- package/plugins/fancy-story-builder/prompts/03-rhythm-prompt.md +40 -5
- package/plugins/fancy-story-builder/prompts/04-writing-prompt.md +37 -0
- package/plugins/fancy-story-builder/purpose-template.md +42 -66
- package/plugins/fancy-story-builder/references/formulas-skeleton.json +229 -56
- package/plugins/fancy-story-builder/references/genre-profiles.json +3514 -0
- package/plugins/fancy-story-builder/references/glossary.md +15 -6
- package/plugins/fancy-story-builder/references/index.md +97 -6
- package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/00-index.md +1 -1
- package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/07-audit.md +25 -7
- package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/01-character.md +5 -1
- package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/03-relations.md +6 -2
- package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/05-audit.md +15 -5
- package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/00-index.md +1 -1
- package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/01-positioning.md +2 -1
- package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/03-entities.md +1 -1
- package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/06-audit.md +19 -1
- package/plugins/fancy-story-builder/schema-setting.json +1100 -0
- package/plugins/fancy-story-builder/schema-setting.md +201 -293
- package/plugins/fancy-story-builder/scripts/brief-validate.js +164 -0
- package/plugins/fancy-story-builder/scripts/content-quality.js +4 -1
- package/plugins/fancy-story-builder/scripts/flesh-validate.js +135 -12
- package/plugins/fancy-story-builder/scripts/graph-utils.js +146 -47
- package/plugins/fancy-story-builder/scripts/reference-log.js +110 -0
- package/plugins/fancy-story-builder/scripts/rhythm-validate.js +100 -4
- package/plugins/fancy-story-builder/scripts/schema-pages.js +2668 -0
- package/plugins/fancy-story-builder/scripts/semantic-lint.js +5 -1
- package/plugins/fancy-story-builder/scripts/skeleton-validate.js +158 -17
- package/plugins/fancy-story-builder/scripts/stage-batches.js +36 -2
- package/plugins/fancy-story-builder/scripts/stage-contract.js +12 -0
- package/plugins/fancy-story-builder/scripts/story-artifacts.js +933 -0
- package/plugins/fancy-story-builder/scripts/upstream-query.js +1 -1
- package/plugins/fancy-story-builder/scripts/v2/baseline.js +112 -0
- package/plugins/fancy-story-builder/scripts/v2/cast.js +517 -0
- package/plugins/fancy-story-builder/scripts/v2/context.js +434 -0
- package/plugins/fancy-story-builder/scripts/v2/contracts.js +493 -0
- package/plugins/fancy-story-builder/scripts/v2/core.js +419 -0
- package/plugins/fancy-story-builder/scripts/v2/engine.js +1068 -0
- package/plugins/fancy-story-builder/scripts/v2/export-markdown.js +794 -0
- package/plugins/fancy-story-builder/scripts/v2/genre.js +195 -0
- package/plugins/fancy-story-builder/scripts/v2/knowledge.js +321 -0
- package/plugins/fancy-story-builder/scripts/v2/macro.js +409 -0
- package/plugins/fancy-story-builder/scripts/v2/manifest.js +538 -0
- package/plugins/fancy-story-builder/scripts/v2/migrate.js +137 -0
- package/plugins/fancy-story-builder/scripts/v2/prompts.js +1335 -0
- package/plugins/fancy-story-builder/scripts/v2/rhythm.js +618 -0
- package/plugins/fancy-story-builder/scripts/v2/scale.js +843 -0
- package/plugins/fancy-story-builder/scripts/v2/snapshot.js +121 -0
- package/plugins/fancy-story-builder/scripts/v2/stages.js +273 -0
- package/plugins/fancy-story-builder/scripts/v2/writing.js +216 -0
- package/plugins/fancy-story-builder/storydb/README.md +271 -0
- package/plugins/fancy-story-builder/storydb/cli.js +458 -0
- package/plugins/fancy-story-builder/storydb/client.js +351 -0
- package/plugins/fancy-story-builder/storydb/compile.js +452 -0
- package/plugins/fancy-story-builder/storydb/context.js +288 -0
- package/plugins/fancy-story-builder/storydb/definitions.js +699 -0
- package/plugins/fancy-story-builder/storydb/driver.js +106 -0
- package/plugins/fancy-story-builder/storydb/dsh-tools.js +313 -0
- package/plugins/fancy-story-builder/storydb/export.js +175 -0
- package/plugins/fancy-story-builder/storydb/ids.js +31 -0
- package/plugins/fancy-story-builder/storydb/impact.js +170 -0
- package/plugins/fancy-story-builder/storydb/import.js +559 -0
- package/plugins/fancy-story-builder/storydb/mcp.js +99 -0
- package/plugins/fancy-story-builder/storydb/prompts.js +952 -0
- package/plugins/fancy-story-builder/storydb/stage-command.js +379 -0
- package/plugins/fancy-story-builder/storydb/stage.js +426 -0
- package/plugins/fancy-story-builder/storydb/store.js +891 -0
- package/plugins/fancy-story-builder/storydb/tools.js +592 -0
- package/plugins/fancy-story-builder/storydb/validation.js +183 -0
- package/plugins/fancy-story-builder/templates/v2/10-scale/scale-template.json +144 -0
- package/plugins/fancy-story-builder/templates/v2/20-macro/emotion-template.json +15 -0
- package/plugins/fancy-story-builder/templates/v2/20-macro/endings-template.json +16 -0
- package/plugins/fancy-story-builder/templates/v2/20-macro/lines-template.json +28 -0
- package/plugins/fancy-story-builder/templates/v2/20-macro/roles-template.json +10 -0
- package/plugins/fancy-story-builder/templates/v2/20-macro/story-template.json +19 -0
- package/plugins/fancy-story-builder/templates/v2/20-macro/volumes-template.json +24 -0
- package/plugins/fancy-story-builder/templates/v2/20-macro/world-template.json +24 -0
- package/plugins/fancy-story-builder/templates/v2/30-cast/characters-template.json +49 -0
- package/plugins/fancy-story-builder/templates/v2/30-cast/climaxes-template.json +14 -0
- package/plugins/fancy-story-builder/templates/v2/30-cast/conflicts-template.json +30 -0
- package/plugins/fancy-story-builder/templates/v2/30-cast/factions-template.json +18 -0
- package/plugins/fancy-story-builder/templates/v2/30-cast/foreshadows-template.json +29 -0
- package/plugins/fancy-story-builder/templates/v2/30-cast/information-template.json +29 -0
- package/plugins/fancy-story-builder/templates/v2/30-cast/items-template.json +13 -0
- package/plugins/fancy-story-builder/templates/v2/30-cast/locations-template.json +12 -0
- package/plugins/fancy-story-builder/templates/v2/30-cast/payoffs-template.json +16 -0
- package/plugins/fancy-story-builder/templates/v2/30-cast/relations-template.json +44 -0
- package/plugins/fancy-story-builder/templates/v2/40-rhythm/chapter-map-template.json +81 -0
- package/plugins/fancy-story-builder/templates/v2/40-rhythm/outline-template.json +162 -0
- package/plugins/fancy-story-builder/templates/v2/50-writing/ledger-template.json +46 -0
- package/plugins/fancy-story-builder/templates/v2/60-deslop/report-template.json +10 -0
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- package/plugins/fancy-story-builder/templates.js +272 -74
- package/plugins/fancy-story-builder/wiki-references.js +429 -93
- package/plugins/fancy-story-builder/writing-driver.js +336 -9
- package/plugins/fancy-topic/index.js +414 -61
- package/plugins/fancy-topic/prompts/01-exploration-prompt.md +115 -0
- package/plugins/fancy-topic/scripts/save-suggestion.js +219 -0
- package/plugins/fancy-topic/scripts/select-topic.js +106 -63
- package/plugins/fancy-topic/templates.js +225 -122
- package/plugins/fancy-topic/tool-deploy.js +200 -0
- 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
|
-
*
|
|
36
|
-
*
|
|
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-
|
|
65
|
-
"fancy-
|
|
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
|
-
/**
|
|
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
|
-
|
|
110
|
-
|
|
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
|
-
*
|
|
119
|
-
*
|
|
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
|
-
|
|
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":
|
|
126
|
-
|
|
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 中,下次运行自动复用。
|