structural-codes-viewer 0.1.0-alpha.1 → 0.1.0-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/LICENSE +501 -0
  2. package/NOTICE +16 -0
  3. package/README.md +57 -68
  4. package/package-dist/CorpusContent.d.ts +44 -9
  5. package/package-dist/CorpusContent.js +452 -26
  6. package/package-dist/CorpusContent.js.map +1 -1
  7. package/package-dist/NormativeViewer.d.ts +23 -2
  8. package/package-dist/NormativeViewer.js +1699 -234
  9. package/package-dist/NormativeViewer.js.map +1 -1
  10. package/package-dist/ReferenceTools.d.ts +49 -0
  11. package/package-dist/ReferenceTools.js +14 -0
  12. package/package-dist/ReferenceTools.js.map +1 -0
  13. package/package-dist/annotations/AnnotationUi.d.ts +57 -0
  14. package/package-dist/annotations/AnnotationUi.js +114 -0
  15. package/package-dist/annotations/AnnotationUi.js.map +1 -0
  16. package/package-dist/annotations/annotationTargets.d.ts +39 -0
  17. package/package-dist/annotations/annotationTargets.js +94 -0
  18. package/package-dist/annotations/annotationTargets.js.map +1 -0
  19. package/package-dist/annotations/exportImport.d.ts +6 -0
  20. package/package-dist/annotations/exportImport.js +30 -0
  21. package/package-dist/annotations/exportImport.js.map +1 -0
  22. package/package-dist/annotations/index.d.ts +4 -0
  23. package/package-dist/annotations/index.js +5 -0
  24. package/package-dist/annotations/index.js.map +1 -0
  25. package/package-dist/annotations/indexedDb.d.ts +21 -0
  26. package/package-dist/annotations/indexedDb.js +210 -0
  27. package/package-dist/annotations/indexedDb.js.map +1 -0
  28. package/package-dist/annotations/schema.d.ts +8 -0
  29. package/package-dist/annotations/schema.js +102 -0
  30. package/package-dist/annotations/schema.js.map +1 -0
  31. package/package-dist/annotations/types.d.ts +52 -0
  32. package/package-dist/annotations/types.js +18 -0
  33. package/package-dist/annotations/types.js.map +1 -0
  34. package/package-dist/chunkNavigation.d.ts +12 -0
  35. package/package-dist/chunkNavigation.js +44 -0
  36. package/package-dist/chunkNavigation.js.map +1 -0
  37. package/package-dist/corpusData.d.ts +176 -33
  38. package/package-dist/corpusData.js +52 -0
  39. package/package-dist/corpusData.js.map +1 -1
  40. package/package-dist/crossReferences.d.ts +46 -0
  41. package/package-dist/crossReferences.js +198 -0
  42. package/package-dist/crossReferences.js.map +1 -0
  43. package/package-dist/generate-artifacts.mjs +47 -12
  44. package/package-dist/globalScrubber.d.ts +10 -0
  45. package/package-dist/globalScrubber.js +85 -0
  46. package/package-dist/globalScrubber.js.map +1 -0
  47. package/package-dist/permalinks.d.ts +22 -0
  48. package/package-dist/permalinks.js +36 -0
  49. package/package-dist/permalinks.js.map +1 -0
  50. package/package-dist/searchClient.d.ts +22 -0
  51. package/package-dist/searchClient.js +133 -0
  52. package/package-dist/searchClient.js.map +1 -0
  53. package/package-dist/searchEngine.d.ts +68 -0
  54. package/package-dist/searchEngine.js +558 -0
  55. package/package-dist/searchEngine.js.map +1 -0
  56. package/package-dist/searchWorker.d.ts +1 -0
  57. package/package-dist/searchWorker.js +32 -0
  58. package/package-dist/searchWorker.js.map +1 -0
  59. package/package-dist/styles.css +762 -182
  60. package/package-dist/tableCaptions.d.mts +3 -0
  61. package/package-dist/tableCaptions.mjs +35 -0
  62. package/package-dist/tableCaptions.mjs.map +1 -0
  63. package/package.json +18 -14
