@godcreator/word-report 0.2.0 → 0.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/dist/{assemble-Czu-J7tB.mjs → assemble-D25NLmLx.mjs} +145 -28
- package/dist/cli.d.mts +1 -1
- package/dist/cli.mjs +2 -2
- package/dist/core.d.mts +2 -2
- package/dist/core.mjs +1 -1
- package/dist/{export-Bi5yS3GW.d.mts → export-DC764Umz.d.mts} +1 -1
- package/dist/{export-BdwmhUIp.mjs → export-DJQiBPPU.mjs} +7 -2
- package/dist/node.d.mts +2 -2
- package/dist/node.mjs +2 -2
- package/dist/{page-data-DIsywo6U.d.mts → page-data-CJ6nCBnW.d.mts} +1 -1
- package/dist/{report-files-DwLdh0QQ.mjs → report-files-CgWzLxH-.mjs} +1 -1
- package/dist/site-server.d.mts +1 -1
- package/dist/site-server.mjs +2 -2
- package/dist/site.d.mts +2 -2
- package/dist/site.mjs +48 -11
- package/dist/{types-BYcLlSjZ.d.mts → types-Bcl48xQN.d.mts} +27 -2
- package/engine/src/docreport/compose.py +29 -25
- package/engine/src/docreport/content.py +3 -0
- package/package.json +1 -1
- package/styles/report.css +6 -0
|
@@ -371,13 +371,90 @@ const schema = {
|
|
|
371
371
|
"$ref": "#/$defs/inlines",
|
|
372
372
|
"minItems": 1
|
|
373
373
|
},
|
|
374
|
-
"alt": { "$ref": "#/$defs/line" }
|
|
374
|
+
"alt": { "$ref": "#/$defs/line" },
|
|
375
|
+
"rows": {
|
|
376
|
+
"type": "integer",
|
|
377
|
+
"minimum": 1,
|
|
378
|
+
"maximum": 6
|
|
379
|
+
},
|
|
380
|
+
"columns": {
|
|
381
|
+
"type": "integer",
|
|
382
|
+
"minimum": 1,
|
|
383
|
+
"maximum": 6
|
|
384
|
+
},
|
|
385
|
+
"panels": {
|
|
386
|
+
"type": "array",
|
|
387
|
+
"items": { "$ref": "#/$defs/figurePanel" },
|
|
388
|
+
"minItems": 2,
|
|
389
|
+
"maxItems": 36
|
|
390
|
+
}
|
|
375
391
|
},
|
|
376
392
|
"required": [
|
|
377
393
|
"type",
|
|
378
394
|
"id",
|
|
395
|
+
"caption"
|
|
396
|
+
],
|
|
397
|
+
"additionalProperties": false,
|
|
398
|
+
"allOf": [
|
|
399
|
+
{
|
|
400
|
+
"if": { "required": ["panels"] },
|
|
401
|
+
"then": { "required": ["rows", "columns"] }
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
"if": { "required": ["panels"] },
|
|
405
|
+
"then": { "not": { "required": ["src"] } }
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
"if": { "required": ["panels"] },
|
|
409
|
+
"then": { "not": { "required": ["alt"] } }
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
"if": { "not": { "required": ["panels"] } },
|
|
413
|
+
"then": { "required": ["src", "alt"] }
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
"if": { "not": { "required": ["panels"] } },
|
|
417
|
+
"then": { "not": { "required": ["rows"] } }
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
"if": { "not": { "required": ["panels"] } },
|
|
421
|
+
"then": { "not": { "required": ["columns"] } }
|
|
422
|
+
}
|
|
423
|
+
],
|
|
424
|
+
"description": "A single image (src, alt) or several images under one figure number (rows, columns, panels). Each panel names its own grid cell and label; cells nobody names stay empty. Cells inside the grid, one panel per cell and unique labels are semantic checks."
|
|
425
|
+
},
|
|
426
|
+
"figurePanel": {
|
|
427
|
+
"type": "object",
|
|
428
|
+
"properties": {
|
|
429
|
+
"id": { "$ref": "#/$defs/id" },
|
|
430
|
+
"row": {
|
|
431
|
+
"type": "integer",
|
|
432
|
+
"minimum": 1,
|
|
433
|
+
"maximum": 6
|
|
434
|
+
},
|
|
435
|
+
"column": {
|
|
436
|
+
"type": "integer",
|
|
437
|
+
"minimum": 1,
|
|
438
|
+
"maximum": 6
|
|
439
|
+
},
|
|
440
|
+
"label": {
|
|
441
|
+
"type": "string",
|
|
442
|
+
"pattern": "^[^\\s()()]{1,8}$",
|
|
443
|
+
"description": "Shown under the image as (label) and in references as figure number + (label); written without parentheses."
|
|
444
|
+
},
|
|
445
|
+
"src": { "$ref": "#/$defs/assetPath" },
|
|
446
|
+
"alt": { "$ref": "#/$defs/line" },
|
|
447
|
+
"caption": {
|
|
448
|
+
"$ref": "#/$defs/inlines",
|
|
449
|
+
"minItems": 1
|
|
450
|
+
}
|
|
451
|
+
},
|
|
452
|
+
"required": [
|
|
453
|
+
"id",
|
|
454
|
+
"row",
|
|
455
|
+
"column",
|
|
456
|
+
"label",
|
|
379
457
|
"src",
|
|
380
|
-
"caption",
|
|
381
458
|
"alt"
|
|
382
459
|
],
|
|
383
460
|
"additionalProperties": false
|
|
@@ -675,8 +752,10 @@ function shape(value, rule, pointer, file) {
|
|
|
675
752
|
if (rule.format === "date" && (Number.isNaN(Date.parse(value)) || new Date(value).toISOString().slice(0, 10) !== value)) fail("Invalid calendar date");
|
|
676
753
|
}
|
|
677
754
|
if (typeof value === "number" && rule.minimum !== void 0 && value < rule.minimum) fail(`Minimum is ${rule.minimum}`);
|
|
755
|
+
if (typeof value === "number" && rule.maximum !== void 0 && value > rule.maximum) fail(`Maximum is ${rule.maximum}`);
|
|
678
756
|
if (Array.isArray(value)) {
|
|
679
|
-
if (rule.minItems && value.length < rule.minItems) fail("Array must not be empty");
|
|
757
|
+
if (rule.minItems && value.length < rule.minItems) fail(rule.minItems === 1 ? "Array must not be empty" : `At least ${rule.minItems} items`);
|
|
758
|
+
if (rule.maxItems && value.length > rule.maxItems) fail(`At most ${rule.maxItems} items`);
|
|
680
759
|
if (rule.uniqueItems && new Set(value.map(stableValue)).size !== value.length) fail("Duplicate array values");
|
|
681
760
|
if (rule.items) value.forEach((v, i) => shape(v, rule.items, `${pointer}/${i}`, file));
|
|
682
761
|
if (rule.contains && !value.some((v) => {
|
|
@@ -767,6 +846,23 @@ function assembleDocument(entry, files, options = {}) {
|
|
|
767
846
|
const pending = /* @__PURE__ */ new Map();
|
|
768
847
|
const resourceNames = /* @__PURE__ */ new Map();
|
|
769
848
|
let rootBibliography;
|
|
849
|
+
/** 图片路径改成按文档 id 分开的 assets/<文档 id>/…,同名的章节图片不互相覆盖 */
|
|
850
|
+
function registerAsset(image, file, srcPointer) {
|
|
851
|
+
try {
|
|
852
|
+
safeRelativePath(image.src, true);
|
|
853
|
+
} catch (e) {
|
|
854
|
+
throw new ReportContentError(e.message, file, srcPointer);
|
|
855
|
+
}
|
|
856
|
+
const original = join(file, image.src);
|
|
857
|
+
const output = `assets/${documents.get(file).id}/${image.src.slice(7)}`;
|
|
858
|
+
for (const resource of [original, output]) {
|
|
859
|
+
const key = resource.normalize("NFC").toLowerCase(), previous = resourceNames.get(key);
|
|
860
|
+
if (previous && previous !== resource) throw new ReportContentError("Asset names collide on Windows", file, srcPointer);
|
|
861
|
+
resourceNames.set(key, resource);
|
|
862
|
+
}
|
|
863
|
+
assets[output] = original;
|
|
864
|
+
image.src = output;
|
|
865
|
+
}
|
|
770
866
|
function register(value, file, prefix) {
|
|
771
867
|
for (const { node, pointer } of walk(value, prefix)) {
|
|
772
868
|
if (node.id !== void 0) {
|
|
@@ -778,20 +874,8 @@ function assembleDocument(entry, files, options = {}) {
|
|
|
778
874
|
};
|
|
779
875
|
}
|
|
780
876
|
if (node.type === "figure") {
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
} catch (e) {
|
|
784
|
-
throw new ReportContentError(e.message, file, pointer + "/src");
|
|
785
|
-
}
|
|
786
|
-
const original = join(file, node.src);
|
|
787
|
-
const output = `assets/${documents.get(file).id}/${node.src.slice(7)}`;
|
|
788
|
-
for (const resource of [original, output]) {
|
|
789
|
-
const key = resource.normalize("NFC").toLowerCase(), previous = resourceNames.get(key);
|
|
790
|
-
if (previous && previous !== resource) throw new ReportContentError("Asset names collide on Windows", file, pointer + "/src");
|
|
791
|
-
resourceNames.set(key, resource);
|
|
792
|
-
}
|
|
793
|
-
assets[output] = original;
|
|
794
|
-
node.src = output;
|
|
877
|
+
const images = node.panels ? node.panels.map((panel, k) => [panel, `${pointer}/panels/${k}/src`]) : [[node, `${pointer}/src`]];
|
|
878
|
+
for (const [image, srcPointer] of images) registerAsset(image, file, srcPointer);
|
|
795
879
|
}
|
|
796
880
|
}
|
|
797
881
|
}
|
|
@@ -892,6 +976,7 @@ function assembleDocument(entry, files, options = {}) {
|
|
|
892
976
|
const source = sources[node.id];
|
|
893
977
|
validateTable(node, source.file, source.pointer);
|
|
894
978
|
}
|
|
979
|
+
if (node.type === "figure" && node.panels) validatePanelGrid(node, fail);
|
|
895
980
|
if (node.type === "math" || node.type === "equation") {
|
|
896
981
|
const latex = node.latex.trim();
|
|
897
982
|
if (!latex || /[^\x00-\x7f]/.test(latex) || /^(\$|\\\(|\\\[)/.test(latex) || /\\(?:tag|label|documentclass)\b|\\begin\s*\{document\}/.test(latex)) fail(node, "MathType requires nonempty ASCII TeX without delimiters or manual numbering");
|
|
@@ -909,12 +994,21 @@ function assembleDocument(entry, files, options = {}) {
|
|
|
909
994
|
}
|
|
910
995
|
const counts = {
|
|
911
996
|
decimal: 0,
|
|
912
|
-
appendix: 0
|
|
913
|
-
figure: 0,
|
|
914
|
-
table: 0
|
|
997
|
+
appendix: 0
|
|
915
998
|
};
|
|
916
999
|
let appendixSeen = false;
|
|
917
|
-
const
|
|
1000
|
+
const chapterCounts = {
|
|
1001
|
+
figure: /* @__PURE__ */ new Map(),
|
|
1002
|
+
table: /* @__PURE__ */ new Map(),
|
|
1003
|
+
equation: /* @__PURE__ */ new Map()
|
|
1004
|
+
};
|
|
1005
|
+
/** 图、编号表格、编号公式各自按所在的顶层章从 1 编,写成「章号-n」;导出 Word 的题注同样取章号,不在编号章里就没有章号可取 */
|
|
1006
|
+
function chapterNumber(node, kind, chapter) {
|
|
1007
|
+
if (!chapter) fail(node, `Numbered ${kind} outside a numbered chapter`);
|
|
1008
|
+
const n = (chapterCounts[kind].get(chapter) ?? 0) + 1;
|
|
1009
|
+
chapterCounts[kind].set(chapter, n);
|
|
1010
|
+
return `${chapter}-${n}`;
|
|
1011
|
+
}
|
|
918
1012
|
function blockNumbers(blocks, depth = 0, chapter, parentNumber, parentMode, tables = 0) {
|
|
919
1013
|
let decimal = 0;
|
|
920
1014
|
for (const node of blocks) {
|
|
@@ -944,18 +1038,15 @@ function assembleDocument(entry, files, options = {}) {
|
|
|
944
1038
|
continue;
|
|
945
1039
|
}
|
|
946
1040
|
if (node.type === "figure") {
|
|
947
|
-
number =
|
|
1041
|
+
number = chapterNumber(node, "figure", chapter);
|
|
948
1042
|
label = `图 ${number}`;
|
|
949
1043
|
}
|
|
950
1044
|
if (node.type === "table" && node.numbered) {
|
|
951
|
-
number =
|
|
1045
|
+
number = chapterNumber(node, "table", chapter);
|
|
952
1046
|
label = `表 ${number}`;
|
|
953
1047
|
}
|
|
954
1048
|
if (node.type === "equation" && node.numbered) {
|
|
955
|
-
|
|
956
|
-
const n = (eqCounts.get(chapter) ?? 0) + 1;
|
|
957
|
-
eqCounts.set(chapter, n);
|
|
958
|
-
number = `${chapter}-${n}`;
|
|
1049
|
+
number = chapterNumber(node, "equation", chapter);
|
|
959
1050
|
label = `式(${number})`;
|
|
960
1051
|
}
|
|
961
1052
|
if (node.id) index[node.id] = {
|
|
@@ -964,6 +1055,15 @@ function assembleDocument(entry, files, options = {}) {
|
|
|
964
1055
|
label,
|
|
965
1056
|
title
|
|
966
1057
|
};
|
|
1058
|
+
if (node.type === "figure" && node.panels) for (const panel of node.panels) {
|
|
1059
|
+
const panelNumber = `${number}(${panel.label})`;
|
|
1060
|
+
index[panel.id] = {
|
|
1061
|
+
type: "figure-panel",
|
|
1062
|
+
number: panelNumber,
|
|
1063
|
+
label: `图 ${panelNumber}`,
|
|
1064
|
+
title: plain(panel.caption)
|
|
1065
|
+
};
|
|
1066
|
+
}
|
|
967
1067
|
if (node.type === "table") {
|
|
968
1068
|
if (tables >= 6) fail(node, "At most six table levels");
|
|
969
1069
|
for (const row of node.rows) for (const cell of row.cells) blockNumbers(cell.blocks, depth, chapter, parentNumber, parentMode, tables + 1);
|
|
@@ -1033,6 +1133,11 @@ function assembleDocument(entry, files, options = {}) {
|
|
|
1033
1133
|
}
|
|
1034
1134
|
fail(node, `Unknown reference target: ${node.target}`);
|
|
1035
1135
|
}
|
|
1136
|
+
if (index[node.target]?.type === "figure-panel") {
|
|
1137
|
+
if (node.display === "title" && (!target.caption?.length || target.caption.some((x) => !["text", "break"].includes(x.type)))) fail(node, "Title reference requires plain caption");
|
|
1138
|
+
delete node.externalLabel;
|
|
1139
|
+
continue;
|
|
1140
|
+
}
|
|
1036
1141
|
if (![
|
|
1037
1142
|
"section",
|
|
1038
1143
|
"figure",
|
|
@@ -1065,6 +1170,18 @@ function assembleDocument(entry, files, options = {}) {
|
|
|
1065
1170
|
warnings
|
|
1066
1171
|
};
|
|
1067
1172
|
}
|
|
1173
|
+
/** 多图的每张都要在 rows × columns 的网格里、一格只放一张、编号不重复 */
|
|
1174
|
+
function validatePanelGrid(figure, fail) {
|
|
1175
|
+
const occupied = /* @__PURE__ */ new Map(), labels = /* @__PURE__ */ new Map();
|
|
1176
|
+
for (const panel of figure.panels) {
|
|
1177
|
+
if (panel.row > figure.rows || panel.column > figure.columns) fail(panel, `Panel cell row ${panel.row}, column ${panel.column} is outside the ${figure.rows} x ${figure.columns} grid of ${figure.id}`);
|
|
1178
|
+
const cell = `${panel.row},${panel.column}`;
|
|
1179
|
+
if (occupied.has(cell)) fail(panel, `Panel shares row ${panel.row}, column ${panel.column} with ${occupied.get(cell)}`);
|
|
1180
|
+
if (labels.has(panel.label)) fail(panel, `Panel label (${panel.label}) is also used by ${labels.get(panel.label)}`);
|
|
1181
|
+
occupied.set(cell, panel.id);
|
|
1182
|
+
labels.set(panel.label, panel.id);
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1068
1185
|
function alphabet(n) {
|
|
1069
1186
|
let s = "";
|
|
1070
1187
|
while (n > 0) {
|
|
@@ -1075,4 +1192,4 @@ function alphabet(n) {
|
|
|
1075
1192
|
return s;
|
|
1076
1193
|
}
|
|
1077
1194
|
//#endregion
|
|
1078
|
-
export {
|
|
1195
|
+
export { parseReportJson as a, walk as i, safeRelativePath as n, ReportContentError as o, validateDocument as r, assembleDocument as t };
|
package/dist/cli.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as ReportExportResult, o as ReportCheckResult, t as ReportExportOptions } from "./export-
|
|
1
|
+
import { n as ReportExportResult, o as ReportCheckResult, t as ReportExportOptions } from "./export-DC764Umz.mjs";
|
|
2
2
|
//#region src/node/cli.d.ts
|
|
3
3
|
type CliCommand = {
|
|
4
4
|
command: 'export' | 'status';
|
package/dist/cli.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { n as reportExport, r as checkReports } from "./export-
|
|
3
|
-
import { r as reportEntryOf } from "./report-files-
|
|
2
|
+
import { n as reportExport, r as checkReports } from "./export-DJQiBPPU.mjs";
|
|
3
|
+
import { r as reportEntryOf } from "./report-files-CgWzLxH-.mjs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { realpathSync } from "node:fs";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
package/dist/core.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as TableCell, S as Table, _ as
|
|
1
|
+
import { C as SingleImageFigure, E as TableCell, S as Section, T as Table, _ as ReportContentError, a as CellBlock, b as ReportInline, c as ContentWarning, d as FigurePanel, f as Include, g as ReportBlock, h as Paragraph, i as Bibliography, l as Equation, m as MultiImageFigure, n as AssemblyOptions, o as Code, p as IndexEntry, r as AssemblyResult, s as CompiledReport, t as Abstract, u as Figure, v as ReportDocument, w as SourceLocation, x as ReportList, y as ReportIndex } from "./types-Bcl48xQN.mjs";
|
|
2
2
|
//#region src/core/assemble.d.ts
|
|
3
3
|
declare function assembleDocument(entry: string, files: Record<string, string | ReportDocument>, options?: AssemblyOptions): AssemblyResult;
|
|
4
4
|
//#endregion
|
|
@@ -10,4 +10,4 @@ declare function parseReportJson(text: string, file?: string): unknown;
|
|
|
10
10
|
declare function validateDocument(value: unknown, file?: string): asserts value is ReportDocument;
|
|
11
11
|
declare function safeRelativePath(raw: string, asset?: boolean): string;
|
|
12
12
|
//#endregion
|
|
13
|
-
export { Abstract, AssemblyOptions, AssemblyResult, Bibliography, CellBlock, Code, CompiledReport, ContentWarning, Equation, Figure, Include, IndexEntry, Paragraph, ReportBlock, ReportContentError, ReportDocument, ReportIndex, ReportInline, ReportList, Section, SourceLocation, Table, TableCell, assembleDocument, parseReportJson, safeRelativePath, validateDocument };
|
|
13
|
+
export { Abstract, AssemblyOptions, AssemblyResult, Bibliography, CellBlock, Code, CompiledReport, ContentWarning, Equation, Figure, FigurePanel, Include, IndexEntry, MultiImageFigure, Paragraph, ReportBlock, ReportContentError, ReportDocument, ReportIndex, ReportInline, ReportList, Section, SingleImageFigure, SourceLocation, Table, TableCell, assembleDocument, parseReportJson, safeRelativePath, validateDocument };
|
package/dist/core.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { o as ReportContentError, t as assembleDocument, a as parseReportJson, n as safeRelativePath, r as validateDocument } from "./assemble-D25NLmLx.mjs";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import { a as reportsDirName } from "./report-files-
|
|
1
|
+
import { a as parseReportJson, i as walk, n as safeRelativePath, o as ReportContentError, r as validateDocument, t as assembleDocument } from "./assemble-D25NLmLx.mjs";
|
|
2
|
+
import { a as reportsDirName } from "./report-files-CgWzLxH-.mjs";
|
|
3
3
|
import { access, mkdir, open, readFile, readdir, realpath, rename, stat, unlink, writeFile } from "node:fs/promises";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -283,12 +283,17 @@ async function layoutOf(root, entry) {
|
|
|
283
283
|
value: JSON.parse(bytes.toString("utf8"))
|
|
284
284
|
};
|
|
285
285
|
}
|
|
286
|
+
/** Word 引擎还不会排一个图号下的多张图:报告里有多图就在启动 Word 之前拒绝 */
|
|
287
|
+
function rejectMultiImageFigures(document) {
|
|
288
|
+
for (const { node } of walk(document)) if (node.type === "figure" && node.panels) throw new Error(`图 ${node.id} 是多图(panels),导出 Word 还不支持多图;网页上照常显示`);
|
|
289
|
+
}
|
|
286
290
|
async function capture(site, engine, compiler, entry, mode) {
|
|
287
291
|
const root = site;
|
|
288
292
|
const report = await readReport(path.join(root, entry), {
|
|
289
293
|
root,
|
|
290
294
|
mode
|
|
291
295
|
});
|
|
296
|
+
rejectMultiImageFigures(report.document);
|
|
292
297
|
const layout = await layoutOf(root, entry);
|
|
293
298
|
const engineHash = await engineFingerprint(engine, compiler);
|
|
294
299
|
return {
|
package/dist/node.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as AssemblyOptions, r as AssemblyResult } from "./types-
|
|
2
|
-
import { a as ReportCheckDiagnostic, i as reportExport, n as ReportExportResult, o as ReportCheckResult, r as reportEntry, s as checkReports, t as ReportExportOptions } from "./export-
|
|
1
|
+
import { n as AssemblyOptions, r as AssemblyResult } from "./types-Bcl48xQN.mjs";
|
|
2
|
+
import { a as ReportCheckDiagnostic, i as reportExport, n as ReportExportResult, o as ReportCheckResult, r as reportEntry, s as checkReports, t as ReportExportOptions } from "./export-DC764Umz.mjs";
|
|
3
3
|
//#region src/node/read-report.d.ts
|
|
4
4
|
interface SnapshotEntry {
|
|
5
5
|
path: string;
|
package/dist/node.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { i as readReport, n as reportExport, r as checkReports, t as reportEntry } from "./export-
|
|
2
|
-
import { a as reportsDirName, i as reportIdsOf, n as readReportFiles, r as reportEntryOf, t as REPORT_ID_PATTERN } from "./report-files-
|
|
1
|
+
import { i as readReport, n as reportExport, r as checkReports, t as reportEntry } from "./export-DJQiBPPU.mjs";
|
|
2
|
+
import { a as reportsDirName, i as reportIdsOf, n as readReportFiles, r as reportEntryOf, t as REPORT_ID_PATTERN } from "./report-files-CgWzLxH-.mjs";
|
|
3
3
|
export { REPORT_ID_PATTERN, checkReports, readReport, readReportFiles, reportEntry, reportEntryOf, reportExport, reportIdsOf, reportsDirName };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as AssemblyResult } from "./types-
|
|
1
|
+
import { r as AssemblyResult } from "./types-Bcl48xQN.mjs";
|
|
2
2
|
import { CommentsAccess, PageThreadSummary } from "@godcreator/comments/site";
|
|
3
3
|
//#region src/site/page-data.d.ts
|
|
4
4
|
/** 报告列表里的一行:报告读不对时 problem 是原话,标题退回报告 id */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as parseReportJson, n as safeRelativePath, o as ReportContentError, r as validateDocument } from "./assemble-D25NLmLx.mjs";
|
|
2
2
|
import { readFile, readdir, realpath, stat } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
//#region src/node/report-files.ts
|
package/dist/site-server.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as ReportTocItem, i as ReportSummary, n as ReportPageComments, r as ReportPageData, t as ReportListData } from "./page-data-
|
|
1
|
+
import { a as ReportTocItem, i as ReportSummary, n as ReportPageComments, r as ReportPageData, t as ReportListData } from "./page-data-CJ6nCBnW.mjs";
|
|
2
2
|
//#region src/site/loaders.server.d.ts
|
|
3
3
|
/**
|
|
4
4
|
* 报告功能的站点信息:站点目录(报告在它的 reports/ 下)、站名、报告页的地址前缀,
|
package/dist/site-server.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as assembleDocument } from "./assemble-
|
|
2
|
-
import { i as reportIdsOf, n as readReportFiles, r as reportEntryOf, t as REPORT_ID_PATTERN } from "./report-files-
|
|
1
|
+
import { t as assembleDocument } from "./assemble-D25NLmLx.mjs";
|
|
2
|
+
import { i as reportIdsOf, n as readReportFiles, r as reportEntryOf, t as REPORT_ID_PATTERN } from "./report-files-CgWzLxH-.mjs";
|
|
3
3
|
import { n as reportHrefOf } from "./page-data-DLPoVgXy.mjs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { createReadStream } from "node:fs";
|
package/dist/site.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as ReportTocItem, i as ReportSummary, n as ReportPageComments, o as reportAssetHrefOf, r as ReportPageData, s as reportHrefOf, t as ReportListData } from "./page-data-
|
|
1
|
+
import { r as AssemblyResult, v as ReportDocument } from "./types-Bcl48xQN.mjs";
|
|
2
|
+
import { a as ReportTocItem, i as ReportSummary, n as ReportPageComments, o as reportAssetHrefOf, r as ReportPageData, s as reportHrefOf, t as ReportListData } from "./page-data-CJ6nCBnW.mjs";
|
|
3
3
|
import { CommentAvatar, CommentBodyComponent } from "@godcreator/comments/site";
|
|
4
4
|
//#region src/site/report-pages.d.ts
|
|
5
5
|
declare function ReportListPage(): import("react").JSX.Element;
|
package/dist/site.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as assembleDocument } from "./assemble-
|
|
1
|
+
import { t as assembleDocument } from "./assemble-D25NLmLx.mjs";
|
|
2
2
|
import { n as reportHrefOf, t as reportAssetHrefOf } from "./page-data-DLPoVgXy.mjs";
|
|
3
3
|
import { Link, useLoaderData } from "react-router";
|
|
4
4
|
import { DocsBody, DocsPage, DocsTitle } from "@fumadocs/base-ui/layouts/docs/page";
|
|
@@ -178,19 +178,56 @@ function ReportView({ report, assetUrl }) {
|
|
|
178
178
|
})]
|
|
179
179
|
}, node.id);
|
|
180
180
|
case "figure": {
|
|
181
|
-
const
|
|
181
|
+
const image = (src, id, alt) => {
|
|
182
|
+
const source = report.assets[src] ?? src;
|
|
183
|
+
return /* @__PURE__ */ jsx("img", {
|
|
184
|
+
id,
|
|
185
|
+
src: assetUrl?.(source, src) ?? source,
|
|
186
|
+
alt,
|
|
187
|
+
loading: "lazy"
|
|
188
|
+
});
|
|
189
|
+
};
|
|
190
|
+
const caption = /* @__PURE__ */ jsxs("figcaption", {
|
|
191
|
+
id: `${node.id}-caption`,
|
|
192
|
+
children: [label?.number && /* @__PURE__ */ jsxs("span", { children: [label.label, " "] }), inlines(node.caption)]
|
|
193
|
+
});
|
|
194
|
+
if (!node.panels) return /* @__PURE__ */ jsxs("figure", {
|
|
195
|
+
"data-report-source": origin?.file,
|
|
196
|
+
"data-report-pointer": origin?.pointer,
|
|
197
|
+
children: [image(node.src, node.id, node.alt), caption]
|
|
198
|
+
}, node.id);
|
|
182
199
|
return /* @__PURE__ */ jsxs("figure", {
|
|
200
|
+
className: "report-multi-figure",
|
|
183
201
|
"data-report-source": origin?.file,
|
|
184
202
|
"data-report-pointer": origin?.pointer,
|
|
185
|
-
children: [/* @__PURE__ */ jsx("
|
|
203
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
186
204
|
id: node.id,
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
children:
|
|
193
|
-
|
|
205
|
+
className: "report-panels",
|
|
206
|
+
style: {
|
|
207
|
+
gridTemplateColumns: `repeat(${node.columns}, minmax(0, 1fr))`,
|
|
208
|
+
gridTemplateRows: `repeat(${node.rows}, auto)`
|
|
209
|
+
},
|
|
210
|
+
children: node.panels.map((panel) => /* @__PURE__ */ jsxs("div", {
|
|
211
|
+
className: "report-panel",
|
|
212
|
+
style: {
|
|
213
|
+
gridRow: panel.row,
|
|
214
|
+
gridColumn: panel.column
|
|
215
|
+
},
|
|
216
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
217
|
+
className: "report-panel-image",
|
|
218
|
+
children: image(panel.src, panel.id, panel.alt)
|
|
219
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
220
|
+
id: `${panel.id}-caption`,
|
|
221
|
+
className: "report-panel-caption",
|
|
222
|
+
children: [
|
|
223
|
+
"(",
|
|
224
|
+
panel.label,
|
|
225
|
+
")",
|
|
226
|
+
panel.caption && /* @__PURE__ */ jsxs(Fragment$1, { children: [" ", inlines(panel.caption)] })
|
|
227
|
+
]
|
|
228
|
+
})]
|
|
229
|
+
}, panel.id))
|
|
230
|
+
}), caption]
|
|
194
231
|
}, node.id);
|
|
195
232
|
}
|
|
196
233
|
case "code": return /* @__PURE__ */ jsx("pre", {
|
|
@@ -403,7 +440,7 @@ function reportMeta(input) {
|
|
|
403
440
|
case "abstract": return `## ${node.title ?? "摘要"}\n\n${node.paragraphs.map((p) => block(p)).join("\n\n")}\n\n关键词:${node.keywords.join(";")}`;
|
|
404
441
|
case "paragraph": return inline(node.content);
|
|
405
442
|
case "equation": return `$$\n${node.latex}\n$$${report?.index[node.id]?.number ? `\n(${report.index[node.id].number})` : ""}`;
|
|
406
|
-
case "figure": return `\n\n${report?.index[node.id]?.label ?? ""} ${inline(node.caption)}`;
|
|
443
|
+
case "figure": return `${node.panels ? node.panels.map((panel) => `\n${report?.index[panel.id]?.label ?? panel.id}${panel.caption ? " " + inline(panel.caption) : ""}`).join("\n\n") : ``}\n\n${report?.index[node.id]?.label ?? ""} ${inline(node.caption)}`;
|
|
407
444
|
case "table": return `${node.caption ? `${report?.index[node.id]?.label ?? ""} ${inline(node.caption)}\n\n` : ""}${node.rows.map((row) => row.cells.map((cell) => cell.blocks.map((child) => block(child, depth)).join("\n")).join(" | ")).join("\n")}`;
|
|
408
445
|
case "list": return node.items.map((item, i) => `${node.ordered ? `${(node.start ?? 1) + i}.` : "-"} ${item.blocks.map((child) => block(child, depth)).join("\n")}`).join("\n");
|
|
409
446
|
case "code": return `\`\`\`${node.language}\n${node.text}\n\`\`\``;
|
|
@@ -51,13 +51,38 @@ interface ReportList {
|
|
|
51
51
|
blocks: (Paragraph | ReportList)[];
|
|
52
52
|
}[];
|
|
53
53
|
}
|
|
54
|
-
|
|
54
|
+
/** 多图里的一张:放在第 row 行第 column 列(从 1 起),图下显示「(label)」,caption 是写在编号后面的小标题;label 由人写,不自动编 */
|
|
55
|
+
interface FigurePanel {
|
|
56
|
+
id: string;
|
|
57
|
+
row: number;
|
|
58
|
+
column: number;
|
|
59
|
+
label: string;
|
|
60
|
+
src: string;
|
|
61
|
+
alt: string;
|
|
62
|
+
caption?: ReportInline[];
|
|
63
|
+
}
|
|
64
|
+
interface SingleImageFigure {
|
|
55
65
|
type: 'figure';
|
|
56
66
|
id: string;
|
|
57
67
|
src: string;
|
|
58
68
|
caption: ReportInline[];
|
|
59
69
|
alt: string;
|
|
70
|
+
rows?: never;
|
|
71
|
+
columns?: never;
|
|
72
|
+
panels?: never;
|
|
73
|
+
}
|
|
74
|
+
/** 一个图号下的多张图:rows 行 columns 列的网格,每张图写明自己的格子,没写到的格子留空;caption 是整个图的图题 */
|
|
75
|
+
interface MultiImageFigure {
|
|
76
|
+
type: 'figure';
|
|
77
|
+
id: string;
|
|
78
|
+
caption: ReportInline[];
|
|
79
|
+
rows: number;
|
|
80
|
+
columns: number;
|
|
81
|
+
panels: FigurePanel[];
|
|
82
|
+
src?: never;
|
|
83
|
+
alt?: never;
|
|
60
84
|
}
|
|
85
|
+
type Figure = SingleImageFigure | MultiImageFigure;
|
|
61
86
|
interface Equation {
|
|
62
87
|
type: 'equation';
|
|
63
88
|
id: string;
|
|
@@ -169,4 +194,4 @@ declare class ReportContentError extends Error {
|
|
|
169
194
|
constructor(message: string, file?: string, pointer?: string);
|
|
170
195
|
}
|
|
171
196
|
//#endregion
|
|
172
|
-
export {
|
|
197
|
+
export { SingleImageFigure as C, TableCell as E, Section as S, Table as T, ReportContentError as _, CellBlock as a, ReportInline as b, ContentWarning as c, FigurePanel as d, Include as f, ReportBlock as g, Paragraph as h, Bibliography as i, Equation as l, MultiImageFigure as m, AssemblyOptions as n, Code as o, IndexEntry as p, AssemblyResult as r, CompiledReport as s, Abstract as t, Figure as u, ReportDocument as v, SourceLocation as w, ReportList as x, ReportIndex as y };
|
|
@@ -61,10 +61,19 @@ class Composer:
|
|
|
61
61
|
if kind not in ('Table', 'Figure'):
|
|
62
62
|
raise ValueError(f'Unknown caption kind: {kind}')
|
|
63
63
|
name = {'Table': '表', 'Figure': '图'}[kind]
|
|
64
|
-
number = self.
|
|
64
|
+
number = self.chapter_number(r'STYLEREF 1 \s', 'SEQ ' + kind + r' \* ARABIC \s 1', bookmark)
|
|
65
65
|
return self.paragraph(f'{name} {number} {text}',
|
|
66
66
|
'Table Caption' if kind == 'Table' else -35)
|
|
67
67
|
|
|
68
|
+
def chapter_number(self, chapter_code, sequence_code, bookmark):
|
|
69
|
+
"""Return「章号-序号」 placeholders; ``bookmark`` spans both fields so REF shows the whole number."""
|
|
70
|
+
chapter = self.field_token(chapter_code)
|
|
71
|
+
number = self.field_token(sequence_code)
|
|
72
|
+
index = len(self._pending_numbers)
|
|
73
|
+
begin, end = f'@NUMSTART{index}@', f'@NUMEND{index}@'
|
|
74
|
+
self._pending_numbers.append((begin, end, bookmark))
|
|
75
|
+
return f'{begin}{chapter}-{number}{end}'
|
|
76
|
+
|
|
68
77
|
def list(self, items, ordered=False):
|
|
69
78
|
doc = self.doc
|
|
70
79
|
list_start = doc.Content.End - 1
|
|
@@ -100,12 +109,8 @@ class Composer:
|
|
|
100
109
|
boldSymbol=bold_symbol))
|
|
101
110
|
if inline:
|
|
102
111
|
return self.paragraph(before + token + after)
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
index = len(self._pending_numbers)
|
|
106
|
-
begin, end = f'@NUMSTART{index}@', f'@NUMEND{index}@'
|
|
107
|
-
self._pending_numbers.append((begin, end, bookmark))
|
|
108
|
-
return self.paragraph(f'\t{token}\t({begin}{chapter}-{number}{end})', 'Equation')
|
|
112
|
+
number = self.chapter_number(r'STYLEREF 1 \s', r'SEQ Equation \* ARABIC \s 1', bookmark)
|
|
113
|
+
return self.paragraph(f'\t{token}\t({number})', 'Equation')
|
|
109
114
|
|
|
110
115
|
def footnote(self, token, text):
|
|
111
116
|
rng = self.doc.Content.Duplicate
|
|
@@ -313,12 +318,26 @@ class ContentComposer(Composer):
|
|
|
313
318
|
setattr(run.Font, {'strong': 'Bold', 'em': 'Italic', 'sup': 'Superscript', 'sub': 'Subscript'}[mark], True)
|
|
314
319
|
return rng
|
|
315
320
|
|
|
321
|
+
def chapter_sequence(self, kind, bookmark):
|
|
322
|
+
"""「章号-序号」 for an equation, figure or table in the current top-level chapter."""
|
|
323
|
+
if self.chapter_is_appendix:
|
|
324
|
+
# The SEQ "\s 1" restart only fires on built-in Heading 1
|
|
325
|
+
# paragraphs; the template's Appendix Heading is Normal-based,
|
|
326
|
+
# so appendix sequences never restart there. Giving each
|
|
327
|
+
# appendix its own SEQ identifier keeps a natural 1, 2, 3 …
|
|
328
|
+
# count that renumbers on insert or delete like main chapters,
|
|
329
|
+
# without touching heading styles or their linked lists.
|
|
330
|
+
letter = chr(ord('A') + self.appendix_count - 1)
|
|
331
|
+
return self.chapter_number(r'STYLEREF "Appendix Heading" \s',
|
|
332
|
+
'SEQ ' + kind + 'Appendix' + letter + r' \* ARABIC', bookmark)
|
|
333
|
+
return self.chapter_number(r'STYLEREF 1 \s', 'SEQ ' + kind + r' \* ARABIC \s 1', bookmark)
|
|
334
|
+
|
|
316
335
|
def numbered_caption(self, node, kind):
|
|
317
336
|
from .content import bookmark
|
|
318
337
|
numbered = node.get('numbered', True)
|
|
319
338
|
prefix = ''
|
|
320
339
|
if numbered:
|
|
321
|
-
prefix = {'Figure': '图 ', 'Table': '表 '}[kind] + self.
|
|
340
|
+
prefix = {'Figure': '图 ', 'Table': '表 '}[kind] + self.chapter_sequence(kind, bookmark(node['id'], '_n')) + ' '
|
|
322
341
|
rng = self.rich_paragraph(node['caption'], 'Table Caption' if kind == 'Table' else -35, prefix=prefix)
|
|
323
342
|
title = self.part(rng, utf16_length(prefix), rng.End - rng.Start - utf16_length(prefix) - 1)
|
|
324
343
|
self.mark(node['id'], title, '_t')
|
|
@@ -416,24 +435,9 @@ class ContentComposer(Composer):
|
|
|
416
435
|
token = self.math_token(node, False)
|
|
417
436
|
suffix = ''
|
|
418
437
|
if node['numbered']:
|
|
419
|
-
chapter = self.field_token(r'STYLEREF 1 \s' if self.chapter_style == 'Heading 1' else r'STYLEREF "Appendix Heading" \s')
|
|
420
|
-
if self.chapter_is_appendix:
|
|
421
|
-
# The SEQ "\s 1" restart only fires on built-in Heading 1
|
|
422
|
-
# paragraphs; the template's Appendix Heading is Normal-based,
|
|
423
|
-
# so appendix sequences never restart there. Giving each
|
|
424
|
-
# appendix its own SEQ identifier keeps a natural 1, 2, 3 …
|
|
425
|
-
# count that renumbers on insert or delete like main chapters,
|
|
426
|
-
# without touching heading styles or their linked lists.
|
|
427
|
-
letter = chr(ord('A') + self.appendix_count - 1)
|
|
428
|
-
number = self.field_token(r'SEQ EquationAppendix' + letter + r' \* ARABIC')
|
|
429
|
-
else:
|
|
430
|
-
number = self.field_token(r'SEQ Equation \* ARABIC \s 1')
|
|
431
|
-
self.chapter_first_equation = False
|
|
432
|
-
i = len(self._pending_numbers)
|
|
433
|
-
begin, end = f'@NUMSTART{i}@', f'@NUMEND{i}@'
|
|
434
438
|
from .content import bookmark
|
|
435
|
-
self.
|
|
436
|
-
suffix =
|
|
439
|
+
self.chapter_first_equation = False
|
|
440
|
+
suffix = '\t(' + self.chapter_sequence('Equation', bookmark(node['id'], '_n')) + ')'
|
|
437
441
|
rng = self.paragraph('\t' + token + suffix, 'Equation')
|
|
438
442
|
rng.ParagraphFormat.TabStops.ClearAll()
|
|
439
443
|
rng.ParagraphFormat.TabStops.Add(self.width / 2, 1, 0)
|
|
@@ -166,7 +166,10 @@ def validate_content(content, directory):
|
|
|
166
166
|
blocks(item["children"], depth + 1, scope, mode, tables)
|
|
167
167
|
elif kind == "equation" and item["numbered"]:
|
|
168
168
|
require(chapter is not None, f"{item['id']}: numbered equation outside a chapter")
|
|
169
|
+
elif kind == "figure":
|
|
170
|
+
require(chapter is not None, f"{item['id']}: numbered figure outside a chapter")
|
|
169
171
|
elif kind == "table":
|
|
172
|
+
require(chapter is not None or not item["numbered"], f"{item['id']}: numbered table outside a chapter")
|
|
170
173
|
require(tables < 6, f"{item['id']}: Word renderer supports at most six table levels")
|
|
171
174
|
for row in item["rows"]:
|
|
172
175
|
for cell in row["cells"]:
|
package/package.json
CHANGED
package/styles/report.css
CHANGED
|
@@ -6,6 +6,12 @@
|
|
|
6
6
|
.report-document figure { margin: 1.5rem 0; text-align: center; }
|
|
7
7
|
.report-document img { margin: 0 auto; max-width: 100%; height: auto; }
|
|
8
8
|
.report-document figcaption, .report-document caption { color: var(--color-fd-muted-foreground); font-size: .9em; }
|
|
9
|
+
/* 多图:行列由报告写定(网格与每张的格子写在元素的 style 上),各列等宽、没写到的格子留空,任何宽度都不重排;同一行里图片上下居中、(a)(b) 对齐在同一条线上 */
|
|
10
|
+
.report-panels { display: grid; gap: .75rem 1rem; }
|
|
11
|
+
.report-panel { min-width: 0; display: flex; flex-direction: column; }
|
|
12
|
+
.report-panel-image { flex: 1; display: flex; align-items: center; justify-content: center; }
|
|
13
|
+
.report-panel-caption { margin-top: .25rem; font-size: .9em; color: var(--color-fd-muted-foreground); }
|
|
14
|
+
.report-multi-figure > figcaption { margin-top: .5rem; }
|
|
9
15
|
.report-document .report-equation { display: flex; align-items: center; gap: 1rem; margin: 1.25rem 0; }
|
|
10
16
|
.report-math-block { display: block; flex: 1; min-width: 0; overflow-x: auto; overflow-y: hidden; padding: .5em 0; }
|
|
11
17
|
.report-equation-number { flex-shrink: 0; }
|