@musnows/scriverse 0.7.2 → 0.7.4
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.en.md +3 -0
- package/README.md +8 -0
- package/dist/ai-connectivity-test.js +109 -0
- package/dist/ai-connectivity-test.js.map +1 -0
- package/dist/ai-conversation-export.js +70 -0
- package/dist/ai-conversation-export.js.map +1 -0
- package/dist/ai-stream-timeout.js +18 -0
- package/dist/ai-stream-timeout.js.map +1 -0
- package/dist/ai.js +926 -354
- package/dist/ai.js.map +1 -1
- package/dist/app.js +498 -80
- package/dist/app.js.map +1 -1
- package/dist/attachment-storage.js +28 -12
- package/dist/attachment-storage.js.map +1 -1
- package/dist/backup-encryption.js +129 -0
- package/dist/backup-encryption.js.map +1 -0
- package/dist/character-extraction.js +133 -0
- package/dist/character-extraction.js.map +1 -0
- package/dist/cli-core.js +7 -6
- package/dist/cli-core.js.map +1 -1
- package/dist/collaboration-presence.js +200 -18
- package/dist/collaboration-presence.js.map +1 -1
- package/dist/database.js +335 -3
- package/dist/database.js.map +1 -1
- package/dist/docx-export.js +1 -1
- package/dist/docx-export.js.map +1 -1
- package/dist/domain.js +18 -0
- package/dist/domain.js.map +1 -1
- package/dist/epub-export.js +319 -0
- package/dist/epub-export.js.map +1 -0
- package/dist/hybrid-search.js +8 -0
- package/dist/hybrid-search.js.map +1 -1
- package/dist/image-metadata.js +17 -2
- package/dist/image-metadata.js.map +1 -1
- package/dist/presence-store.js +160 -0
- package/dist/presence-store.js.map +1 -0
- package/dist/public/ai-connectivity-test.d.ts +7 -0
- package/dist/public/ai-connectivity-test.js +82 -0
- package/dist/public/ai-context-meter.js +27 -0
- package/dist/public/ai-mentions.js +14 -0
- package/dist/public/ai-request-manager.js +99 -0
- package/dist/public/ai-stream-protocol.js +51 -0
- package/dist/public/app.js +3587 -559
- package/dist/public/background-task-center.js +1 -1
- package/dist/public/chapter-editor-virtualization.js +52 -0
- package/dist/public/chapter-version-diff.d.ts +20 -0
- package/dist/public/chapter-version-diff.js +116 -0
- package/dist/public/foreshadow-reminder.d.ts +32 -0
- package/dist/public/foreshadow-reminder.js +73 -0
- package/dist/public/global-replace-refresh.js +60 -0
- package/dist/public/index.html +161 -31
- package/dist/public/outline-board.d.ts +61 -0
- package/dist/public/outline-board.js +137 -0
- package/dist/public/page-route.d.ts +1 -0
- package/dist/public/page-route.js +8 -0
- package/dist/public/presence-client-id.d.ts +10 -0
- package/dist/public/presence-client-id.js +31 -0
- package/dist/public/reading-preview.d.ts +32 -0
- package/dist/public/reading-preview.js +136 -0
- package/dist/public/s3-backup-ui.d.ts +10 -0
- package/dist/public/s3-backup-ui.js +29 -0
- package/dist/public/setting-filters.d.ts +16 -0
- package/dist/public/setting-filters.js +19 -0
- package/dist/public/styles.css +522 -11
- package/dist/public/upload-progress.d.ts +2 -0
- package/dist/public/upload-progress.js +10 -0
- package/dist/s3-backup.js +197 -12
- package/dist/s3-backup.js.map +1 -1
- package/dist/security.js +86 -21
- package/dist/security.js.map +1 -1
- package/dist/server-runtime.js +38 -18
- package/dist/server-runtime.js.map +1 -1
- package/dist/store.js +899 -104
- package/dist/store.js.map +1 -1
- package/dist/upload-limits.js +35 -0
- package/dist/upload-limits.js.map +1 -0
- package/dist/user-auth.js +56 -10
- package/dist/user-auth.js.map +1 -1
- package/dist/utils.js +3 -0
- package/dist/utils.js.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const activeTaskStatuses = new Set(["pending", "running"]);
|
|
2
|
-
const terminalTaskStatuses = new Set(["review", "completed", "partial", "expired", "cancelled"]);
|
|
2
|
+
const terminalTaskStatuses = new Set(["review", "completed", "partial", "failed", "expired", "cancelled"]);
|
|
3
3
|
|
|
4
4
|
export function backgroundTaskActivityCount(taskPage, relationshipIndex) {
|
|
5
5
|
const pendingCount = Number(taskPage?.stats?.pendingCount ?? 0);
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const lineMarker = "\u200b";
|
|
2
|
+
|
|
3
|
+
export function buildChapterLineMirror(value) {
|
|
4
|
+
const lines = String(value ?? "").replace(/\r\n?/gu, "\n").split("\n");
|
|
5
|
+
const offsets = [];
|
|
6
|
+
const parts = [];
|
|
7
|
+
let offset = 0;
|
|
8
|
+
lines.forEach((line, index) => {
|
|
9
|
+
offsets.push(offset);
|
|
10
|
+
parts.push(lineMarker, line);
|
|
11
|
+
offset += lineMarker.length + line.length;
|
|
12
|
+
if (index < lines.length - 1) {
|
|
13
|
+
parts.push("\n");
|
|
14
|
+
offset += 1;
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
return { lines, offsets, text: parts.join("") };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function findChapterLineWindow(lineCount, getBounds, viewportStart, viewportEnd) {
|
|
21
|
+
const count = Math.max(0, Math.floor(Number(lineCount) || 0));
|
|
22
|
+
if (count === 0) return { start: 0, end: 0 };
|
|
23
|
+
const startTarget = Number.isFinite(Number(viewportStart)) ? Number(viewportStart) : 0;
|
|
24
|
+
const endTarget = Math.max(startTarget, Number.isFinite(Number(viewportEnd)) ? Number(viewportEnd) : startTarget);
|
|
25
|
+
|
|
26
|
+
let low = 0;
|
|
27
|
+
let high = count - 1;
|
|
28
|
+
let start = count - 1;
|
|
29
|
+
while (low <= high) {
|
|
30
|
+
const middle = Math.floor((low + high) / 2);
|
|
31
|
+
if (getBounds(middle).bottom >= startTarget) {
|
|
32
|
+
start = middle;
|
|
33
|
+
high = middle - 1;
|
|
34
|
+
} else {
|
|
35
|
+
low = middle + 1;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
low = start;
|
|
40
|
+
high = count - 1;
|
|
41
|
+
let end = start;
|
|
42
|
+
while (low <= high) {
|
|
43
|
+
const middle = Math.floor((low + high) / 2);
|
|
44
|
+
if (getBounds(middle).top <= endTarget) {
|
|
45
|
+
end = middle;
|
|
46
|
+
low = middle + 1;
|
|
47
|
+
} else {
|
|
48
|
+
high = middle - 1;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return { start, end: end + 1 };
|
|
52
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type ChapterDiffRow =
|
|
2
|
+
| { type: "equal"; before: string; after: string; beforeLine: number; afterLine: number }
|
|
3
|
+
| { type: "added"; after: string; afterLine: number }
|
|
4
|
+
| { type: "deleted"; before: string; beforeLine: number }
|
|
5
|
+
| { type: "modified"; before: string; after: string; beforeLine: number; afterLine: number };
|
|
6
|
+
|
|
7
|
+
export type ChapterDiffSummary = {
|
|
8
|
+
added: number;
|
|
9
|
+
deleted: number;
|
|
10
|
+
modified: number;
|
|
11
|
+
unchanged: number;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export function diffChapterLines(
|
|
15
|
+
beforeContent: unknown,
|
|
16
|
+
afterContent: unknown,
|
|
17
|
+
matrixCellLimit?: number
|
|
18
|
+
): ChapterDiffRow[];
|
|
19
|
+
|
|
20
|
+
export function chapterDiffSummary(rows: ChapterDiffRow[]): ChapterDiffSummary;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
const DEFAULT_MATRIX_CELL_LIMIT = 2_000_000;
|
|
2
|
+
|
|
3
|
+
function chapterLines(content) {
|
|
4
|
+
return String(content ?? "").replace(/\r\n?/gu, "\n").split("\n");
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function positionalDiff(beforeLines, afterLines) {
|
|
8
|
+
const rows = [];
|
|
9
|
+
const length = Math.max(beforeLines.length, afterLines.length);
|
|
10
|
+
for (let index = 0; index < length; index += 1) {
|
|
11
|
+
const before = beforeLines[index];
|
|
12
|
+
const after = afterLines[index];
|
|
13
|
+
if (before === undefined) rows.push({ type: "added", after, afterLine: index + 1 });
|
|
14
|
+
else if (after === undefined) rows.push({ type: "deleted", before, beforeLine: index + 1 });
|
|
15
|
+
else if (before === after) rows.push({ type: "equal", before, after, beforeLine: index + 1, afterLine: index + 1 });
|
|
16
|
+
else rows.push({ type: "modified", before, after, beforeLine: index + 1, afterLine: index + 1 });
|
|
17
|
+
}
|
|
18
|
+
return rows;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function rawLineDiff(beforeLines, afterLines, matrixCellLimit) {
|
|
22
|
+
const rows = beforeLines.length + 1;
|
|
23
|
+
const columns = afterLines.length + 1;
|
|
24
|
+
if (rows * columns > matrixCellLimit) return null;
|
|
25
|
+
const matrix = Array.from({ length: rows }, () => new Uint32Array(columns));
|
|
26
|
+
for (let beforeIndex = beforeLines.length - 1; beforeIndex >= 0; beforeIndex -= 1) {
|
|
27
|
+
for (let afterIndex = afterLines.length - 1; afterIndex >= 0; afterIndex -= 1) {
|
|
28
|
+
matrix[beforeIndex][afterIndex] = beforeLines[beforeIndex] === afterLines[afterIndex]
|
|
29
|
+
? matrix[beforeIndex + 1][afterIndex + 1] + 1
|
|
30
|
+
: Math.max(matrix[beforeIndex + 1][afterIndex], matrix[beforeIndex][afterIndex + 1]);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const diff = [];
|
|
35
|
+
let beforeIndex = 0;
|
|
36
|
+
let afterIndex = 0;
|
|
37
|
+
while (beforeIndex < beforeLines.length || afterIndex < afterLines.length) {
|
|
38
|
+
if (beforeIndex < beforeLines.length && afterIndex < afterLines.length
|
|
39
|
+
&& beforeLines[beforeIndex] === afterLines[afterIndex]) {
|
|
40
|
+
diff.push({ type: "equal", text: beforeLines[beforeIndex], beforeLine: beforeIndex + 1, afterLine: afterIndex + 1 });
|
|
41
|
+
beforeIndex += 1;
|
|
42
|
+
afterIndex += 1;
|
|
43
|
+
} else if (afterIndex < afterLines.length
|
|
44
|
+
&& (beforeIndex >= beforeLines.length || matrix[beforeIndex][afterIndex + 1] >= matrix[beforeIndex + 1][afterIndex])) {
|
|
45
|
+
diff.push({ type: "added", text: afterLines[afterIndex], afterLine: afterIndex + 1 });
|
|
46
|
+
afterIndex += 1;
|
|
47
|
+
} else {
|
|
48
|
+
diff.push({ type: "deleted", text: beforeLines[beforeIndex], beforeLine: beforeIndex + 1 });
|
|
49
|
+
beforeIndex += 1;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return diff;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function pairChangedLines(changes) {
|
|
56
|
+
const deleted = changes.filter((row) => row.type === "deleted");
|
|
57
|
+
const added = changes.filter((row) => row.type === "added");
|
|
58
|
+
const paired = [];
|
|
59
|
+
const pairCount = Math.min(deleted.length, added.length);
|
|
60
|
+
for (let index = 0; index < pairCount; index += 1) {
|
|
61
|
+
paired.push({
|
|
62
|
+
type: "modified",
|
|
63
|
+
before: deleted[index].text,
|
|
64
|
+
after: added[index].text,
|
|
65
|
+
beforeLine: deleted[index].beforeLine,
|
|
66
|
+
afterLine: added[index].afterLine
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
for (let index = pairCount; index < deleted.length; index += 1) {
|
|
70
|
+
paired.push({ type: "deleted", before: deleted[index].text, beforeLine: deleted[index].beforeLine });
|
|
71
|
+
}
|
|
72
|
+
for (let index = pairCount; index < added.length; index += 1) {
|
|
73
|
+
paired.push({ type: "added", after: added[index].text, afterLine: added[index].afterLine });
|
|
74
|
+
}
|
|
75
|
+
return paired;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function diffChapterLines(beforeContent, afterContent, matrixCellLimit = DEFAULT_MATRIX_CELL_LIMIT) {
|
|
79
|
+
const beforeLines = chapterLines(beforeContent);
|
|
80
|
+
const afterLines = chapterLines(afterContent);
|
|
81
|
+
const raw = rawLineDiff(beforeLines, afterLines, matrixCellLimit);
|
|
82
|
+
if (!raw) return positionalDiff(beforeLines, afterLines);
|
|
83
|
+
|
|
84
|
+
const result = [];
|
|
85
|
+
let index = 0;
|
|
86
|
+
while (index < raw.length) {
|
|
87
|
+
if (raw[index].type === "equal") {
|
|
88
|
+
result.push({
|
|
89
|
+
type: "equal",
|
|
90
|
+
before: raw[index].text,
|
|
91
|
+
after: raw[index].text,
|
|
92
|
+
beforeLine: raw[index].beforeLine,
|
|
93
|
+
afterLine: raw[index].afterLine
|
|
94
|
+
});
|
|
95
|
+
index += 1;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const changes = [];
|
|
99
|
+
while (index < raw.length && raw[index].type !== "equal") {
|
|
100
|
+
changes.push(raw[index]);
|
|
101
|
+
index += 1;
|
|
102
|
+
}
|
|
103
|
+
result.push(...pairChangedLines(changes));
|
|
104
|
+
}
|
|
105
|
+
return result;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function chapterDiffSummary(rows) {
|
|
109
|
+
return rows.reduce((summary, row) => {
|
|
110
|
+
if (row.type === "added") summary.added += 1;
|
|
111
|
+
if (row.type === "deleted") summary.deleted += 1;
|
|
112
|
+
if (row.type === "modified") summary.modified += 1;
|
|
113
|
+
if (row.type === "equal") summary.unchanged += 1;
|
|
114
|
+
return summary;
|
|
115
|
+
}, { added: 0, deleted: 0, modified: 0, unchanged: 0 });
|
|
116
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export type ForeshadowReminder = {
|
|
2
|
+
foreshadowId: string;
|
|
3
|
+
occurrenceId: string;
|
|
4
|
+
title: string;
|
|
5
|
+
description: string;
|
|
6
|
+
status: "planned" | "planted";
|
|
7
|
+
importance: "low" | "medium" | "high";
|
|
8
|
+
role: "reminder" | "payoff";
|
|
9
|
+
note: string;
|
|
10
|
+
versionNo: number;
|
|
11
|
+
updatedAt: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const FORESHADOW_REMINDER_SNOOZE_STORAGE_KEY: string;
|
|
15
|
+
export function normalizeForeshadowReminders(value: unknown): ForeshadowReminder[];
|
|
16
|
+
export function foreshadowReminderSnoozeKey(
|
|
17
|
+
workId: unknown,
|
|
18
|
+
chapterId: unknown,
|
|
19
|
+
reminder: Partial<ForeshadowReminder> | null | undefined
|
|
20
|
+
): string | null;
|
|
21
|
+
export function parseForeshadowReminderSnoozes(serialized: unknown): Set<string>;
|
|
22
|
+
export function serializeForeshadowReminderSnoozes(snoozes: ReadonlySet<string>, limit?: number): string;
|
|
23
|
+
export function visibleForeshadowReminders(
|
|
24
|
+
reminders: unknown,
|
|
25
|
+
workId: unknown,
|
|
26
|
+
chapterId: unknown,
|
|
27
|
+
snoozes: ReadonlySet<string>
|
|
28
|
+
): ForeshadowReminder[];
|
|
29
|
+
export function foreshadowReminderRequestTargetsState(
|
|
30
|
+
request: { workId: unknown; chapterId: unknown } | null | undefined,
|
|
31
|
+
current: { workId?: unknown; chapterId?: unknown } | null | undefined
|
|
32
|
+
): boolean;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
export const FORESHADOW_REMINDER_SNOOZE_STORAGE_KEY = "scriverse.foreshadow-reminder-snoozes.v1";
|
|
2
|
+
|
|
3
|
+
function normalizedId(value) {
|
|
4
|
+
const normalized = String(value ?? "").trim();
|
|
5
|
+
return normalized || null;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function normalizeForeshadowReminders(value) {
|
|
9
|
+
if (!Array.isArray(value)) return [];
|
|
10
|
+
return value.flatMap((item) => {
|
|
11
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) return [];
|
|
12
|
+
const foreshadowId = normalizedId(item.foreshadowId);
|
|
13
|
+
const occurrenceId = normalizedId(item.occurrenceId);
|
|
14
|
+
const title = String(item.title ?? "").trim();
|
|
15
|
+
const role = item.role === "payoff" ? "payoff" : item.role === "reminder" ? "reminder" : null;
|
|
16
|
+
const versionNo = Number(item.versionNo);
|
|
17
|
+
if (!foreshadowId || !occurrenceId || !title || !role || !Number.isInteger(versionNo) || versionNo < 1) return [];
|
|
18
|
+
return [{
|
|
19
|
+
foreshadowId,
|
|
20
|
+
occurrenceId,
|
|
21
|
+
title,
|
|
22
|
+
description: String(item.description ?? ""),
|
|
23
|
+
status: item.status === "planned" ? "planned" : "planted",
|
|
24
|
+
importance: ["low", "medium", "high"].includes(item.importance) ? item.importance : "medium",
|
|
25
|
+
role,
|
|
26
|
+
note: String(item.note ?? ""),
|
|
27
|
+
versionNo,
|
|
28
|
+
updatedAt: String(item.updatedAt ?? "")
|
|
29
|
+
}];
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function foreshadowReminderSnoozeKey(workId, chapterId, reminder) {
|
|
34
|
+
const parts = [
|
|
35
|
+
normalizedId(workId),
|
|
36
|
+
normalizedId(chapterId),
|
|
37
|
+
normalizedId(reminder?.foreshadowId),
|
|
38
|
+
normalizedId(reminder?.occurrenceId)
|
|
39
|
+
];
|
|
40
|
+
const versionNo = Number(reminder?.versionNo);
|
|
41
|
+
if (parts.some((part) => !part) || !Number.isInteger(versionNo) || versionNo < 1) return null;
|
|
42
|
+
return [...parts, `v${versionNo}`].map((part) => encodeURIComponent(part)).join("|");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function parseForeshadowReminderSnoozes(serialized) {
|
|
46
|
+
try {
|
|
47
|
+
const value = JSON.parse(String(serialized ?? "[]"));
|
|
48
|
+
return new Set(Array.isArray(value) ? value.filter((item) => typeof item === "string" && item.length <= 1000) : []);
|
|
49
|
+
} catch {
|
|
50
|
+
return new Set();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function serializeForeshadowReminderSnoozes(snoozes, limit = 500) {
|
|
55
|
+
const safeLimit = Number.isInteger(limit) && limit > 0 ? limit : 500;
|
|
56
|
+
const values = [...snoozes].filter((item) => typeof item === "string" && item.length <= 1000);
|
|
57
|
+
return JSON.stringify(values.slice(-safeLimit));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function visibleForeshadowReminders(reminders, workId, chapterId, snoozes) {
|
|
61
|
+
return normalizeForeshadowReminders(reminders).filter((reminder) => {
|
|
62
|
+
const key = foreshadowReminderSnoozeKey(workId, chapterId, reminder);
|
|
63
|
+
return key && !snoozes.has(key);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function foreshadowReminderRequestTargetsState(request, current) {
|
|
68
|
+
return Boolean(
|
|
69
|
+
request
|
|
70
|
+
&& normalizedId(request.workId) === normalizedId(current?.workId)
|
|
71
|
+
&& normalizedId(request.chapterId) === normalizedId(current?.chapterId)
|
|
72
|
+
);
|
|
73
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
function normalizeId(value) {
|
|
2
|
+
const id = String(value ?? "").trim();
|
|
3
|
+
return id || null;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
function collapsedIds(value) {
|
|
7
|
+
if (value instanceof Set) return value;
|
|
8
|
+
return new Set(Array.isArray(value) ? value.map(normalizeId).filter(Boolean) : []);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function normalizedChapterCount(value) {
|
|
12
|
+
if (value === null || value === undefined || value === "") return null;
|
|
13
|
+
const count = Number(value);
|
|
14
|
+
return Number.isSafeInteger(count) && count >= 0 ? count : null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function resolveGlobalReplaceChapterCount(volume, previousVolume) {
|
|
18
|
+
const responseCount = normalizedChapterCount(volume?.chapterCount);
|
|
19
|
+
if (responseCount !== null) return responseCount;
|
|
20
|
+
if (Array.isArray(volume?.chapters)) return volume.chapters.length;
|
|
21
|
+
const previousCount = normalizedChapterCount(previousVolume?.chapterCount);
|
|
22
|
+
if (previousCount !== null) return previousCount;
|
|
23
|
+
return Array.isArray(previousVolume?.chapters) ? previousVolume.chapters.length : 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function buildGlobalReplaceRefreshPlan({
|
|
27
|
+
volumes = [],
|
|
28
|
+
collapsedVolumeIds = [],
|
|
29
|
+
selectedChapterId = null,
|
|
30
|
+
selectedChapterVolumeId = null,
|
|
31
|
+
routeChapterId = null,
|
|
32
|
+
scope = "",
|
|
33
|
+
chapterCount = 0,
|
|
34
|
+
settingCount = 0
|
|
35
|
+
} = {}) {
|
|
36
|
+
const volumeList = Array.isArray(volumes) ? volumes : [];
|
|
37
|
+
const collapsed = collapsedIds(collapsedVolumeIds);
|
|
38
|
+
const chapterId = normalizeId(selectedChapterId) ?? normalizeId(routeChapterId);
|
|
39
|
+
const chapterVolumeId = normalizeId(selectedChapterVolumeId)
|
|
40
|
+
?? normalizeId(volumeList.find((volume) => Array.isArray(volume?.chapters)
|
|
41
|
+
&& volume.chapters.some((chapter) => normalizeId(chapter?.id) === chapterId))?.id);
|
|
42
|
+
const proseChanged = (scope === "prose" || scope === "prose-and-settings") && Number(chapterCount) > 0;
|
|
43
|
+
const settingsChanged = (scope === "settings" || scope === "prose-and-settings") && Number(settingCount) > 0;
|
|
44
|
+
const expandedVolumeIds = volumeList
|
|
45
|
+
.filter((volume) => !collapsed.has(volume?.id))
|
|
46
|
+
.map((volume) => normalizeId(volume?.id))
|
|
47
|
+
.filter(Boolean);
|
|
48
|
+
const reloadVolumeIds = proseChanged
|
|
49
|
+
? [...new Set([...expandedVolumeIds, chapterVolumeId].filter(Boolean))]
|
|
50
|
+
: [];
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
proseChanged,
|
|
54
|
+
settingsChanged,
|
|
55
|
+
selectedChapterId: chapterId,
|
|
56
|
+
selectedChapterVolumeId: chapterVolumeId,
|
|
57
|
+
expandedVolumeIds,
|
|
58
|
+
reloadVolumeIds
|
|
59
|
+
};
|
|
60
|
+
}
|