@@ -0,0 +1,198 @@
1
+ const unitPattern = /(^|[^\p{L}\p{N}])(§\s*C?\.?\d+(?:\.\d+)+|C\.?\d+(?:\.\d+)+|\d+(?:\.\d+){2,})/giu;
2
+ const assetPatterns = [
3
+ { kind: "table", pattern: /\b(?:Tab\.\s*|Tab(?:ella|elle)?\s+)((?:C(?:\.)?)?\d+(?:\.\d+)*(?:\.[IVXLCDM]+[a-z]?(?:\.[a-z]+)?)?)/giu },
4
+ { kind: "table", pattern: /\b((?:C(?:\.)?)?\d+(?:\.\d+)*\.[IVXLCDM]+[a-z]?(?:\.[a-z]+)?)/giu },
5
+ { kind: "figure", pattern: /\b(?:Fig\.\s*|Fig\s+|Figura\s+)((?:C(?:\.)?)?\d+(?:\.\d+)+)/giu },
6
+ { kind: "formula", pattern: /\b(?:formula|equazione|relazione)\s+(?:n\.?\s*)?\[?((?:C(?:\.)?)?\d+(?:\.\d+)+)\]?/giu },
7
+ { kind: "formula", pattern: /\[((?:C(?:\.)?)?\d+(?:\.\d+)+)\]/giu },
8
+ ];
9
+ function normalizedNumber(value) {
10
+ return value.replace(/^C\.?/iu, "").replace(/[.\s]+$/gu, "").replace(/(^|\.)([ivxlcdm]+)(?=\.|$)/gu, (match, prefix, part) => prefix + (part.length > 1 || part === part.toUpperCase() ? part.toUpperCase() : part));
11
+ }
12
+ function documentHint(value) {
13
+ return /^C/iu.test(value.trim().replace(/^§\s*/iu, "")) ? "circ2019" : null;
14
+ }
15
+ function overlaps(left, right) {
16
+ return left.start < right.end && right.start < left.end;
17
+ }
18
+ export function findCrossReferences(value) {
19
+ const source = String(value !== null && value !== void 0 ? value : "");
20
+ const candidates = [];
21
+ const combinedTablePattern = /\b(?:Tab\.\s*|Tab(?:ella|elle)?\s+)?((?:C(?:\.)?)?\d+(?:\.\d+)*\.[IVXLCDM]+)([a-z])-\s*([a-z])\b/giu;
22
+ for (const match of source.matchAll(combinedTablePattern)) {
23
+ const suffix = match[3];
24
+ const end = match.index + match[0].length;
25
+ const start = end - suffix.length;
26
+ candidates.push({
27
+ start,
28
+ end,
29
+ text: suffix,
30
+ kind: "table",
31
+ number: `${match[1]}${suffix}`,
32
+ documentHint: documentHint(match[1]),
33
+ });
34
+ }
35
+ for (const { kind, pattern } of assetPatterns) {
36
+ pattern.lastIndex = 0;
37
+ for (const match of source.matchAll(pattern)) {
38
+ candidates.push({
39
+ start: match.index,
40
+ end: match.index + match[0].length,
41
+ text: match[0],
42
+ kind,
43
+ number: normalizedNumber(match[1]),
44
+ documentHint: documentHint(match[1]),
45
+ });
46
+ }
47
+ }
48
+ unitPattern.lastIndex = 0;
49
+ for (const match of source.matchAll(unitPattern)) {
50
+ const prefixLength = match[1].length;
51
+ const raw = match[2];
52
+ candidates.push({
53
+ start: match.index + prefixLength,
54
+ end: match.index + prefixLength + raw.length,
55
+ text: raw,
56
+ kind: "unit",
57
+ number: normalizedNumber(raw.replace(/^§\s*/iu, "")),
58
+ documentHint: documentHint(raw),
59
+ });
60
+ }
61
+ const accepted = [];
62
+ for (const candidate of candidates.sort((left, right) => left.start - right.start || (left.kind === "unit" ? 1 : -1) || right.end - left.end)) {
63
+ if (!accepted.some((current) => overlaps(current, candidate)))
64
+ accepted.push(candidate);
65
+ }
66
+ return accepted.sort((left, right) => left.start - right.start);
67
+ }
68
+ function compactText(unit, maximum = 110) {
69
+ const value = unit.blocks
70
+ .filter((block) => block.blockId !== unit.titleBlockId)
71
+ .flatMap((block) => { var _a; return ((_a = block.text) === null || _a === void 0 ? void 0 : _a.normalized) ? [block.text.normalized] : []; })
72
+ .join(" ")
73
+ .replace(/\s+/gu, " ")
74
+ .trim();
75
+ return value.length > maximum ? `${value.slice(0, maximum - 1).trimEnd()}…` : value;
76
+ }
77
+ function assetKindFor(collection) {
78
+ return collection === "formulas" ? "formula" : collection === "tables" ? "table" : "figure";
79
+ }
80
+ function preferredDocument(reference, sourceDocument) {
81
+ if (reference.documentHint)
82
+ return reference.documentHint;
83
+ return sourceDocument === "circ2019" ? "ntc2018" : sourceDocument;
84
+ }
85
+ function targetKey(targetType, targetId) {
86
+ return `${targetType}:${targetId}`;
87
+ }
88
+ export function buildCrossReferenceIndexPayload({ units, assetCollections }) {
89
+ var _a;
90
+ const unitTargets = units.map((unit) => ({
91
+ id: unit.id,
92
+ document: unit.document,
93
+ numbering: unit.numbering.official,
94
+ title: unit.title,
95
+ snippet: compactText(unit),
96
+ }));
97
+ const unitById = new Map(unitTargets.map((unit) => [unit.id, unit]));
98
+ const unitByDocumentAndNumber = new Map(unitTargets.map((unit) => [`${unit.document}:${normalizedNumber(unit.numbering)}`, unit]));
99
+ const ownerByAssetId = new Map();
100
+ for (const unit of units) {
101
+ for (const block of unit.blocks) {
102
+ if (block.assetId && !ownerByAssetId.has(block.assetId))
103
+ ownerByAssetId.set(block.assetId, { unit, blockId: block.blockId });
104
+ }
105
+ }
106
+ const assets = [];
107
+ for (const [collection, records] of Object.entries(assetCollections)) {
108
+ const kind = assetKindFor(collection);
109
+ for (const asset of records) {
110
+ if (!asset.officialNumber)
111
+ continue;
112
+ const owner = ownerByAssetId.get(asset.id);
113
+ if (!owner)
114
+ continue;
115
+ assets.push(Object.assign(Object.assign({ id: asset.id, kind, unitId: owner.unit.id, blockId: owner.blockId, officialNumber: asset.officialNumber }, (kind === "table" && asset.caption ? { title: asset.caption, snippet: asset.caption } : {})), (kind === "figure" ? { title: asset.caption, snippet: asset.alt } : {})));
116
+ }
117
+ }
118
+ const assetByDocumentKindAndNumber = new Map(assets.map((asset) => {
119
+ const owner = unitById.get(asset.unitId);
120
+ return [`${owner.document}:${asset.kind}:${normalizedNumber(asset.officialNumber)}`, asset];
121
+ }));
122
+ const backlinks = [];
123
+ const seen = new Set();
124
+ const referencedAssetIds = new Set();
125
+ for (const source of units) {
126
+ for (const block of source.blocks) {
127
+ if (!((_a = block.text) === null || _a === void 0 ? void 0 : _a.normalized) || block.blockId === source.titleBlockId)
128
+ continue;
129
+ for (const reference of findCrossReferences(block.text.normalized)) {
130
+ const preferred = preferredDocument(reference, source.document);
131
+ const documents = preferred === source.document ? [preferred] : [preferred, source.document];
132
+ let target = null;
133
+ let targetType = reference.kind;
134
+ for (const document of documents) {
135
+ target = reference.kind === "unit"
136
+ ? unitByDocumentAndNumber.get(`${document}:${reference.number}`)
137
+ : assetByDocumentKindAndNumber.get(`${document}:${reference.kind}:${reference.number}`);
138
+ if (target)
139
+ break;
140
+ }
141
+ if (!target || (targetType === "unit" && target.id === source.id))
142
+ continue;
143
+ if (targetType !== "unit") {
144
+ referencedAssetIds.add(target.id);
145
+ continue;
146
+ }
147
+ const key = `${targetKey(targetType, target.id)}:${source.id}:${block.blockId}`;
148
+ if (seen.has(key))
149
+ continue;
150
+ seen.add(key);
151
+ backlinks.push({
152
+ targetType,
153
+ targetId: target.id,
154
+ sourceUnitId: source.id,
155
+ sourceBlockId: block.blockId,
156
+ });
157
+ }
158
+ }
159
+ }
160
+ // Exact canonical resolution needs every numbered asset, even without a
161
+ // backlink. Keep descriptive preview fields only for assets referenced by
162
+ // corpus prose so the lazy viewer index remains compact.
163
+ const indexedAssets = assets.map((asset) => referencedAssetIds.has(asset.id) ? asset : ({
164
+ id: asset.id, kind: asset.kind, unitId: asset.unitId, blockId: asset.blockId, officialNumber: asset.officialNumber,
165
+ }));
166
+ return { formatVersion: 1, units: unitTargets, assets: indexedAssets, backlinks };
167
+ }
168
+ export function createCrossReferenceLookup(index) {
169
+ var _a;
170
+ const unitById = new Map(index.units.map((unit) => [unit.id, unit]));
171
+ const unitByDocumentAndNumber = new Map(index.units.map((unit) => [`${unit.document}:${normalizedNumber(unit.numbering)}`, unit]));
172
+ const assetById = new Map(index.assets.map((asset) => [asset.id, asset]));
173
+ const assetByDocumentKindAndNumber = new Map(index.assets.map((asset) => {
174
+ const unit = unitById.get(asset.unitId);
175
+ return [`${unit.document}:${asset.kind}:${normalizedNumber(asset.officialNumber)}`, asset];
176
+ }));
177
+ const backlinksByTarget = new Map();
178
+ for (const backlink of index.backlinks) {
179
+ const key = targetKey(backlink.targetType, backlink.targetId);
180
+ const group = (_a = backlinksByTarget.get(key)) !== null && _a !== void 0 ? _a : [];
181
+ group.push(backlink);
182
+ backlinksByTarget.set(key, group);
183
+ }
184
+ return { unitById, unitByDocumentAndNumber, assetById, assetByDocumentKindAndNumber, backlinksByTarget };
185
+ }
186
+ export function resolveCrossReference(lookup, reference, sourceDocument) {
187
+ const preferred = preferredDocument(reference, sourceDocument);
188
+ const documents = preferred === sourceDocument ? [preferred] : [preferred, sourceDocument];
189
+ for (const document of documents) {
190
+ const target = reference.kind === "unit"
191
+ ? lookup.unitByDocumentAndNumber.get(`${document}:${reference.number}`)
192
+ : lookup.assetByDocumentKindAndNumber.get(`${document}:${reference.kind}:${reference.number}`);
193
+ if (target)
194
+ return reference.kind === "unit" ? { targetType: "unit", unit: target } : { targetType: reference.kind, unit: lookup.unitById.get(target.unitId), asset: target };
195
+ }
196
+ return null;
197
+ }
198
+ //# sourceMappingURL=crossReferences.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crossReferences.js","sourceRoot":"","sources":["../shared/crossReferences.js"],"names":[],"mappings":"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"}
@@ -3,6 +3,8 @@ import { createHash } from "node:crypto";
3
3
  import { copyFile, mkdir, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
4
4
  import { dirname, join, relative, resolve } from "node:path";
5
5
  import { fileURLToPath, pathToFileURL } from "node:url";
6
+ import { buildSearchIndexPayload } from "./searchEngine.js";
7
+ import { buildCrossReferenceIndexPayload } from "./crossReferences.js";
6
8
 
7
9
  const documentOrder = ["ntc2018", "circ2019"];
8
10
  const maxInitialChunkBytes = 1_500_000;
@@ -61,8 +63,39 @@ function assignChunkKeys(units) {
61
63
  }
62
64
  function publicUnit(document, unit) { return { ...unit, document }; }
63
65
  function unitSummary(document, unit, chunkPath) {
64
- const openIssues = unit.workflow.openIssues ?? [];
65
- return { id: unit.id, document, kind: unit.kind, numbering: unit.numbering, title: unit.title, hierarchy: unit.hierarchy, validity: unit.validity, workflowStatus: unit.workflow.status, openIssueCount: openIssues.length, blockingIssueCount: openIssues.filter(({ severity }) => severity === "blocking").length, chunkPath };
66
+ return { id: unit.id, document, kind: unit.kind, numbering: unit.numbering, title: unit.title, hierarchy: unit.hierarchy, validity: unit.validity, reviewStatus: unit.review.status, chunkPath };
67
+ }
68
+
69
+ function compactSearchText(parts) {
70
+ const seen = new Set();
71
+ return parts.flatMap((part) => {
72
+ const text = String(part ?? "").replace(/\s+/gu, " ").trim();
73
+ const key = text.normalize("NFKC").toLocaleLowerCase("it");
74
+ if (!text || seen.has(key)) return [];
75
+ seen.add(key);
76
+ return [text];
77
+ }).join(" ");
78
+ }
79
+
80
+ function searchableAssetText(kind, asset) {
81
+ const label = kind === "formula" ? "Formula" : kind === "table" ? "Tabella" : "Figura";
82
+ const identity = asset.officialNumber ? `${label} ${asset.officialNumber}` : label;
83
+ if (kind === "formula") return identity;
84
+ if (kind === "figure") return compactSearchText([identity, asset.caption, asset.alt]);
85
+ const cells = [...asset.headers, ...asset.rows, ...(asset.footerRows ?? [])].flat().map((cell) => cell.text);
86
+ return compactSearchText([identity, asset.caption, ...cells, ...asset.notes]);
87
+ }
88
+
89
+ function searchSegments(unit, assetById) {
90
+ return unit.blocks.flatMap((block) => {
91
+ const segments = [];
92
+ if (block.text?.normalized && block.blockId !== unit.titleBlockId) segments.push({ blockId: block.blockId, text: block.text.normalized });
93
+ if (block.assetId) {
94
+ const found = assetById.get(block.assetId);
95
+ if (found) segments.push({ blockId: block.blockId, assetId: block.assetId, assetKind: found.kind, officialNumber: found.asset.officialNumber, text: searchableAssetText(found.kind, found.asset) });
96
+ }
97
+ return segments;
98
+ });
66
99
  }
67
100
 
68
101
  export async function generateArtifacts({ sourcePackage = "structural-codes", outputDirectory = join(process.cwd(), "public", "data", "codes"), assetOutputDirectory = join(process.cwd(), "public", "assets") } = {}) {
@@ -74,16 +107,16 @@ export async function generateArtifacts({ sourcePackage = "structural-codes", ou
74
107
  const assetManifestDirectory = join(sourceRoot, "corpus", "assets");
75
108
  const sourceRegistryFile = join(sourceRoot, "sources", "registry", "sources.v2.json");
76
109
  const packageFile = join(sourceRoot, "package.json");
77
- const legacyOutput = join(dirname(outputDirectory), "corpus.json");
78
110
  const figureOutput = assetOutputDirectory;
79
111
  const [corpusManifest, sourceRegistry, packageManifest] = await Promise.all([json(corpusManifestFile), json(sourceRegistryFile), json(packageFile)]);
80
112
  const assetManifestFiles = (await readdir(assetManifestDirectory, { recursive: true })).filter((file) => file.endsWith(".json")).sort((left, right) => left.localeCompare(right, "it", { numeric: true }));
81
113
  const assetManifests = await Promise.all(assetManifestFiles.map((file) => json(join(assetManifestDirectory, file))));
82
114
  const assetCollections = { formulas: assetManifests.flatMap(({ formulas }) => formulas), tables: assetManifests.flatMap(({ tables }) => tables), figures: assetManifests.flatMap(({ figures }) => figures) };
83
115
  const assetsById = Object.fromEntries(Object.entries(assetCollections).map(([kind, assets]) => [kind, new Map(assets.map((asset) => [asset.id, asset]))]));
116
+ const searchableAssetById = new Map(Object.entries(assetCollections).flatMap(([collection, assets]) => assets.map((asset) => [asset.id, { kind: collection === "formulas" ? "formula" : collection === "tables" ? "table" : "figure", asset }])));
84
117
  const unitsByDocument = Object.fromEntries(await Promise.all(documentOrder.map(async (document) => [document, await loadUnits(corpusRoot, document)])));
85
118
  const allUnits = documentOrder.flatMap((document) => unitsByDocument[document].map((unit) => publicUnit(document, unit)));
86
- await Promise.all([rm(outputDirectory, { recursive: true, force: true }), rm(legacyOutput, { force: true }), rm(figureOutput, { recursive: true, force: true })]);
119
+ await Promise.all([rm(outputDirectory, { recursive: true, force: true }), rm(figureOutput, { recursive: true, force: true })]);
87
120
  const chunkPathByUnit = new Map();
88
121
  const documentIndexes = {};
89
122
  const chunkInventory = [];
@@ -119,8 +152,9 @@ export async function generateArtifacts({ sourcePackage = "structural-codes", ou
119
152
  const relationsWritten = await writeJson(join(outputDirectory, "relations.json"), { formatVersion: 2, sourceOfTruth: "explicit-corpus-relations", inferredRelationsIncluded: false, relations });
120
153
  const ntcByNumber = new Map(unitsByDocument.ntc2018.map((unit) => [unit.numbering.official, unit]));
121
154
  const diagnostics = unitsByDocument.circ2019.flatMap((unit) => { const candidate = ntcByNumber.get(unit.numbering.official.replace(/^C/iu, "")); if (!candidate || unit.relations.some((relation) => relation.targetUnitId === candidate.id)) return []; return [{ kind: "suggested-relation", status: "diagnostic-only-not-canonical", reason: "same-numbering", sourceUnitId: unit.id, targetUnitId: candidate.id, sourceChunkPath: chunkPathByUnit.get(unit.id), targetChunkPath: chunkPathByUnit.get(candidate.id) }]; });
122
- const diagnosticsWritten = await writeJson(join(outputDirectory, "relation-diagnostics.json"), { formatVersion: 2, warning: "Suggerimenti diagnostici non revisionati: non sono usati dalla vista combinata.", suggestions: diagnostics });
123
- const searchWritten = await writeJson(join(outputDirectory, "search-index.json"), { formatVersion: 2, normalization: "NFKC lowercase it-IT at query time", units: allUnits.map((unit) => ({ id: unit.id, document: unit.document, numbering: unit.numbering.official, title: unit.title, chunkPath: chunkPathByUnit.get(unit.id), text: unit.blocks.flatMap((block) => block.text?.normalized ? [block.text.normalized] : []).join(" ") })) });
155
+ const diagnosticsWritten = await writeJson(join(outputDirectory, "relation-diagnostics.json"), { formatVersion: 2, warning: "Suggerimenti diagnostici non confermati: non sono usati dalla vista combinata.", suggestions: diagnostics });
156
+ const searchWritten = await writeJson(join(outputDirectory, "search-index.json"), buildSearchIndexPayload(allUnits.map((unit) => ({ id: unit.id, document: unit.document, numbering: unit.numbering.official, title: unit.title, titleBlockId: unit.titleBlockId, chunkPath: chunkPathByUnit.get(unit.id), segments: searchSegments(unit, searchableAssetById) }))));
157
+ const crossReferenceWritten = await writeJson(join(outputDirectory, "cross-reference-index.json"), buildCrossReferenceIndexPayload({ units: allUnits, assetCollections }));
124
158
  const manifestationBySourceId = new Map(sourceRegistry.works.flatMap((work) => work.manifestations.map((manifestation) => [manifestation.sourceId, { manifestation, work }])));
125
159
  for (const document of documentOrder) {
126
160
  const manifestDocument = corpusManifest.documents[document];
@@ -128,15 +162,16 @@ export async function generateArtifacts({ sourcePackage = "structural-codes", ou
128
162
  if (!source) throw new Error(`sourceId non risolto: ${manifestDocument.sourceId}`);
129
163
  Object.assign(documentIndexes[document], { sourceId: manifestDocument.sourceId, sourceUrl: source.manifestation.officialUrl, publicationUrl: source.work.publication.eliUri, localSourcePath: source.manifestation.localFile, sourceSha256: source.manifestation.sha256, pages: manifestDocument.pages, blocks: unitsByDocument[document].reduce((total, unit) => total + unit.blocks.filter((block) => block.text).length, 0) });
130
164
  }
131
- const reviewedStatuses = new Set(["source-checked", "double-reviewed", "published", "superseded"]);
132
165
  const corpusFingerprintSha256 = sha256(JSON.stringify({ corpusManifest, unitsByDocument, assetManifests }));
133
- const artifactFingerprintSha256 = sha256(JSON.stringify({ chunkInventory, indexes: documentIndexes, relations: relationsWritten.sha256, diagnostics: diagnosticsWritten.sha256, search: searchWritten.sha256 }));
134
- const manifest = { formatVersion: 2, generatedAt: corpusManifest.asOf, structuralCodesVersion: packageManifest.version, schemaVersion: allUnits[0]?.schemaVersion ?? null, assetSchemaVersion: assetManifests[0]?.schemaVersion ?? null, status: corpusManifest.status, disclaimer: corpusManifest.disclaimer, corpusFingerprintSha256, generatedArtifactFingerprintSha256: artifactFingerprintSha256, stats: { units: allUnits.length, blocks: allUnits.reduce((total, unit) => total + unit.blocks.filter((block) => block.text).length, 0), explicitRelations: relations.length, proposedRelations: relations.filter((relation) => relation.review.status === "proposed").length, suggestedRelationDiagnostics: diagnostics.length, reviewedUnits: allUnits.filter((unit) => reviewedStatuses.has(unit.workflow.status)).length, assetUnits: allUnits.filter((unit) => unit.assets.formulaIds.length > 0 || unit.assets.tableIds.length > 0 || unit.assets.figureIds.length > 0).length, formulas: assetCollections.formulas.length, tables: assetCollections.tables.length, figures: assetCollections.figures.length, chunks: chunkInventory.length }, documents: documentIndexes, relationsPath: "/data/codes/relations.json", relationDiagnosticsPath: "/data/codes/relation-diagnostics.json", searchIndexPath: "/data/codes/search-index.json", chunks: chunkInventory };
166
+ const artifactFingerprintSha256 = sha256(JSON.stringify({ chunkInventory, indexes: documentIndexes, relations: relationsWritten.sha256, diagnostics: diagnosticsWritten.sha256, search: searchWritten.sha256, crossReferences: crossReferenceWritten.sha256 }));
167
+ const manifest = { formatVersion: 2, generatedAt: corpusManifest.asOf, structuralCodesVersion: packageManifest.version, schemaVersion: allUnits[0]?.schemaVersion ?? null, assetSchemaVersion: assetManifests[0]?.schemaVersion ?? null, status: corpusManifest.status, disclaimer: corpusManifest.disclaimer, corpusFingerprintSha256, generatedArtifactFingerprintSha256: artifactFingerprintSha256, stats: { units: allUnits.length, blocks: allUnits.reduce((total, unit) => total + unit.blocks.filter((block) => block.text).length, 0), explicitRelations: relations.length, proposedRelations: relations.filter((relation) => relation.review.status === "proposed").length, suggestedRelationDiagnostics: diagnostics.length, assetUnits: allUnits.filter((unit) => unit.assets.formulaIds.length > 0 || unit.assets.tableIds.length > 0 || unit.assets.figureIds.length > 0).length, formulas: assetCollections.formulas.length, tables: assetCollections.tables.length, figures: assetCollections.figures.length, chunks: chunkInventory.length }, documents: documentIndexes, relationsPath: "/data/codes/relations.json", relationDiagnosticsPath: "/data/codes/relation-diagnostics.json", searchIndexPath: "/data/codes/search-index.json", crossReferenceIndexPath: "/data/codes/cross-reference-index.json", chunks: chunkInventory };
135
168
  const manifestWritten = await writeJson(join(outputDirectory, "manifest.json"), manifest);
136
169
  let copiedFigureBytes = 0;
137
- for (const figure of assetCollections.figures) {
138
- const source = join(sourceRoot, "corpus", "assets", figure.imagePath);
139
- const destination = join(figureOutput, figure.imagePath);
170
+ const tableCellImages = assetCollections.tables.flatMap((table) => [...table.headers, ...table.rows].flat().flatMap((cell) => cell.image ? [cell.image] : []));
171
+ const imagesByPath = new Map([...assetCollections.figures, ...tableCellImages].map((entry) => [entry.imagePath, entry]));
172
+ for (const image of imagesByPath.values()) {
173
+ const source = join(sourceRoot, "corpus", "assets", image.imagePath);
174
+ const destination = join(figureOutput, image.imagePath);
140
175
  await mkdir(dirname(destination), { recursive: true });
141
176
  await copyFile(source, destination);
142
177
  copiedFigureBytes += (await stat(source)).size;
@@ -0,0 +1,10 @@
1
+ export function buildGlobalScrubberEntries(entries: any, maximumLevel?: number): any;
2
+ export function globalScrubberEntryAtRatio(entries: any, ratio: any): any;
3
+ export function globalScrubberRatioForId(entries: any, id: any, fallbackRatio?: number): any;
4
+ export function resolveGlobalScrubberActiveId(entries: any, activeId: any, activeBaseNumber: any): any;
5
+ export function globalScrubberKeyboardIndex(currentIndex: any, key: any, entryCount: any): number;
6
+ export function createGlobalScrubberDragSession(entries: any, onCommit: any): {
7
+ preview(ratio: any): any;
8
+ commit(): any;
9
+ cancel(): void;
10
+ };
@@ -0,0 +1,85 @@
1
+ function clampRatio(value) {
2
+ if (!Number.isFinite(value))
3
+ return 0;
4
+ return Math.max(0, Math.min(1, value));
5
+ }
6
+ export function buildGlobalScrubberEntries(entries, maximumLevel = Number.POSITIVE_INFINITY) {
7
+ const visibleEntries = Number.isFinite(maximumLevel) ? entries.filter((entry) => entry.level <= maximumLevel) : entries;
8
+ const lastIndex = Math.max(0, visibleEntries.length - 1);
9
+ return visibleEntries.map((entry, index) => (Object.assign(Object.assign({}, entry), { index, ratio: lastIndex === 0 ? 0 : index / lastIndex })));
10
+ }
11
+ export function globalScrubberEntryAtRatio(entries, ratio) {
12
+ var _a;
13
+ if (entries.length === 0)
14
+ return null;
15
+ const index = Math.round(clampRatio(ratio) * (entries.length - 1));
16
+ return (_a = entries[index]) !== null && _a !== void 0 ? _a : null;
17
+ }
18
+ export function globalScrubberRatioForId(entries, id, fallbackRatio = 0) {
19
+ var _a;
20
+ if (entries.length < 2)
21
+ return 0;
22
+ const entry = entries.find((candidate) => candidate.id === id);
23
+ return (_a = entry === null || entry === void 0 ? void 0 : entry.ratio) !== null && _a !== void 0 ? _a : clampRatio(fallbackRatio);
24
+ }
25
+ export function resolveGlobalScrubberActiveId(entries, activeId, activeBaseNumber) {
26
+ var _a;
27
+ if (activeId && entries.some((entry) => entry.id === activeId))
28
+ return activeId;
29
+ if (!activeBaseNumber)
30
+ return null;
31
+ const exactEntry = entries.find((entry) => entry.baseNumber === activeBaseNumber);
32
+ if (exactEntry)
33
+ return exactEntry.id;
34
+ let closestAncestor = null;
35
+ for (const entry of entries) {
36
+ if (!activeBaseNumber.startsWith(`${entry.baseNumber}.`))
37
+ continue;
38
+ if (!closestAncestor || entry.baseNumber.length > closestAncestor.baseNumber.length)
39
+ closestAncestor = entry;
40
+ }
41
+ return (_a = closestAncestor === null || closestAncestor === void 0 ? void 0 : closestAncestor.id) !== null && _a !== void 0 ? _a : null;
42
+ }
43
+ export function globalScrubberKeyboardIndex(currentIndex, key, entryCount) {
44
+ if (entryCount <= 0)
45
+ return -1;
46
+ const lastIndex = entryCount - 1;
47
+ const safeIndex = Math.max(0, Math.min(lastIndex, currentIndex));
48
+ const pageStep = Math.max(1, Math.round(entryCount * 0.1));
49
+ if (key === "Home")
50
+ return 0;
51
+ if (key === "End")
52
+ return lastIndex;
53
+ if (key === "ArrowUp")
54
+ return Math.max(0, safeIndex - 1);
55
+ if (key === "ArrowDown")
56
+ return Math.min(lastIndex, safeIndex + 1);
57
+ if (key === "PageUp")
58
+ return Math.max(0, safeIndex - pageStep);
59
+ if (key === "PageDown")
60
+ return Math.min(lastIndex, safeIndex + pageStep);
61
+ return safeIndex;
62
+ }
63
+ export function createGlobalScrubberDragSession(entries, onCommit) {
64
+ let current = null;
65
+ let finished = false;
66
+ return {
67
+ preview(ratio) {
68
+ if (!finished)
69
+ current = globalScrubberEntryAtRatio(entries, ratio);
70
+ return current;
71
+ },
72
+ commit() {
73
+ if (finished)
74
+ return null;
75
+ finished = true;
76
+ if (current)
77
+ onCommit(current);
78
+ return current;
79
+ },
80
+ cancel() {
81
+ finished = true;
82
+ },
83
+ };
84
+ }
85
+ //# sourceMappingURL=globalScrubber.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"globalScrubber.js","sourceRoot":"","sources":["../shared/globalScrubber.js"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { ViewerMode } from "./corpusData";
2
+ export type ViewerTarget = {
3
+ kind: "unit";
4
+ unitId: string;
5
+ } | {
6
+ kind: "block";
7
+ unitId: string;
8
+ blockId: string;
9
+ } | {
10
+ kind: "asset";
11
+ unitId: string;
12
+ assetId: string;
13
+ assetKind?: "formula" | "table" | "figure";
14
+ };
15
+ export declare function targetFromUrl(url: URL): ViewerTarget | null;
16
+ export declare function urlForViewerTarget(currentUrl: string | URL, mode: ViewerMode, defaultMode: ViewerMode, target: ViewerTarget): URL;
17
+ export declare function citationForTarget({ documentLabel, unitNumber, targetLabel, permalink }: {
18
+ documentLabel: string;
19
+ unitNumber: string;
20
+ targetLabel?: string | null;
21
+ permalink: string;
22
+ }): string;
@@ -0,0 +1,36 @@
1
+ export function targetFromUrl(url) {
2
+ const unitId = url.searchParams.get("unit");
3
+ if (!unitId)
4
+ return null;
5
+ const assetId = url.searchParams.get("asset");
6
+ if (assetId) {
7
+ const kind = url.searchParams.get("assetKind");
8
+ const assetKind = kind === "formula" || kind === "table" || kind === "figure" ? kind : undefined;
9
+ return { kind: "asset", unitId, assetId, assetKind };
10
+ }
11
+ const blockId = url.searchParams.get("block");
12
+ return blockId ? { kind: "block", unitId, blockId } : { kind: "unit", unitId };
13
+ }
14
+ export function urlForViewerTarget(currentUrl, mode, defaultMode, target) {
15
+ const url = new URL(currentUrl);
16
+ if (mode === defaultMode)
17
+ url.searchParams.delete("mode");
18
+ else
19
+ url.searchParams.set("mode", mode);
20
+ url.searchParams.set("unit", target.unitId);
21
+ url.searchParams.delete("block");
22
+ url.searchParams.delete("asset");
23
+ url.searchParams.delete("assetKind");
24
+ if (target.kind === "block")
25
+ url.searchParams.set("block", target.blockId);
26
+ if (target.kind === "asset") {
27
+ url.searchParams.set("asset", target.assetId);
28
+ if (target.assetKind)
29
+ url.searchParams.set("assetKind", target.assetKind);
30
+ }
31
+ return url;
32
+ }
33
+ export function citationForTarget({ documentLabel, unitNumber, targetLabel, permalink }) {
34
+ return `${documentLabel}, § ${unitNumber}${targetLabel ? `, ${targetLabel}` : ""} — ${permalink}`;
35
+ }
36
+ //# sourceMappingURL=permalinks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permalinks.js","sourceRoot":"","sources":["../shared/permalinks.ts"],"names":[],"mappings":"AAOA,MAAM,UAAU,aAAa,CAAC,GAAQ;IACpC,MAAM,MAAM,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5C,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,MAAM,OAAO,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC9C,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,IAAI,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAC/C,MAAM,SAAS,GAAG,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;QACjG,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;IACvD,CAAC;IACD,MAAM,OAAO,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC9C,OAAO,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AACjF,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,UAAwB,EAAE,IAAgB,EAAE,WAAuB,EAAE,MAAoB;IAC1H,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC;IAChC,IAAI,IAAI,KAAK,WAAW;QAAE,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;;QACrD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACxC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IAC5C,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACjC,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACjC,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IACrC,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO;QAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IAC3E,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC5B,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;QAC9C,IAAI,MAAM,CAAC,SAAS;YAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC;IAC5E,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAiG;IACpL,OAAO,GAAG,aAAa,OAAO,UAAU,GAAG,WAAW,CAAC,CAAC,CAAC,KAAK,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,SAAS,EAAE,CAAC;AACpG,CAAC"}
@@ -0,0 +1,22 @@
1
+ import { type CorpusManifest, type DocumentLookup, type SearchResult, type ViewerMode } from "./corpusData";
2
+ type SearchStatus = "idle" | "loading" | "ready" | "error";
3
+ export declare function resolveExactSearch(query: string, mode: ViewerMode, lookup: DocumentLookup | null, circLookup: DocumentLookup | null): SearchResult[] | null;
4
+ export declare function useViewerSearch({ query, mode, manifest, dataBaseUrl, lookup, circLookup, maxResults, debounceMs }: {
5
+ query: string;
6
+ mode: ViewerMode;
7
+ manifest: CorpusManifest | null;
8
+ dataBaseUrl: string;
9
+ lookup: DocumentLookup | null;
10
+ circLookup: DocumentLookup | null;
11
+ maxResults: number;
12
+ debounceMs?: number;
13
+ }): {
14
+ queryReady: boolean;
15
+ results: SearchResult[];
16
+ status: SearchStatus;
17
+ source: "exact" | "worker";
18
+ durationMs: number | null;
19
+ errorMessage: string | undefined;
20
+ indexRequested: boolean;
21
+ };
22
+ export {};