novelws 5.2.0 → 5.4.0
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/CHANGELOG.md +51 -0
- package/README.md +53 -0
- package/dist/cli.js +3 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/dashboard.d.ts +3 -0
- package/dist/commands/dashboard.d.ts.map +1 -0
- package/dist/commands/dashboard.js +51 -0
- package/dist/commands/dashboard.js.map +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +26 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/commands/upgrade.js +23 -0
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/core/config.d.ts +6 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +8 -0
- package/dist/core/config.js.map +1 -1
- package/dist/server/datasource/db.d.ts +38 -0
- package/dist/server/datasource/db.d.ts.map +1 -0
- package/dist/server/datasource/db.js +323 -0
- package/dist/server/datasource/db.js.map +1 -0
- package/dist/server/datasource/fs.d.ts +30 -0
- package/dist/server/datasource/fs.d.ts.map +1 -0
- package/dist/server/datasource/fs.js +308 -0
- package/dist/server/datasource/fs.js.map +1 -0
- package/dist/server/datasource/index.d.ts +11 -0
- package/dist/server/datasource/index.d.ts.map +1 -0
- package/dist/server/datasource/index.js +33 -0
- package/dist/server/datasource/index.js.map +1 -0
- package/dist/server/index.d.ts +12 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +69 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/routes/characters.d.ts +4 -0
- package/dist/server/routes/characters.d.ts.map +1 -0
- package/dist/server/routes/characters.js +27 -0
- package/dist/server/routes/characters.js.map +1 -0
- package/dist/server/routes/plots.d.ts +4 -0
- package/dist/server/routes/plots.d.ts.map +1 -0
- package/dist/server/routes/plots.js +36 -0
- package/dist/server/routes/plots.js.map +1 -0
- package/dist/server/routes/protagonist.d.ts +4 -0
- package/dist/server/routes/protagonist.d.ts.map +1 -0
- package/dist/server/routes/protagonist.js +46 -0
- package/dist/server/routes/protagonist.js.map +1 -0
- package/dist/server/routes/relationships.d.ts +4 -0
- package/dist/server/routes/relationships.d.ts.map +1 -0
- package/dist/server/routes/relationships.js +27 -0
- package/dist/server/routes/relationships.js.map +1 -0
- package/dist/server/routes/stats.d.ts +4 -0
- package/dist/server/routes/stats.d.ts.map +1 -0
- package/dist/server/routes/stats.js +21 -0
- package/dist/server/routes/stats.js.map +1 -0
- package/dist/server/routes/stories.d.ts +4 -0
- package/dist/server/routes/stories.d.ts.map +1 -0
- package/dist/server/routes/stories.js +80 -0
- package/dist/server/routes/stories.js.map +1 -0
- package/dist/server/routes/timeline.d.ts +4 -0
- package/dist/server/routes/timeline.d.ts.map +1 -0
- package/dist/server/routes/timeline.js +17 -0
- package/dist/server/routes/timeline.js.map +1 -0
- package/dist/server/types.d.ts +199 -0
- package/dist/server/types.d.ts.map +1 -0
- package/dist/server/types.js +2 -0
- package/dist/server/types.js.map +1 -0
- package/package.json +10 -3
- package/templates/commands/analyze.md +9 -1
- package/templates/commands/expand.md +19 -3
- package/templates/commands/write.md +23 -7
- package/templates/dashboard/assets/ChaptersView-CkYRzkTt.css +1 -0
- package/templates/dashboard/assets/CharactersView-BuH15mT-.css +1 -0
- package/templates/dashboard/assets/DashboardView-qD5yG8Hj.css +1 -0
- package/templates/dashboard/assets/PlotsView-Cnx9_j0t.css +1 -0
- package/templates/dashboard/assets/ProtagonistView-DfWCFC3K.css +1 -0
- package/templates/dashboard/assets/RelationshipsView-DMtu4xH0.css +1 -0
- package/templates/dashboard/assets/TimelineView-CovxzAeu.css +1 -0
- package/templates/dashboard/assets/index-nD7kmLMb.css +1 -0
- package/templates/dashboard/index.html +13 -0
- package/templates/dot-claude/CLAUDE.md +27 -0
- package/templates/scripts/db_context.py +609 -0
- package/templates/scripts/db_init_protagonist.py +343 -0
- package/templates/scripts/db_sync.py +611 -0
- package/templates/scripts/db_volume_switch.py +278 -0
- package/templates/scripts/phase_a_init_db.py +428 -0
- package/templates/scripts/requirements.txt +1 -0
- package/templates/tracking/character-state.json +1 -3
- package/templates/tracking/plot-tracker.json +1 -5
- package/templates/tracking/relationships.json +1 -3
- package/templates/tracking/timeline.json +1 -3
- package/templates/volume-outline.md +31 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relationships.d.ts","sourceRoot":"","sources":["../../../src/server/routes/relationships.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,wBAAgB,yBAAyB,CAAC,EAAE,EAAE,UAAU,GAAG,MAAM,CAyBhE"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
export function createRelationshipsRouter(ds) {
|
|
3
|
+
const router = Router({ mergeParams: true });
|
|
4
|
+
// GET /api/stories/:story/relationships?vol=X
|
|
5
|
+
router.get('/', async (req, res) => {
|
|
6
|
+
try {
|
|
7
|
+
const vol = req.query.vol ? parseInt(req.query.vol, 10) : undefined;
|
|
8
|
+
const graph = await ds.getRelationships(req.params.story, vol);
|
|
9
|
+
res.json(graph);
|
|
10
|
+
}
|
|
11
|
+
catch (err) {
|
|
12
|
+
res.status(500).json({ error: err.message });
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
// GET /api/stories/:story/relationships/history
|
|
16
|
+
router.get('/history', async (req, res) => {
|
|
17
|
+
try {
|
|
18
|
+
const history = await ds.getRelationshipHistory(req.params.story);
|
|
19
|
+
res.json(history);
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
res.status(500).json({ error: err.message });
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
return router;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=relationships.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relationships.js","sourceRoot":"","sources":["../../../src/server/routes/relationships.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;AAGpD,MAAM,UAAU,yBAAyB,CAAC,EAAc;IACtD,MAAM,MAAM,GAAG,MAAM,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;IAE7C,8CAA8C;IAC9C,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,GAA+B,EAAE,GAAa,EAAE,EAAE;QACvE,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,GAAa,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAC9E,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;YAC/D,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAG,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,gDAAgD;IAChD,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,KAAK,EAAE,GAA+B,EAAE,GAAa,EAAE,EAAE;QAC9E,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,sBAAsB,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAClE,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAG,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../../../src/server/routes/stats.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,UAAU,GAAG,MAAM,CAmBxD"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
export function createStatsRouter(ds) {
|
|
3
|
+
const router = Router();
|
|
4
|
+
// GET /api/stats/dashboard?story=X
|
|
5
|
+
router.get('/dashboard', async (req, res) => {
|
|
6
|
+
try {
|
|
7
|
+
const story = req.query.story;
|
|
8
|
+
if (!story) {
|
|
9
|
+
res.status(400).json({ error: '缺少 story 参数' });
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
const stats = await ds.getDashboardStats(story);
|
|
13
|
+
res.json(stats);
|
|
14
|
+
}
|
|
15
|
+
catch (err) {
|
|
16
|
+
res.status(500).json({ error: err.message });
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
return router;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=stats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stats.js","sourceRoot":"","sources":["../../../src/server/routes/stats.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAGjC,MAAM,UAAU,iBAAiB,CAAC,EAAc;IAC9C,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC;IAExB,mCAAmC;IACnC,MAAM,CAAC,GAAG,CAAC,YAAY,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QAC1C,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,KAAe,CAAC;YACxC,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC;gBAC/C,OAAO;YACT,CAAC;YACD,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC;YAChD,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAG,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stories.d.ts","sourceRoot":"","sources":["../../../src/server/routes/stories.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,UAAU,GAAG,MAAM,CA8E1D"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
export function createStoriesRouter(ds) {
|
|
3
|
+
const router = Router();
|
|
4
|
+
// GET /api/stories
|
|
5
|
+
router.get('/', async (_req, res) => {
|
|
6
|
+
try {
|
|
7
|
+
const stories = await ds.getStories();
|
|
8
|
+
res.json(stories);
|
|
9
|
+
}
|
|
10
|
+
catch (err) {
|
|
11
|
+
res.status(500).json({ error: err.message });
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
// GET /api/stories/:story/overview
|
|
15
|
+
router.get('/:story/overview', async (req, res) => {
|
|
16
|
+
try {
|
|
17
|
+
const overview = await ds.getOverview(req.params.story);
|
|
18
|
+
res.json(overview);
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
res.status(500).json({ error: err.message });
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
// GET /api/stories/:story/volumes
|
|
25
|
+
router.get('/:story/volumes', async (req, res) => {
|
|
26
|
+
try {
|
|
27
|
+
const volumes = await ds.getVolumes(req.params.story);
|
|
28
|
+
res.json(volumes);
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
res.status(500).json({ error: err.message });
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
// GET /api/stories/:story/volumes/:vol/stats
|
|
35
|
+
router.get('/:story/volumes/:vol/stats', async (req, res) => {
|
|
36
|
+
try {
|
|
37
|
+
const vol = parseInt(req.params.vol, 10);
|
|
38
|
+
const chapters = await ds.getChapters(req.params.story, vol);
|
|
39
|
+
const characters = await ds.getCharacters(req.params.story, vol);
|
|
40
|
+
res.json({
|
|
41
|
+
volume: vol,
|
|
42
|
+
chapters: chapters.length,
|
|
43
|
+
words: chapters.reduce((sum, c) => sum + c.words, 0),
|
|
44
|
+
characters: characters.length,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
res.status(500).json({ error: err.message });
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
// GET /api/stories/:story/chapters?vol=X
|
|
52
|
+
router.get('/:story/chapters', async (req, res) => {
|
|
53
|
+
try {
|
|
54
|
+
const vol = req.query.vol ? parseInt(req.query.vol, 10) : undefined;
|
|
55
|
+
const chapters = await ds.getChapters(req.params.story, vol);
|
|
56
|
+
res.json(chapters);
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
res.status(500).json({ error: err.message });
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
// GET /api/stories/:story/chapters/:ch
|
|
63
|
+
router.get('/:story/chapters/:ch', async (req, res) => {
|
|
64
|
+
try {
|
|
65
|
+
const chNum = parseInt(req.params.ch, 10);
|
|
66
|
+
const chapters = await ds.getChapters(req.params.story);
|
|
67
|
+
const chapter = chapters.find(c => c.globalNumber === chNum);
|
|
68
|
+
if (!chapter) {
|
|
69
|
+
res.status(404).json({ error: '章节不存在' });
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
res.json(chapter);
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
res.status(500).json({ error: err.message });
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
return router;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=stories.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stories.js","sourceRoot":"","sources":["../../../src/server/routes/stories.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timeline.d.ts","sourceRoot":"","sources":["../../../src/server/routes/timeline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,UAAU,GAAG,MAAM,CAe3D"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
export function createTimelineRouter(ds) {
|
|
3
|
+
const router = Router({ mergeParams: true });
|
|
4
|
+
// GET /api/stories/:story/timeline?vol=X
|
|
5
|
+
router.get('/', async (req, res) => {
|
|
6
|
+
try {
|
|
7
|
+
const vol = req.query.vol ? parseInt(req.query.vol, 10) : undefined;
|
|
8
|
+
const events = await ds.getTimeline(req.params.story, vol);
|
|
9
|
+
res.json(events);
|
|
10
|
+
}
|
|
11
|
+
catch (err) {
|
|
12
|
+
res.status(500).json({ error: err.message });
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
return router;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=timeline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timeline.js","sourceRoot":"","sources":["../../../src/server/routes/timeline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;AAGpD,MAAM,UAAU,oBAAoB,CAAC,EAAc;IACjD,MAAM,MAAM,GAAG,MAAM,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;IAE7C,yCAAyC;IACzC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,GAA+B,EAAE,GAAa,EAAE,EAAE;QACvE,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,GAAa,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAC9E,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;YAC3D,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACnB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAG,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/** 故事基础信息 */
|
|
2
|
+
export interface Story {
|
|
3
|
+
name: string;
|
|
4
|
+
path: string;
|
|
5
|
+
}
|
|
6
|
+
/** 故事总览 */
|
|
7
|
+
export interface StoryOverview {
|
|
8
|
+
name: string;
|
|
9
|
+
totalVolumes: number;
|
|
10
|
+
totalChapters: number;
|
|
11
|
+
totalWords: number;
|
|
12
|
+
currentVolume: number;
|
|
13
|
+
}
|
|
14
|
+
/** 卷信息 */
|
|
15
|
+
export interface Volume {
|
|
16
|
+
number: number;
|
|
17
|
+
title: string;
|
|
18
|
+
chapters: number;
|
|
19
|
+
words: number;
|
|
20
|
+
progress: number;
|
|
21
|
+
}
|
|
22
|
+
/** 章节信息 */
|
|
23
|
+
export interface Chapter {
|
|
24
|
+
globalNumber: number;
|
|
25
|
+
volumeNumber: number;
|
|
26
|
+
chapterInVolume: number;
|
|
27
|
+
title: string;
|
|
28
|
+
words: number;
|
|
29
|
+
pov: string;
|
|
30
|
+
participants: string[];
|
|
31
|
+
hasSynopsis: boolean;
|
|
32
|
+
hasContent: boolean;
|
|
33
|
+
}
|
|
34
|
+
/** 角色信息 */
|
|
35
|
+
export interface Character {
|
|
36
|
+
name: string;
|
|
37
|
+
aliases: string[];
|
|
38
|
+
role: string;
|
|
39
|
+
firstVolume: number;
|
|
40
|
+
firstChapter: number;
|
|
41
|
+
cultivation: string;
|
|
42
|
+
faction: string;
|
|
43
|
+
status: string;
|
|
44
|
+
}
|
|
45
|
+
/** 角色状态快照(按卷) */
|
|
46
|
+
export interface CharacterState {
|
|
47
|
+
volume: number;
|
|
48
|
+
cultivation: string;
|
|
49
|
+
location: string;
|
|
50
|
+
summary: string;
|
|
51
|
+
lastChapter: number;
|
|
52
|
+
}
|
|
53
|
+
/** 关系图节点 */
|
|
54
|
+
export interface RelationshipNode {
|
|
55
|
+
id: string;
|
|
56
|
+
name: string;
|
|
57
|
+
faction: string;
|
|
58
|
+
role: string;
|
|
59
|
+
appearances: number;
|
|
60
|
+
}
|
|
61
|
+
/** 关系图边 */
|
|
62
|
+
export interface RelationshipEdge {
|
|
63
|
+
source: string;
|
|
64
|
+
target: string;
|
|
65
|
+
type: string;
|
|
66
|
+
status: string;
|
|
67
|
+
lastChapter: number;
|
|
68
|
+
}
|
|
69
|
+
/** 关系网络图 */
|
|
70
|
+
export interface RelationshipGraph {
|
|
71
|
+
nodes: RelationshipNode[];
|
|
72
|
+
edges: RelationshipEdge[];
|
|
73
|
+
}
|
|
74
|
+
/** 关系变迁事件 */
|
|
75
|
+
export interface RelationshipEvent {
|
|
76
|
+
chapter: number;
|
|
77
|
+
source: string;
|
|
78
|
+
target: string;
|
|
79
|
+
type: string;
|
|
80
|
+
oldStatus: string;
|
|
81
|
+
newStatus: string;
|
|
82
|
+
}
|
|
83
|
+
/** 时间线事件 */
|
|
84
|
+
export interface TimelineEvent {
|
|
85
|
+
id: number;
|
|
86
|
+
chapter: number;
|
|
87
|
+
storyTime: string;
|
|
88
|
+
location: string;
|
|
89
|
+
description: string;
|
|
90
|
+
tags: string[];
|
|
91
|
+
}
|
|
92
|
+
/** 情节线 */
|
|
93
|
+
export interface PlotThread {
|
|
94
|
+
name: string;
|
|
95
|
+
type: string;
|
|
96
|
+
status: string;
|
|
97
|
+
description: string;
|
|
98
|
+
keyEvents: string[];
|
|
99
|
+
}
|
|
100
|
+
/** 伏笔 */
|
|
101
|
+
export interface Foreshadow {
|
|
102
|
+
code: string;
|
|
103
|
+
description: string;
|
|
104
|
+
plantedChapter: number;
|
|
105
|
+
hintedChapters: number[];
|
|
106
|
+
resolvedChapter: number | null;
|
|
107
|
+
status: string;
|
|
108
|
+
importance: string;
|
|
109
|
+
}
|
|
110
|
+
/** 伏笔矩阵单元格 */
|
|
111
|
+
export interface ForeshadowCell {
|
|
112
|
+
chapter: number;
|
|
113
|
+
action: 'plant' | 'hint' | 'resolve' | null;
|
|
114
|
+
}
|
|
115
|
+
/** 伏笔矩阵 */
|
|
116
|
+
export interface ForeshadowMatrix {
|
|
117
|
+
chapters: number[];
|
|
118
|
+
rows: Array<{
|
|
119
|
+
code: string;
|
|
120
|
+
description: string;
|
|
121
|
+
cells: ForeshadowCell[];
|
|
122
|
+
}>;
|
|
123
|
+
}
|
|
124
|
+
/** 卷级统计 */
|
|
125
|
+
export interface VolumeStat {
|
|
126
|
+
volume: number;
|
|
127
|
+
title: string;
|
|
128
|
+
words: number;
|
|
129
|
+
chapters: number;
|
|
130
|
+
progress: number;
|
|
131
|
+
}
|
|
132
|
+
/** 仪表盘聚合统计 */
|
|
133
|
+
export interface DashboardStats {
|
|
134
|
+
totalWords: number;
|
|
135
|
+
totalChapters: number;
|
|
136
|
+
totalVolumes: number;
|
|
137
|
+
totalCharacters: number;
|
|
138
|
+
activePlotThreads: number;
|
|
139
|
+
unresolvedForeshadowing: number;
|
|
140
|
+
volumeStats: VolumeStat[];
|
|
141
|
+
}
|
|
142
|
+
/** 主角技能 */
|
|
143
|
+
export interface ProtagonistSkill {
|
|
144
|
+
name: string;
|
|
145
|
+
category: string;
|
|
146
|
+
level: string;
|
|
147
|
+
description: string;
|
|
148
|
+
acquiredChapter: number;
|
|
149
|
+
useCount: number;
|
|
150
|
+
status: string;
|
|
151
|
+
}
|
|
152
|
+
/** 主角道具 */
|
|
153
|
+
export interface ProtagonistItem {
|
|
154
|
+
name: string;
|
|
155
|
+
type: string;
|
|
156
|
+
quantity: number;
|
|
157
|
+
quality: string;
|
|
158
|
+
description: string;
|
|
159
|
+
acquiredChapter: number;
|
|
160
|
+
status: string;
|
|
161
|
+
}
|
|
162
|
+
/** 修炼进度节点 */
|
|
163
|
+
export interface CultivationNode {
|
|
164
|
+
chapter: number;
|
|
165
|
+
level: string;
|
|
166
|
+
progressPct: number;
|
|
167
|
+
breakthroughType: string | null;
|
|
168
|
+
detail: string;
|
|
169
|
+
}
|
|
170
|
+
/** 主角总览 */
|
|
171
|
+
export interface ProtagonistOverview {
|
|
172
|
+
currentLevel: string;
|
|
173
|
+
currentProgress: number;
|
|
174
|
+
totalSkills: number;
|
|
175
|
+
activeSkills: number;
|
|
176
|
+
totalItems: number;
|
|
177
|
+
heldItems: number;
|
|
178
|
+
}
|
|
179
|
+
/** 数据源抽象接口 */
|
|
180
|
+
export interface DataSource {
|
|
181
|
+
getStories(): Promise<Story[]>;
|
|
182
|
+
getOverview(story: string): Promise<StoryOverview>;
|
|
183
|
+
getVolumes(story: string): Promise<Volume[]>;
|
|
184
|
+
getChapters(story: string, vol?: number): Promise<Chapter[]>;
|
|
185
|
+
getCharacters(story: string, vol?: number): Promise<Character[]>;
|
|
186
|
+
getCharacterArc(story: string, name: string): Promise<CharacterState[]>;
|
|
187
|
+
getRelationships(story: string, vol?: number): Promise<RelationshipGraph>;
|
|
188
|
+
getRelationshipHistory(story: string): Promise<RelationshipEvent[]>;
|
|
189
|
+
getTimeline(story: string, vol?: number): Promise<TimelineEvent[]>;
|
|
190
|
+
getPlotThreads(story: string): Promise<PlotThread[]>;
|
|
191
|
+
getForeshadowing(story: string): Promise<Foreshadow[]>;
|
|
192
|
+
getForeshadowingMatrix(story: string): Promise<ForeshadowMatrix>;
|
|
193
|
+
getDashboardStats(story: string): Promise<DashboardStats>;
|
|
194
|
+
getProtagonistOverview(story: string): Promise<ProtagonistOverview>;
|
|
195
|
+
getProtagonistSkills(story: string): Promise<ProtagonistSkill[]>;
|
|
196
|
+
getProtagonistInventory(story: string): Promise<ProtagonistItem[]>;
|
|
197
|
+
getCultivationCurve(story: string): Promise<CultivationNode[]>;
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/server/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/server/types.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "novelws",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.4.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Claude Code 专用的 AI 小说创作工具 - 深度集成 Slash Commands 和 Agent Skills",
|
|
6
6
|
"keywords": [
|
|
@@ -33,22 +33,29 @@
|
|
|
33
33
|
"test:watch": "jest --config jest.config.cjs --watch",
|
|
34
34
|
"test:coverage": "jest --config jest.config.cjs --coverage",
|
|
35
35
|
"test:unit": "jest --config jest.config.cjs tests/unit",
|
|
36
|
-
"test:integration": "jest --config jest.config.cjs tests/integration"
|
|
36
|
+
"test:integration": "jest --config jest.config.cjs tests/integration",
|
|
37
|
+
"build:dashboard": "cd dashboard && npm run build",
|
|
38
|
+
"dashboard": "node dist/cli.js dashboard"
|
|
37
39
|
},
|
|
38
40
|
"dependencies": {
|
|
39
41
|
"@commander-js/extra-typings": "^12.0.0",
|
|
40
42
|
"chalk": "^5.3.0",
|
|
43
|
+
"express": "^5.2.1",
|
|
41
44
|
"fs-extra": "^11.2.0",
|
|
42
45
|
"inquirer": "^9.2.12",
|
|
43
46
|
"js-yaml": "^4.1.0",
|
|
44
|
-
"
|
|
47
|
+
"open": "^11.0.0",
|
|
48
|
+
"ora": "^8.0.1",
|
|
49
|
+
"pg": "^8.18.0"
|
|
45
50
|
},
|
|
46
51
|
"devDependencies": {
|
|
52
|
+
"@types/express": "^5.0.6",
|
|
47
53
|
"@types/fs-extra": "^11.0.4",
|
|
48
54
|
"@types/inquirer": "^9.0.9",
|
|
49
55
|
"@types/jest": "^30.0.0",
|
|
50
56
|
"@types/js-yaml": "^4.0.9",
|
|
51
57
|
"@types/node": "^20.10.0",
|
|
58
|
+
"@types/pg": "^8.16.0",
|
|
52
59
|
"jest": "^30.2.0",
|
|
53
60
|
"ts-jest": "^29.4.6",
|
|
54
61
|
"tsx": "^4.7.0",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
description: 质量检查:对比概要与正文,检测一致性和AI味
|
|
3
3
|
argument-hint: [章节号] [--range start-end]
|
|
4
4
|
recommended-model: claude-sonnet-4-5-20250929
|
|
5
|
-
allowed-tools: Read(//stories/**), Read(//resources/anti-ai.md), Bash(ls:*)
|
|
5
|
+
allowed-tools: Read(//stories/**), Read(//resources/anti-ai.md), Bash(ls:*), Bash(python:scripts/*)
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
用户输入:$ARGUMENTS
|
|
@@ -26,6 +26,14 @@ allowed-tools: Read(//stories/**), Read(//resources/anti-ai.md), Bash(ls:*)
|
|
|
26
26
|
- 对应概要:`stories/<story>/volumes/vol-XXX/content/chapter-YYY-synopsis.md`
|
|
27
27
|
- 卷级 tracking:`stories/<story>/volumes/vol-XXX/tracking/character-state.json`、`stories/<story>/volumes/vol-XXX/tracking/plot-tracker.json`
|
|
28
28
|
|
|
29
|
+
**DB 增强模式**:如果 `resources/config.json` 中 `database.enabled = true`,可用 DB 上下文替代 tracking 文件读取:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
python scripts/db_context.py --chapter <全局章节号> --mode analyze
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
输出包含本章伏笔检查清单、角色状态基准、时间线连续性和一致性警告。输出还包含主角能力基准(技能习得时间线 + 道具持有状态),用于检测正文中是否使用了尚未习得的技能或不持有的道具。
|
|
36
|
+
|
|
29
37
|
## 检查项(5项)
|
|
30
38
|
|
|
31
39
|
### 1. 概要符合度
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
description: 将章节概要扩写为 3000-5000 字正文
|
|
3
3
|
argument-hint: [章节号] [--batch N]
|
|
4
4
|
recommended-model: claude-opus-4-6
|
|
5
|
-
allowed-tools: Read(//stories/**), Write(//stories/**/content/**), Read(//stories/**/tracking/**), Write(//stories/**/tracking/**), Read(//resources/style-reference.md), Read(//resources/anti-ai.md), Read(//resources/constitution.md), Bash(ls:*)
|
|
5
|
+
allowed-tools: Read(//stories/**), Write(//stories/**/content/**), Read(//stories/**/tracking/**), Write(//stories/**/tracking/**), Read(//resources/style-reference.md), Read(//resources/anti-ai.md), Read(//resources/constitution.md), Bash(ls:*), Bash(python:scripts/*)
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
用户输入:$ARGUMENTS
|
|
@@ -38,6 +38,14 @@ allowed-tools: Read(//stories/**), Write(//stories/**/content/**), Read(//storie
|
|
|
38
38
|
|
|
39
39
|
所有资源必须从文件系统读取,不复用对话中的缓存。总上下文控制在 3500 字以内。
|
|
40
40
|
|
|
41
|
+
**DB 增强模式**:如果 `resources/config.json` 中 `database.enabled = true`,可用 DB 上下文替代第 3 层的 tracking 文件读取:
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
python scripts/db_context.py --chapter <全局章节号> --mode expand
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
输出包含本章伏笔、活跃角色状态、角色关系、前序衔接,直接替代手动读取 tracking JSON。输出还包含主角当前修为、可用技能和道具清单,确保扩写中主角能力使用不超出已有设定。第 1、2 层仍从文件系统加载。
|
|
48
|
+
|
|
41
49
|
### 第 1 层 — 全局视角
|
|
42
50
|
|
|
43
51
|
1. **specification.md 摘要**:读取 `stories/<story>/specification.md`,提取 100 字核心摘要(类型 + 主角 + 核心冲突)
|
|
@@ -100,13 +108,21 @@ allowed-tools: Read(//stories/**), Write(//stories/**/content/**), Read(//storie
|
|
|
100
108
|
- 对话中透露的新信息 → 更新 `volumes/vol-XXX/tracking/` 中的 character-state 或 relationships
|
|
101
109
|
- 新的场景细节 → 如有重要设定变化,更新相关 tracking
|
|
102
110
|
|
|
103
|
-
### 5.
|
|
111
|
+
### 5. DB 同步(可选)
|
|
112
|
+
|
|
113
|
+
如果 `resources/config.json` 中 `database.enabled = true`,在 tracking 更新完成后运行:
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
python scripts/db_sync.py --vol <当前卷号>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 6. 批量模式
|
|
104
120
|
|
|
105
121
|
如果指定了 `--batch N`,重复步骤 2-4 共 N 次。每章完成后输出进度和字数。
|
|
106
122
|
|
|
107
123
|
**批量模式资源隔离**:每章都必须重新从文件系统加载所有资源。批量模式中每章视为独立的扩写任务。如果批量过程中跨卷,需要切换到下一卷的 tracking 和 content 目录。
|
|
108
124
|
|
|
109
|
-
###
|
|
125
|
+
### 7. 后续建议
|
|
110
126
|
|
|
111
127
|
单章完成:「第X章扩写完成(XXXX字,vol-XXX)。继续 /expand [X+1] 或使用 /analyze X 检查质量。」
|
|
112
128
|
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: 逐章生成剧情概要(200-500
|
|
2
|
+
description: 逐章生成剧情概要(200-500字),严格控制字数,同步更新 tracking
|
|
3
3
|
argument-hint: [章节号] [--batch N]
|
|
4
4
|
recommended-model: claude-opus-4-6
|
|
5
|
-
allowed-tools: Read(//stories/**), Write(//stories/**), Bash(ls:*), Bash(mkdir:*)
|
|
5
|
+
allowed-tools: Read(//stories/**), Write(//stories/**), Bash(ls:*), Bash(mkdir:*), Bash(python:scripts/*)
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
用户输入:$ARGUMENTS
|
|
9
9
|
|
|
10
10
|
## 目标
|
|
11
11
|
|
|
12
|
-
为指定章节生成 200-500
|
|
12
|
+
为指定章节生成 200-500 字的纯剧情概要(严格控制字数),同步生成 tracking 骨架数据。
|
|
13
13
|
|
|
14
14
|
## 参数解析
|
|
15
15
|
|
|
@@ -37,6 +37,12 @@ allowed-tools: Read(//stories/**), Write(//stories/**), Bash(ls:*), Bash(mkdir:*
|
|
|
37
37
|
|
|
38
38
|
当目标章节属于新卷时(上一卷已完成):
|
|
39
39
|
|
|
40
|
+
**路径 A — DB 模式**(`resources/config.json` 中 `database.enabled = true`):
|
|
41
|
+
|
|
42
|
+
运行 `python scripts/db_volume_switch.py --vol <新卷号>` 自动生成 volume-summary.md。输出还包含主角当前修炼进度、技能清单和道具状态,用于概要中主角能力描写的一致性。
|
|
43
|
+
|
|
44
|
+
**路径 B — 文件模式**(默认):
|
|
45
|
+
|
|
40
46
|
1. 读取上一卷 `volumes/vol-NNN/tracking/` 的 4 个 JSON 文件
|
|
41
47
|
2. 读取上一卷最后一章 synopsis 的章末钩子
|
|
42
48
|
3. 读取 `creative-plan.md` 中下一卷的大纲段落
|
|
@@ -46,7 +52,7 @@ allowed-tools: Read(//stories/**), Write(//stories/**), Bash(ls:*), Bash(mkdir:*
|
|
|
46
52
|
- 活跃伏笔:从 plot-tracker.json 提取 status=planted/hinted 的条目
|
|
47
53
|
- 关键关系:从 relationships.json 提取活跃角色间的关系
|
|
48
54
|
- 待续悬念:上一卷末章的章末钩子
|
|
49
|
-
5. volume-summary.md 控制在 500-
|
|
55
|
+
5. volume-summary.md 控制在 500-2000 字(严格控制字数)
|
|
50
56
|
|
|
51
57
|
## 资源加载(卷级)
|
|
52
58
|
|
|
@@ -74,7 +80,7 @@ allowed-tools: Read(//stories/**), Write(//stories/**), Bash(ls:*), Bash(mkdir:*
|
|
|
74
80
|
|
|
75
81
|
### 4. 生成概要
|
|
76
82
|
|
|
77
|
-
为当前章节生成 200-500
|
|
83
|
+
为当前章节生成 200-500 字纯剧情概要(严格控制字数),包含:
|
|
78
84
|
|
|
79
85
|
- **本章标题**:简短的章节标题
|
|
80
86
|
- **核心事件**:本章发生的主要事件(1-3个)
|
|
@@ -105,11 +111,21 @@ allowed-tools: Read(//stories/**), Write(//stories/**), Bash(ls:*), Bash(mkdir:*
|
|
|
105
111
|
**timeline.json**:
|
|
106
112
|
- 添加本章事件到 events(chapter, time, event)
|
|
107
113
|
|
|
108
|
-
### 6.
|
|
114
|
+
### 6. DB 同步(可选)
|
|
115
|
+
|
|
116
|
+
如果 `resources/config.json` 中 `database.enabled = true`,在 tracking 更新完成后运行:
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
python scripts/db_sync.py --vol <当前卷号>
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
将本卷 tracking 数据同步到 PostgreSQL。
|
|
123
|
+
|
|
124
|
+
### 7. 批量模式
|
|
109
125
|
|
|
110
126
|
如果指定了 `--batch N`,重复步骤 3-5 共 N 次。如果批量过程中跨卷,自动触发卷切换。
|
|
111
127
|
|
|
112
|
-
###
|
|
128
|
+
### 8. 后续建议
|
|
113
129
|
|
|
114
130
|
单章完成:「第X章概要已生成(vol-XXX)。继续 /write [X+1] 或 /write --batch 20 批量生成。」
|
|
115
131
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.view-header[data-v-df160007]{display:flex;justify-content:space-between;align-items:center}.filters[data-v-df160007]{display:flex;gap:10px}.chapter-card[data-v-df160007]{margin-bottom:12px}.ch-header[data-v-df160007]{display:flex;justify-content:space-between;align-items:center}.ch-number[data-v-df160007]{font-weight:700;font-size:14px}.ch-meta[data-v-df160007]{margin-top:6px;font-size:12px;color:#909399;display:flex;gap:12px}.ch-participants[data-v-df160007],.ch-badges[data-v-df160007]{margin-top:6px}.more[data-v-df160007]{font-size:12px;color:#909399}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.view-header[data-v-d7299ae6]{display:flex;justify-content:space-between;align-items:center}.filters[data-v-d7299ae6]{display:flex;gap:10px}.char-grid[data-v-d7299ae6]{margin-top:16px}.char-card[data-v-d7299ae6]{cursor:pointer;margin-bottom:16px;text-align:center}.char-avatar[data-v-d7299ae6]{width:60px;height:60px;border-radius:50%;background:#409eff;color:#fff;font-size:24px;line-height:60px;margin:0 auto 10px}.char-name[data-v-d7299ae6]{font-size:16px;font-weight:700;margin-bottom:6px}.faction-tag[data-v-d7299ae6]{margin-left:4px}.char-meta[data-v-d7299ae6]{margin-top:8px;font-size:12px;color:#909399;display:flex;justify-content:center;align-items:center;gap:8px}.meta[data-v-d7299ae6]{font-size:12px;color:#909399}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.stat-cards[data-v-736fa3cb]{margin-top:16px}.stat-card[data-v-736fa3cb]{text-align:center}.stat-value[data-v-736fa3cb]{font-size:28px;font-weight:700;color:#303133}.stat-label[data-v-736fa3cb]{font-size:13px;color:#909399;margin-top:4px}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.kanban[data-v-63b8f4a4]{display:flex;gap:16px}.kanban-column[data-v-63b8f4a4]{flex:1;min-width:0}.kanban-title[data-v-63b8f4a4]{display:flex;align-items:center;gap:8px;margin-bottom:10px}.kanban-count[data-v-63b8f4a4]{font-size:12px;color:#909399}.kanban-items[data-v-63b8f4a4]{display:flex;flex-direction:column;gap:8px}.kanban-item[data-v-63b8f4a4]{cursor:default}.plot-name[data-v-63b8f4a4]{font-weight:700;margin-bottom:4px}.plot-type[data-v-63b8f4a4]{font-size:12px;color:#909399}.plot-desc[data-v-63b8f4a4]{font-size:13px;margin-top:4px;color:#606266}.foreshadow-header[data-v-63b8f4a4]{display:flex;justify-content:space-between;align-items:center}.fs-stats[data-v-63b8f4a4]{display:flex;gap:8px}.matrix-wrapper[data-v-63b8f4a4]{overflow-x:auto}.matrix-table[data-v-63b8f4a4]{border-collapse:collapse;font-size:12px}.matrix-table th[data-v-63b8f4a4],.matrix-table td[data-v-63b8f4a4]{border:1px solid #ebeef5;padding:4px 6px;text-align:center}.matrix-label[data-v-63b8f4a4]{text-align:left;white-space:nowrap;max-width:100px;overflow:hidden;text-overflow:ellipsis}.matrix-ch[data-v-63b8f4a4]{min-width:28px;font-size:11px;color:#909399}.matrix-cell[data-v-63b8f4a4]{min-width:28px}.dot[data-v-63b8f4a4]{font-size:14px}.dot-plant[data-v-63b8f4a4]{color:#67c23a}.dot-hint[data-v-63b8f4a4]{color:#e6a23c}.dot-resolve[data-v-63b8f4a4]{color:#f56c6c}.matrix-legend[data-v-63b8f4a4]{margin-top:8px;display:flex;gap:16px;font-size:12px;color:#606266}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.stat-cards[data-v-c59ce1be]{margin-top:16px}.stat-card[data-v-c59ce1be]{text-align:center}.stat-value[data-v-c59ce1be]{font-size:28px;font-weight:700;color:#303133}.stat-label[data-v-c59ce1be]{font-size:13px;color:#909399;margin-top:4px}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.view-header[data-v-8f042607]{display:flex;justify-content:space-between;align-items:center}.filters[data-v-8f042607]{display:flex;gap:10px}.graph-card[data-v-8f042607]{margin-top:16px}.network-container[data-v-8f042607]{height:500px;border:1px solid #ebeef5;border-radius:4px}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.view-header[data-v-39de3e34]{display:flex;justify-content:space-between;align-items:center}.filters[data-v-39de3e34]{display:flex;gap:10px}.event-card[data-v-39de3e34]{cursor:pointer}.event-header[data-v-39de3e34]{display:flex;justify-content:space-between;align-items:center}.event-desc[data-v-39de3e34]{font-weight:500}.event-detail[data-v-39de3e34]{margin-top:8px}
|