explico 0.13.1 → 0.15.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 +990 -1
- package/README.md +200 -30
- package/dist/compiler/astro.d.ts +15 -2
- package/dist/compiler/astro.js +40 -11
- package/dist/compiler/cli-main.d.ts +100 -0
- package/dist/compiler/cli-main.js +433 -0
- package/dist/compiler/cli-reference.d.ts +50 -0
- package/dist/compiler/cli-reference.js +213 -0
- package/dist/compiler/cli-verbs.d.ts +45 -0
- package/dist/compiler/cli-verbs.js +224 -0
- package/dist/compiler/cli-verify.d.ts +51 -0
- package/dist/compiler/cli-verify.js +151 -0
- package/dist/compiler/cli.d.ts +408 -24
- package/dist/compiler/cli.js +1328 -8
- package/dist/compiler/collections.d.ts +88 -1
- package/dist/compiler/collections.js +170 -25
- package/dist/compiler/content-release.d.ts +49 -0
- package/dist/compiler/content-release.js +63 -0
- package/dist/compiler/content-schemas.d.ts +2 -2
- package/dist/compiler/course-config.d.ts +1 -1
- package/dist/compiler/course-config.js +105 -1
- package/dist/compiler/decks.d.ts +161 -0
- package/dist/compiler/decks.js +235 -0
- package/dist/compiler/exports/combined-html.d.ts +5 -1
- package/dist/compiler/exports/deck-fit.d.ts +55 -0
- package/dist/compiler/exports/deck-fit.js +90 -0
- package/dist/compiler/exports/export-chapters.d.ts +37 -6
- package/dist/compiler/exports/export-chapters.js +64 -5
- package/dist/compiler/exports/export-pages.d.ts +30 -3
- package/dist/compiler/exports/export-pages.js +24 -1
- package/dist/compiler/exports/latex/beamerthemeexplico.sty +124 -0
- package/dist/compiler/exports/latex/cache.d.ts +78 -0
- package/dist/compiler/exports/latex/cache.js +129 -0
- package/dist/compiler/exports/latex/capture.d.ts +10 -1
- package/dist/compiler/exports/latex/capture.js +3 -1
- package/dist/compiler/exports/latex/compile.d.ts +13 -2
- package/dist/compiler/exports/latex/compile.js +12 -1
- package/dist/compiler/exports/latex/deck.d.ts +92 -0
- package/dist/compiler/exports/latex/deck.js +349 -0
- package/dist/compiler/exports/latex/document.d.ts +51 -6
- package/dist/compiler/exports/latex/document.js +71 -12
- package/dist/compiler/exports/latex/explico-common.sty +198 -0
- package/dist/compiler/exports/latex/explico.cls +13 -183
- package/dist/compiler/exports/latex/serialize.d.ts +60 -4
- package/dist/compiler/exports/latex/serialize.js +4 -2
- package/dist/compiler/exports/latex-export.d.ts +24 -2
- package/dist/compiler/exports/latex-export.js +107 -26
- package/dist/compiler/exports/markdown/serialize.d.ts +24 -4
- package/dist/compiler/exports/markdown/serialize.js +85 -8
- package/dist/compiler/exports/markdown-export.d.ts +6 -1
- package/dist/compiler/exports/markdown-export.js +50 -11
- package/dist/compiler/exports/standalone-html.d.ts +10 -5
- package/dist/compiler/exports/standalone-html.js +3 -3
- package/dist/compiler/exports/static-tree.d.ts +38 -1
- package/dist/compiler/exports/static-tree.js +120 -4
- package/dist/compiler/knowledge-version.d.ts +26 -2
- package/dist/compiler/knowledge-version.js +27 -17
- package/dist/compiler/lesson-derivation.d.ts +143 -6
- package/dist/compiler/lesson-derivation.js +145 -13
- package/dist/compiler/manifest.d.ts +133 -4
- package/dist/compiler/manifest.js +364 -58
- package/dist/compiler/page-versions.d.ts +184 -0
- package/dist/compiler/page-versions.js +217 -0
- package/dist/compiler/render-diagnostics.d.ts +5 -1
- package/dist/compiler/resolution-report.d.ts +24 -6
- package/dist/compiler/resolution-report.js +22 -4
- package/dist/compiler/validate-content.d.ts +7 -0
- package/dist/compiler/validate-content.js +160 -0
- package/dist/{course-config-rRtME5UV.d.ts → course-config-BLtIdkei.d.ts} +164 -1
- package/dist/curriculum/competency-graph.d.ts +54 -0
- package/dist/curriculum/competency-graph.js +41 -0
- package/dist/reference/base-library.d.mts +8 -0
- package/dist/reference/base-library.mjs +19 -0
- package/dist/reference/consistency.d.ts +8 -4
- package/dist/reference/consistency.js +82 -26
- package/dist/reference/deck-remark.d.mts +123 -0
- package/dist/reference/deck-remark.mjs +600 -0
- package/dist/reference/deck-source.d.ts +107 -0
- package/dist/reference/deck-source.js +140 -0
- package/dist/reference/decks-validate.d.ts +133 -0
- package/dist/reference/decks-validate.js +359 -0
- package/dist/reference/derived-entries.d.ts +5 -1
- package/dist/reference/diagram-fit.d.ts +6 -2
- package/dist/reference/diagram-fit.js +5 -1
- package/dist/reference/figure-symbols.d.ts +23 -6
- package/dist/reference/figure-symbols.js +51 -15
- package/dist/reference/float-name.d.ts +43 -0
- package/dist/reference/float-name.js +58 -0
- package/dist/reference/folds-validate.d.ts +1 -1
- package/dist/reference/gate-abbreviations.d.ts +1 -1
- package/dist/reference/gate-indices.js +5 -4
- package/dist/reference/gate-math.d.ts +29 -2
- package/dist/reference/gate-math.js +99 -18
- package/dist/reference/gate-part-pages.d.ts +1 -1
- package/dist/reference/lab-katex-validate.d.ts +10 -6
- package/dist/reference/lab-katex-validate.js +17 -9
- package/dist/reference/lab-math-scope.js +29 -4
- package/dist/reference/math-glyphs.d.mts +25 -0
- package/dist/reference/math-glyphs.mjs +189 -25
- package/dist/reference/page-notation.d.ts +22 -3
- package/dist/reference/page-notation.js +32 -10
- package/dist/reference/references.d.ts +14 -1
- package/dist/reference/references.js +13 -0
- package/dist/reference/registry-scope.d.mts +33 -0
- package/dist/reference/registry-scope.mjs +34 -0
- package/dist/reference/registry.js +29 -10
- package/dist/reference/rehype-deck.d.mts +6 -0
- package/dist/reference/rehype-deck.mjs +286 -0
- package/dist/reference/rehype-heading-anchors.d.mts +20 -0
- package/dist/reference/rehype-heading-anchors.mjs +48 -6
- package/dist/reference/remark-notation.mjs +249 -40
- package/dist/reference/source-lines.d.mts +12 -0
- package/dist/reference/source-lines.mjs +65 -0
- package/dist/reference/types.d.ts +28 -0
- package/dist/rules/catalog.d.ts +21 -4
- package/dist/rules/catalog.js +237 -2
- package/dist/rules/course-rules.d.ts +5 -1
- package/dist/rules/course-rules.js +17 -3
- package/dist/rules/course-rules.schema.json +12 -0
- package/dist/rules/in-effect.d.ts +13 -3
- package/dist/rules/in-effect.js +34 -10
- package/dist/rules/rules.schema.json +280 -1
- package/dist/rules/settings.d.ts +1 -1
- package/dist/session/PreferenceStore.d.ts +6 -0
- package/dist/session/PreferenceStore.js +18 -1
- package/dist/session/deck-memory.d.ts +116 -0
- package/dist/session/deck-memory.js +143 -0
- package/dist/session/reading-memory.d.ts +96 -0
- package/dist/session/reading-memory.js +133 -0
- package/docs/VERSIONING.md +21 -18
- package/docs/authoring/README.md +31 -9
- package/docs/authoring/agents.md +72 -18
- package/docs/authoring/cli-reference.json +2172 -0
- package/docs/authoring/cli.md +887 -0
- package/docs/authoring/components.md +15 -0
- package/docs/authoring/deck.md +612 -0
- package/docs/authoring/migrating.md +1195 -16
- package/docs/authoring/notation.md +49 -6
- package/docs/authoring/structure.md +80 -1
- package/docs/authoring/versioning.md +56 -3
- package/docs/authoring/visualizations.md +7 -2
- package/docs/course-agent/AGENTS.md +40 -0
- package/docs/course-agent/agents/rules-reviewer.md +33 -0
- package/docs/course-agent/skills/draft-lesson/SKILL.md +38 -0
- package/docs/course-agent/skills/migrate-course/SKILL.md +32 -0
- package/docs/course-agent/skills/release-content/SKILL.md +26 -0
- package/docs/diagram-scaling.md +148 -0
- package/docs/privacy.md +59 -0
- package/package.json +8 -2
- package/src/compiler/astro.ts +60 -21
- package/src/compiler/cli-main.ts +557 -0
- package/src/compiler/cli-reference.ts +285 -0
- package/src/compiler/cli-verbs.ts +270 -0
- package/src/compiler/cli-verify.ts +241 -0
- package/src/compiler/cli.ts +2119 -170
- package/src/compiler/collections.ts +284 -25
- package/src/compiler/content-release.ts +91 -0
- package/src/compiler/course-config.ts +267 -0
- package/src/compiler/decks.ts +455 -0
- package/src/compiler/exports/deck-fit.ts +127 -0
- package/src/compiler/exports/export-chapters.ts +105 -6
- package/src/compiler/exports/export-pages.ts +46 -2
- package/src/compiler/exports/latex/beamerthemeexplico.sty +124 -0
- package/src/compiler/exports/latex/cache.ts +183 -0
- package/src/compiler/exports/latex/capture.ts +5 -2
- package/src/compiler/exports/latex/compile.ts +18 -0
- package/src/compiler/exports/latex/deck.ts +419 -0
- package/src/compiler/exports/latex/document.ts +117 -21
- package/src/compiler/exports/latex/explico-common.sty +198 -0
- package/src/compiler/exports/latex/explico.cls +13 -183
- package/src/compiler/exports/latex/serialize.ts +25 -21
- package/src/compiler/exports/latex/texlive-packages.txt +6 -2
- package/src/compiler/exports/latex-export.ts +187 -45
- package/src/compiler/exports/markdown/serialize.ts +118 -9
- package/src/compiler/exports/markdown-export.ts +56 -11
- package/src/compiler/exports/standalone-html.ts +8 -7
- package/src/compiler/exports/static-tree.ts +175 -3
- package/src/compiler/knowledge-version.ts +56 -21
- package/src/compiler/lesson-derivation.ts +282 -16
- package/src/compiler/manifest.ts +568 -63
- package/src/compiler/page-versions.ts +401 -0
- package/src/compiler/resolution-report.ts +38 -7
- package/src/compiler/validate-content.ts +215 -0
- package/src/components/CourseContents.astro +124 -6
- package/src/components/CurriculumMap.astro +49 -1
- package/src/components/FloatIndex.astro +1 -1
- package/src/components/PartOverview.astro +51 -5
- package/src/components/assessments/AssessmentSet.astro +41 -15
- package/src/components/block-heading.ts +15 -6
- package/src/components/card-carry.ts +7 -4
- package/src/components/card-manager.ts +14 -2
- package/src/components/card-memory.ts +13 -9
- package/src/components/deck/DeckEnhancer.astro +13 -0
- package/src/components/deck/deck-context.ts +104 -0
- package/src/components/deck/deck-controller.ts +1287 -0
- package/src/components/deck/deck-map.ts +91 -0
- package/src/components/deck-lane.ts +163 -0
- package/src/components/diagram/Diagram.astro +131 -86
- package/src/components/disclosure/persisted-disclosure.ts +9 -1
- package/src/components/figure/Figure.astro +7 -2
- package/src/components/figure/FloatCaption.astro +5 -3
- package/src/components/fold/Fold.astro +2 -2
- package/src/components/notation/NotationFormula.astro +81 -11
- package/src/components/notation/NotationGlossary.astro +132 -1
- package/src/components/notation/NotationLayer.astro +15 -7
- package/src/components/starlight/AboutPanel.astro +186 -3
- package/src/components/starlight/CollapsibleSections.astro +24 -3
- package/src/components/starlight/DiagramEnhancer.astro +5 -1
- package/src/components/starlight/HeadingAnchorEnhancer.astro +1 -1
- package/src/components/starlight/LayoutHeader.astro +17 -0
- package/src/components/starlight/LayoutPageTitle.astro +110 -0
- package/src/components/starlight/LayoutSidebar.astro +807 -21
- package/src/components/starlight/LayoutSocialIcons.astro +62 -6
- package/src/components/starlight/LessonExportActions.astro +27 -9
- package/src/components/starlight/LessonFooter.astro +59 -0
- package/src/components/starlight/ReadingMemoryNotice.astro +114 -0
- package/src/components/starlight/TextSizeControl.astro +24 -13
- package/src/components/starlight/page-marks.ts +139 -0
- package/src/components/starlight/section-summary.ts +1 -1
- package/src/components/starlight/sidebar-editions.ts +245 -0
- package/src/components/table/Table.astro +15 -7
- package/src/content-config.ts +87 -1
- package/src/curriculum/competency-graph.ts +95 -0
- package/src/reference/base-library.mjs +19 -0
- package/src/reference/consistency.ts +137 -39
- package/src/reference/deck-remark.mjs +600 -0
- package/src/reference/deck-source.ts +273 -0
- package/src/reference/decks-validate.ts +604 -0
- package/src/reference/diagram-fit.ts +18 -4
- package/src/reference/diagram-layout.ts +3 -1
- package/src/reference/figure-symbols.ts +108 -31
- package/src/reference/folds-validate.ts +1 -1
- package/src/reference/gate-indices.ts +5 -3
- package/src/reference/gate-math.ts +169 -25
- package/src/reference/lab-katex-validate.ts +30 -15
- package/src/reference/lab-math-scope.ts +34 -3
- package/src/reference/math-glyphs.mjs +189 -25
- package/src/reference/page-notation.ts +69 -15
- package/src/reference/references.ts +28 -0
- package/src/reference/registry-scope.mjs +34 -0
- package/src/reference/registry.ts +41 -11
- package/src/reference/rehype-deck.mjs +286 -0
- package/src/reference/rehype-heading-anchors.mjs +48 -6
- package/src/reference/remark-notation.mjs +249 -40
- package/src/reference/source-lines.mjs +65 -0
- package/src/reference/types.ts +28 -0
- package/src/rules/catalog.ts +274 -4
- package/src/rules/course-rules.ts +21 -1
- package/src/rules/in-effect.ts +58 -11
- package/src/session/PreferenceStore.ts +23 -0
- package/src/session/deck-memory.ts +263 -0
- package/src/session/reading-memory.ts +243 -0
- package/src/styles/deck.css +1150 -0
- package/src/styles/global.css +958 -27
- package/src/styles/zones.css +277 -0
- /package/src/{components → reference}/float-name.ts +0 -0
|
@@ -9,7 +9,10 @@
|
|
|
9
9
|
* snapshot. Nothing here changes content. A finding is silenced only by a
|
|
10
10
|
* reviewed line in `lint-ignore.yml` (see `./lint-ignore`).
|
|
11
11
|
*
|
|
12
|
-
* (a) glyph-unique-in-corpus one rendered glyph → one meaning
|
|
12
|
+
* (a) glyph-unique-in-corpus one rendered glyph → one shared meaning; a
|
|
13
|
+
* local card or a gloss may reuse a letter
|
|
14
|
+
* (a') definition-repeated one local key declared in two lessons
|
|
15
|
+
* (a'') meaning-repeated one symbol and name defined at two sites, any tier
|
|
13
16
|
* (b) notation-units / units-vocab every entry has units or `dimensionless`,
|
|
14
17
|
* unit strings drawn from a controlled vocab
|
|
15
18
|
* (f) weak-local a `notation.local` symbol used only in its
|
|
@@ -35,6 +38,7 @@
|
|
|
35
38
|
* `src/content/resolution-report.ts`.
|
|
36
39
|
*/
|
|
37
40
|
|
|
41
|
+
import type { LocalNotationDefinitionInput } from './types';
|
|
38
42
|
import type {
|
|
39
43
|
CourseAccents,
|
|
40
44
|
CourseOperators,
|
|
@@ -70,7 +74,8 @@ export type ConsistencyCode =
|
|
|
70
74
|
| 'units-vocab'
|
|
71
75
|
| 'weak-local'
|
|
72
76
|
| 'notation-source-locator'
|
|
73
|
-
| '
|
|
77
|
+
| 'definition-repeated'
|
|
78
|
+
| 'meaning-repeated'
|
|
74
79
|
| 'card-wants-demoting'
|
|
75
80
|
| 'gloss-name-shape'
|
|
76
81
|
| 'operator-explained'
|
|
@@ -142,7 +147,7 @@ function mathSpans(body: string): MathSpan[] {
|
|
|
142
147
|
|
|
143
148
|
// --- glyph regex for weak-local --------------------------------------
|
|
144
149
|
|
|
145
|
-
function glyphMatcher(latex: string): RegExp | undefined {
|
|
150
|
+
export function glyphMatcher(latex: string): RegExp | undefined {
|
|
146
151
|
const trimmed = latex.trim();
|
|
147
152
|
if (trimmed === '') return undefined;
|
|
148
153
|
if (/^[A-Za-z]$/.test(trimmed)) {
|
|
@@ -175,15 +180,19 @@ function checkGlyphUniqueness(
|
|
|
175
180
|
glyphs.set(trimmed, keys);
|
|
176
181
|
};
|
|
177
182
|
|
|
183
|
+
// Shared cards only: a vocabulary glyph has one meaning across the course.
|
|
184
|
+
// A `notation.local` entry or a gloss that reuses a letter is the declared
|
|
185
|
+
// exception — the tier exists for that — so it is never recorded here;
|
|
186
|
+
// `definition-repeated` and `meaning-repeated` watch those for copies.
|
|
178
187
|
for (const definition of input.registry.definitions) {
|
|
179
|
-
if (definition.kind === '
|
|
180
|
-
record(definition.notation, definition.key,
|
|
188
|
+
if (definition.kind === 'shared') {
|
|
189
|
+
record(definition.notation, definition.key, '');
|
|
181
190
|
}
|
|
182
191
|
}
|
|
183
192
|
for (const bundle of input.registry.bundles) {
|
|
184
193
|
for (const binding of bundle.bindings) {
|
|
185
194
|
const definition = byId.get(binding.definitionId);
|
|
186
|
-
if (definition) record(definition.notation, binding.key, bundle.lessonId);
|
|
195
|
+
if (definition?.kind === 'shared') record(definition.notation, binding.key, bundle.lessonId);
|
|
187
196
|
}
|
|
188
197
|
}
|
|
189
198
|
|
|
@@ -194,13 +203,16 @@ function checkGlyphUniqueness(
|
|
|
194
203
|
if (input.lintIgnore.matches('glyph-unique-in-corpus', glyph)) continue;
|
|
195
204
|
const detail = [...keys]
|
|
196
205
|
.sort(([a], [b]) => a.localeCompare(b))
|
|
197
|
-
.map(([key, lessons]) =>
|
|
206
|
+
.map(([key, lessons]) => {
|
|
207
|
+
const bound = [...lessons].filter((lesson) => lesson !== '').sort();
|
|
208
|
+
return `${key} (${bound.length > 0 ? bound.join(', ') : 'no lesson binds it'})`;
|
|
209
|
+
})
|
|
198
210
|
.join('; ');
|
|
199
211
|
out.push({
|
|
200
212
|
code: 'glyph-unique-in-corpus',
|
|
201
213
|
severity: 'warning',
|
|
202
214
|
glyph,
|
|
203
|
-
message: `glyph ${JSON.stringify(glyph)}
|
|
215
|
+
message: `glyph ${JSON.stringify(glyph)} is ${keys.size} shared cards: ${detail}; course vocabulary gives a glyph one meaning — keep one card, or make the page-bound one a notation.local entry`,
|
|
204
216
|
});
|
|
205
217
|
}
|
|
206
218
|
}
|
|
@@ -286,50 +298,134 @@ function checkNotationSourceLocator(
|
|
|
286
298
|
}
|
|
287
299
|
|
|
288
300
|
/**
|
|
289
|
-
* The
|
|
301
|
+
* The tier model needs both directions kept honest (D15). A meaning is
|
|
302
|
+
* declared once: a shared card unless the letter already means something
|
|
303
|
+
* else in the course, and only then `notation.local` or a gloss. The sign
|
|
304
|
+
* that a meaning is not page-bound is that it got defined a second time —
|
|
305
|
+
* so the lints watch for copies, never for how often a symbol is used.
|
|
290
306
|
*
|
|
291
|
-
*
|
|
292
|
-
*
|
|
293
|
-
*
|
|
294
|
-
*
|
|
307
|
+
* `definition-repeated`: one local key declared in two or more lessons. Either
|
|
308
|
+
* it is one meaning, so it belongs in `content/notation/<key>.md` (every
|
|
309
|
+
* `[[key]]` keeps working), or it is two, so one needs another key. A deck
|
|
310
|
+
* inherits its chapters' entries rather than redeclaring them, so decks never
|
|
311
|
+
* count. When a shared card already owns the glyph, the entry is a reused
|
|
312
|
+
* letter (N4) and promoting it would put two shared cards on one glyph, so
|
|
313
|
+
* the message says that instead of "move one copy".
|
|
295
314
|
*/
|
|
296
|
-
|
|
315
|
+
function checkDefinitionRepeated(
|
|
316
|
+
input: ConsistencyInput,
|
|
317
|
+
out: ConsistencyDiagnostic[],
|
|
318
|
+
): void {
|
|
319
|
+
const byKey = new Map<string, { lessonId: string; file: string; definition: LocalNotationDefinitionInput }[]>();
|
|
320
|
+
// A deck resolves in its chapters' scopes (`scopeLessonIds`) and is no
|
|
321
|
+
// declaration site of its own, whatever its frontmatter repeats.
|
|
322
|
+
const chapters = input.notationInput.lessons.filter((lesson) => lesson.scopeLessonIds === undefined);
|
|
323
|
+
for (const lesson of [...chapters].sort((a, b) => a.lessonId.localeCompare(b.lessonId))) {
|
|
324
|
+
for (const definition of lesson.localDefinitions) {
|
|
325
|
+
const list = byKey.get(definition.key) ?? [];
|
|
326
|
+
if (!list.some((entry) => entry.lessonId === lesson.lessonId)) {
|
|
327
|
+
list.push({ lessonId: lesson.lessonId, file: lesson.source.file, definition });
|
|
328
|
+
}
|
|
329
|
+
byKey.set(definition.key, list);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
const sharedByGlyph = new Map<string, string>();
|
|
333
|
+
for (const definition of input.notationInput.sharedDefinitions) {
|
|
334
|
+
if (!sharedByGlyph.has(definition.notation.trim())) sharedByGlyph.set(definition.notation.trim(), definition.key);
|
|
335
|
+
}
|
|
336
|
+
for (const [key, copies] of [...byKey].sort(([a], [b]) => a.localeCompare(b))) {
|
|
337
|
+
if (copies.length < 2) continue;
|
|
338
|
+
if (input.lintIgnore.matches('definition-repeated', key)) continue;
|
|
339
|
+
const first = copies[0]!;
|
|
340
|
+
const differing = (['notation', 'label', 'summary', 'units', 'sources'] as const).filter((field) =>
|
|
341
|
+
copies.some((copy) => JSON.stringify(copy.definition[field] ?? null) !== JSON.stringify(first.definition[field] ?? null)),
|
|
342
|
+
);
|
|
343
|
+
const lessons = copies.map((copy) => copy.lessonId).sort();
|
|
344
|
+
const owner = sharedByGlyph.get(first.definition.notation.trim());
|
|
345
|
+
const advice =
|
|
346
|
+
owner !== undefined && owner !== key
|
|
347
|
+
? ` — its glyph is already shared card ${owner}'s, so it is a reused letter (rule N4) and promoting it would put two shared cards on one glyph (glyph-unique-in-corpus): keep the copies local and acknowledge them in lint-ignore.yml ("definition-repeated:${key} — <why>"), or, if they are two meanings, give one another key`
|
|
348
|
+
: ` — move one copy to content/notation/${key}.md and delete the others: every [[${key}]] and \\explain{${key}} keeps working; if they are two meanings, give one another key`;
|
|
349
|
+
out.push({
|
|
350
|
+
code: 'definition-repeated',
|
|
351
|
+
severity: 'warning',
|
|
352
|
+
key,
|
|
353
|
+
lessonId: first.lessonId,
|
|
354
|
+
file: first.file,
|
|
355
|
+
count: copies.length,
|
|
356
|
+
message:
|
|
357
|
+
`local entry ${key} (${first.definition.notation}) is declared in ${copies.length} lessons (${lessons.join(', ')}); ` +
|
|
358
|
+
(differing.length === 0
|
|
359
|
+
? 'the copies agree'
|
|
360
|
+
: `the copies differ in ${differing.join(', ')}`) +
|
|
361
|
+
advice,
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
}
|
|
297
365
|
|
|
298
|
-
|
|
366
|
+
/**
|
|
367
|
+
* `meaning-repeated`: one symbol with one name defined at two or more sites,
|
|
368
|
+
* in any tier — a local card and a gloss, two glosses, a local card beside a
|
|
369
|
+
* shared card that already says it. A second definition of the same meaning
|
|
370
|
+
* is the signal that it is vocabulary; a shared card serves every site, and
|
|
371
|
+
* where one exists already the page should bind it instead.
|
|
372
|
+
*/
|
|
373
|
+
function checkMeaningRepeated(
|
|
299
374
|
input: ConsistencyInput,
|
|
300
375
|
out: ConsistencyDiagnostic[],
|
|
301
376
|
): void {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
377
|
+
interface Site {
|
|
378
|
+
readonly tier: 'shared' | 'local' | 'gloss';
|
|
379
|
+
readonly key: string;
|
|
380
|
+
readonly where: string;
|
|
381
|
+
}
|
|
382
|
+
const normalize = (label: string) => label.toLowerCase().replace(/\s+/g, ' ').trim();
|
|
383
|
+
const groups = new Map<string, { notation: string; label: string; sites: Site[] }>();
|
|
384
|
+
const record = (notation: string, label: string, site: Site) => {
|
|
385
|
+
const id = `${notation.trim()}\u0000${normalize(label)}`;
|
|
386
|
+
const group = groups.get(id) ?? { notation: notation.trim(), label, sites: [] };
|
|
387
|
+
if (!group.sites.some((known) => known.tier === site.tier && known.key === site.key && known.where === site.where)) {
|
|
388
|
+
group.sites.push(site);
|
|
389
|
+
}
|
|
390
|
+
groups.set(id, group);
|
|
311
391
|
};
|
|
312
|
-
|
|
313
392
|
for (const definition of input.notationInput.sharedDefinitions) {
|
|
314
|
-
|
|
393
|
+
record(definition.notation, definition.label, { tier: 'shared', key: definition.key, where: `card ${definition.key}` });
|
|
394
|
+
for (const gloss of definition.glosses) {
|
|
395
|
+
record(gloss.notation, gloss.label, { tier: 'gloss', key: gloss.key, where: `gloss on ${definition.key}` });
|
|
396
|
+
}
|
|
315
397
|
}
|
|
316
398
|
for (const lesson of input.notationInput.lessons) {
|
|
399
|
+
if (lesson.scopeLessonIds !== undefined) continue; // a deck declares nothing of its own
|
|
317
400
|
for (const definition of lesson.localDefinitions) {
|
|
318
|
-
|
|
401
|
+
record(definition.notation, definition.label, {
|
|
402
|
+
tier: 'local',
|
|
403
|
+
key: definition.key,
|
|
404
|
+
where: `local ${definition.key} on ${lesson.lessonId}`,
|
|
405
|
+
});
|
|
406
|
+
for (const gloss of definition.glosses) {
|
|
407
|
+
record(gloss.notation, gloss.label, { tier: 'gloss', key: gloss.key, where: `gloss on ${definition.key} (${lesson.lessonId})` });
|
|
408
|
+
}
|
|
319
409
|
}
|
|
320
410
|
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
if (
|
|
326
|
-
const
|
|
327
|
-
if (input.lintIgnore.matches('gloss-wants-promoting', glyph)) continue;
|
|
411
|
+
for (const [, group] of [...groups].sort(([a], [b]) => a.localeCompare(b))) {
|
|
412
|
+
if (group.sites.length < 2) continue;
|
|
413
|
+
// One local key copied across lessons is `definition-repeated`'s finding.
|
|
414
|
+
if (group.sites.every((site) => site.tier === 'local' && site.key === group.sites[0]!.key)) continue;
|
|
415
|
+
if (input.lintIgnore.matches('meaning-repeated', group.notation)) continue;
|
|
416
|
+
const shared = group.sites.find((site) => site.tier === 'shared');
|
|
328
417
|
out.push({
|
|
329
|
-
code: '
|
|
418
|
+
code: 'meaning-repeated',
|
|
330
419
|
severity: 'info',
|
|
331
|
-
glyph,
|
|
332
|
-
|
|
420
|
+
glyph: group.notation,
|
|
421
|
+
count: group.sites.length,
|
|
422
|
+
message:
|
|
423
|
+
`${group.notation} as ${JSON.stringify(group.label)} is defined ${group.sites.length} times: ${group.sites
|
|
424
|
+
.map((site) => site.where)
|
|
425
|
+
.join('; ')}` +
|
|
426
|
+
(shared
|
|
427
|
+
? ` — ${shared.key} is the shared card; bind it with [[${shared.key}]] (or \\explain{${shared.key}}{${group.notation}} in a formula) instead of declaring it again`
|
|
428
|
+
: ' — one shared card would serve every site; promote it and reference it'),
|
|
333
429
|
});
|
|
334
430
|
}
|
|
335
431
|
}
|
|
@@ -726,7 +822,8 @@ const CHECKS: ((
|
|
|
726
822
|
checkNotationUnits,
|
|
727
823
|
checkWeakLocal,
|
|
728
824
|
checkNotationSourceLocator,
|
|
729
|
-
|
|
825
|
+
checkDefinitionRepeated,
|
|
826
|
+
checkMeaningRepeated,
|
|
730
827
|
checkCardWantsDemoting,
|
|
731
828
|
checkGlossNameShape,
|
|
732
829
|
checkOperatorExplained,
|
|
@@ -765,7 +862,8 @@ export function consistencyCounts(
|
|
|
765
862
|
'units-vocab': 0,
|
|
766
863
|
'weak-local': 0,
|
|
767
864
|
'notation-source-locator': 0,
|
|
768
|
-
'
|
|
865
|
+
'definition-repeated': 0,
|
|
866
|
+
'meaning-repeated': 0,
|
|
769
867
|
'card-wants-demoting': 0,
|
|
770
868
|
'gloss-name-shape': 0,
|
|
771
869
|
'operator-explained': 0,
|