explico 0.4.0 → 0.6.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 +382 -1
- package/README.md +103 -8
- package/dist/compiler/astro.d.ts +9 -1
- package/dist/compiler/astro.js +182 -3
- package/dist/compiler/cli.d.ts +13 -0
- package/dist/compiler/cli.js +28 -4
- package/dist/compiler/collections.d.ts +3 -2
- package/dist/compiler/collections.js +28 -1
- package/dist/compiler/course-config.d.ts +88 -1
- package/dist/compiler/course-config.js +35 -1
- package/dist/compiler/exports/standalone-html.d.ts +34 -0
- package/dist/compiler/exports/standalone-html.js +803 -0
- package/dist/compiler/manifest.d.ts +98 -6
- package/dist/compiler/manifest.js +154 -17
- package/dist/compiler/resolution-report.d.ts +4 -0
- package/dist/reference/base-abbreviations.d.mts +30 -0
- package/dist/reference/base-abbreviations.mjs +60 -0
- package/dist/reference/base-operators.d.mts +15 -0
- package/dist/reference/base-operators.mjs +40 -0
- package/dist/reference/base-roman-labels.d.mts +35 -0
- package/dist/reference/base-roman-labels.mjs +43 -0
- package/dist/reference/consistency.d.ts +8 -2
- package/dist/reference/consistency.js +50 -3
- package/dist/reference/diagrams-validate.d.ts +21 -0
- package/dist/reference/diagrams-validate.js +13 -0
- package/dist/reference/equations.d.mts +10 -15
- package/dist/reference/equations.mjs +67 -14
- package/dist/reference/gate-abbreviations.d.ts +17 -0
- package/dist/reference/gate-abbreviations.js +64 -0
- package/dist/reference/gate-heading-length.d.ts +12 -0
- package/dist/reference/gate-heading-length.js +79 -0
- package/dist/reference/gate-math.d.ts +32 -5
- package/dist/reference/gate-math.js +6 -2
- package/dist/reference/gate-prose-notation.d.ts +60 -0
- package/dist/reference/gate-prose-notation.js +125 -0
- package/dist/reference/keyed-float-validate.d.ts +5 -5
- package/dist/reference/keyed-jsx-floats.d.mts +7 -12
- package/dist/reference/keyed-jsx-floats.mjs +50 -25
- package/dist/reference/knowledge-checks-validate.d.ts +35 -0
- package/dist/reference/knowledge-checks-validate.js +78 -0
- package/dist/reference/knowledge-checks.d.mts +25 -0
- package/dist/reference/knowledge-checks.mjs +45 -0
- package/dist/reference/lab-math-scope.d.ts +9 -1
- package/dist/reference/lab-math-scope.js +32 -4
- package/dist/reference/math-glyphs.d.mts +42 -0
- package/dist/reference/math-glyphs.mjs +548 -40
- package/dist/reference/mdx-attributes.d.mts +13 -0
- package/dist/reference/mdx-attributes.mjs +38 -0
- package/dist/reference/mixed-content.d.ts +17 -0
- package/dist/reference/mixed-content.js +29 -0
- package/dist/reference/page-notation.d.ts +52 -0
- package/dist/reference/page-notation.js +171 -0
- package/dist/reference/raw-floats-validate.d.ts +18 -0
- package/dist/reference/raw-floats-validate.js +53 -0
- package/dist/reference/raw-floats.d.mts +18 -0
- package/dist/reference/raw-floats.mjs +129 -0
- package/dist/reference/rehype-heading-anchors.d.mts +1 -1
- package/dist/reference/rehype-heading-anchors.mjs +107 -1
- package/dist/reference/rehype-table-header-labels.d.mts +26 -0
- package/dist/reference/rehype-table-header-labels.mjs +152 -0
- package/dist/reference/remark-abbreviations.d.mts +7 -0
- package/dist/reference/remark-abbreviations.mjs +174 -0
- package/dist/reference/remark-citation.d.mts +5 -1
- package/dist/reference/remark-citation.mjs +0 -0
- package/dist/reference/remark-memo.d.mts +16 -0
- package/dist/reference/remark-memo.mjs +339 -0
- package/dist/reference/remark-notation.d.mts +57 -9
- package/dist/reference/remark-notation.mjs +354 -42
- package/dist/reference/remark-short-note.d.mts +4 -0
- package/dist/reference/remark-short-note.mjs +237 -0
- package/dist/reference/types.d.ts +16 -1
- package/dist/reference/units-vocab.js +4 -0
- package/docs/VERSIONING.md +53 -4
- package/docs/authoring/README.md +10 -0
- package/docs/authoring/agents.md +17 -2
- package/docs/authoring/components.md +138 -0
- package/docs/authoring/migrating.md +275 -7
- package/docs/authoring/notation.md +615 -1
- package/docs/authoring/structure-rules.md +69 -0
- package/docs/authoring/structure.md +120 -8
- package/docs/authoring/visualizations.md +36 -3
- package/package.json +5 -2
- package/src/compiler/astro.ts +208 -3
- package/src/compiler/cli.ts +29 -2
- package/src/compiler/collections.ts +45 -4
- package/src/compiler/course-config.ts +132 -0
- package/src/compiler/exports/standalone-html.ts +963 -0
- package/src/compiler/manifest.ts +281 -22
- package/src/components/HoverPanel.astro +45 -4
- package/src/components/abbreviation/AbbreviationGlossary.astro +70 -0
- package/src/components/abbreviation/AbbreviationLayer.astro +127 -0
- package/src/components/assessments/AssessmentRunner.tsx +52 -17
- package/src/components/assessments/AssessmentSet.astro +98 -14
- package/src/components/assessments/RenderedContent.tsx +55 -0
- package/src/components/assessments/assessment-randomization.ts +16 -0
- package/src/components/diagram/Diagram.astro +409 -103
- package/src/components/disclosure/hover-preview.ts +202 -0
- package/src/components/disclosure/persisted-disclosure.ts +66 -0
- package/src/components/examples/CompactExamples.astro +2 -0
- package/src/components/figure/Figure.astro +279 -10
- package/src/components/memo/Memo.astro +453 -0
- package/src/components/memo/ShortNote.astro +223 -0
- package/src/components/notation/NotationFormula.astro +18 -1
- package/src/components/notation/NotationLayer.astro +260 -83
- package/src/components/notation/render-notation-math.ts +39 -2
- package/src/components/notation/safe-math-to-react.ts +50 -0
- package/src/components/reference/FloatRefLayer.astro +233 -15
- package/src/components/starlight/CollapsibleSections.astro +461 -0
- package/src/components/starlight/DiagramEnhancer.astro +385 -0
- package/src/components/starlight/HeadingAnchorEnhancer.astro +5 -17
- package/src/components/starlight/KeyedFloatEnhancer.astro +81 -3
- package/src/components/starlight/LayoutHeader.astro +50 -0
- package/src/components/starlight/LayoutSocialIcons.astro +41 -2
- package/src/components/starlight/LessonExportActions.astro +436 -0
- package/src/components/starlight/LessonFooter.astro +30 -3
- package/src/components/starlight/section-parts.ts +31 -0
- package/src/components/table/Table.astro +293 -7
- package/src/components/table/TableOverflowExpander.astro +489 -0
- package/src/content-config.ts +36 -1
- package/src/reference/base-abbreviations.mjs +60 -0
- package/src/reference/base-operators.mjs +40 -0
- package/src/reference/base-roman-labels.mjs +43 -0
- package/src/reference/consistency.ts +73 -2
- package/src/reference/diagrams-validate.ts +40 -0
- package/src/reference/equations.mjs +67 -14
- package/src/reference/gate-abbreviations.ts +128 -0
- package/src/reference/gate-heading-length.ts +131 -0
- package/src/reference/gate-math.ts +32 -7
- package/src/reference/gate-prose-notation.ts +224 -0
- package/src/reference/keyed-float-validate.ts +5 -5
- package/src/reference/keyed-jsx-floats.mjs +50 -25
- package/src/reference/knowledge-checks-validate.ts +130 -0
- package/src/reference/knowledge-checks.mjs +45 -0
- package/src/reference/lab-math-scope.ts +45 -3
- package/src/reference/math-glyphs.mjs +548 -40
- package/src/reference/mdx-attributes.mjs +38 -0
- package/src/reference/mixed-content.ts +69 -0
- package/src/reference/page-notation.ts +252 -0
- package/src/reference/raw-floats-validate.ts +103 -0
- package/src/reference/raw-floats.mjs +129 -0
- package/src/reference/rehype-heading-anchors.mjs +107 -1
- package/src/reference/rehype-table-header-labels.mjs +152 -0
- package/src/reference/remark-abbreviations.mjs +174 -0
- package/src/reference/remark-citation.mjs +0 -0
- package/src/reference/remark-memo.mjs +339 -0
- package/src/reference/remark-notation.mjs +354 -42
- package/src/reference/remark-short-note.mjs +237 -0
- package/src/reference/types.ts +16 -0
- package/src/reference/units-vocab.ts +4 -0
- package/src/styles/global.css +996 -50
|
@@ -3,10 +3,14 @@ import { ConsistencyDiagnostic } from '../reference/consistency.js';
|
|
|
3
3
|
import { EquationLabelRecord, EquationDiagnostic } from '../reference/equations-validate.js';
|
|
4
4
|
import { TableLabelRecord, TableDiagnostic } from '../reference/tables-validate.js';
|
|
5
5
|
import { FigureLabelRecord, FigureDiagnostic } from '../reference/figures-validate.js';
|
|
6
|
+
import { DiagramLabelRecord, DiagramDiagnostic } from '../reference/diagrams-validate.js';
|
|
7
|
+
import { KnowledgeCheckLabelRecord, KnowledgeCheckDiagnostic } from '../reference/knowledge-checks-validate.js';
|
|
8
|
+
import { RawFloatDiagnostic } from '../reference/raw-floats-validate.js';
|
|
6
9
|
import { MathGateDiagnostic } from '../reference/gate-math.js';
|
|
10
|
+
import { ContentSegment } from '../reference/mixed-content.js';
|
|
7
11
|
import { GlyphScopeEntry } from '../reference/gloss.js';
|
|
8
12
|
import { NotationDefinitionRecord, NotationPageBundle, NotationBacklink, NotationDiagnostic, EditorialStatus, NotationGlossEntry } from '../reference/types.js';
|
|
9
|
-
import { CourseConfig } from './course-config.js';
|
|
13
|
+
import { NormalizedLessonExports, NormalizedDisclosureDefaults, CourseConfig } from './course-config.js';
|
|
10
14
|
import { KnowledgeVersionDiagnostic } from './knowledge-version.js';
|
|
11
15
|
import { SidebarGroup } from './lesson-derivation.js';
|
|
12
16
|
import './collections.js';
|
|
@@ -51,7 +55,7 @@ import '../reference/keyed-float-validate.js';
|
|
|
51
55
|
* `lessons[].equations`, and `diagnostics.equations`.
|
|
52
56
|
* v4 (F-tables-figures): `tables`/`figures` (same shape as `equations`, for
|
|
53
57
|
* `<Table>`/`<Figure>`), `lessons[].tables`/`lessons[].figures`, and
|
|
54
|
-
* `diagnostics.tables`/`diagnostics.figures`.
|
|
58
|
+
* `diagnostics.tables`/`diagnostics.figures`/`diagnostics.diagrams`.
|
|
55
59
|
* v5 (about): `about` (engine identity from `explico`'s `package.json`, the
|
|
56
60
|
* course's reader-facing `about` block, and the knowledge version resolved
|
|
57
61
|
* against `content/CHANGELOG.md`) and `diagnostics.knowledge`.
|
|
@@ -61,8 +65,39 @@ import '../reference/keyed-float-validate.js';
|
|
|
61
65
|
* now the number before the dot), `lessons[].chapter` redefined as the
|
|
62
66
|
* lesson's ordinal *within its part*, and `lessons[].numbering` — the
|
|
63
67
|
* per-kind offsets that make `n` run continuously across a part.
|
|
68
|
+
* v8 (assessment math): `assessmentContent` — every assessment item's
|
|
69
|
+
* `prompt` / `explanation`, pre-split into text/math segments and resolved
|
|
70
|
+
* against the scope of the lesson that embeds it, so `AssessmentSet.astro`
|
|
71
|
+
* can render them through the same KaTeX pipeline as lesson body math instead
|
|
72
|
+
* of showing raw text.
|
|
73
|
+
* v9 (diagram identity): `diagrams` (the independent `<Diagram>` number and
|
|
74
|
+
* cross-page lookup), `lessons[].diagrams`, `lessons[].numbering.diagrams`,
|
|
75
|
+
* and `diagnostics.diagrams`.
|
|
76
|
+
* v10 (knowledge-check identity): `checks` (part-wide numbering and cross-page
|
|
77
|
+
* lookup), `lessons[].knowledgeChecks`, `lessons[].numbering.checks`, and
|
|
78
|
+
* `diagnostics.checks`.
|
|
79
|
+
* v11 (lesson exports): normalized standalone HTML and print build/UI settings.
|
|
80
|
+
* v12 (abbreviation hover cards): `manifest.abbreviations` — the
|
|
81
|
+
* `abbreviations` content collection (N14), flattened to plain
|
|
82
|
+
* `{key, abbr, expansion, aliases, domain?}` records sorted by key, the way
|
|
83
|
+
* `manifest.notation.raw` already carries notation frontmatter for
|
|
84
|
+
* `remarkNotation`. `remarkAbbreviations` reads it to wrap the first
|
|
85
|
+
* occurrence of a known abbreviation in a lesson's prose with a hover/click
|
|
86
|
+
* card.
|
|
87
|
+
* v13 (raw-float diagnostics, additive): `diagnostics.rawFloats` — a raw
|
|
88
|
+
* Markdown table, a raw HTML `<table>`, or an Explico `<Diagram>` that is
|
|
89
|
+
* unkeyed or nested outside `<Table>`/`<Figure>` never reaches keyed-float
|
|
90
|
+
* validation and can never get a stable number or `[[…]]` reference; this
|
|
91
|
+
* flags it (warning-only — the manifest shape a client already reads is
|
|
92
|
+
* unchanged).
|
|
93
|
+
* v14 (disclosure defaults, additive): `disclosureDefaults` — the resolved
|
|
94
|
+
* initial open/closed state for the "Page notation" panel and the generated
|
|
95
|
+
* "References" section, from `CourseConfig.disclosureDefaults`. Both are
|
|
96
|
+
* engine-authored sections, not written by a lesson, so they get their own
|
|
97
|
+
* default independent of a course's `##` heading default; closed unless the
|
|
98
|
+
* course sets one.
|
|
64
99
|
*/
|
|
65
|
-
declare const MANIFEST_SCHEMA_VERSION =
|
|
100
|
+
declare const MANIFEST_SCHEMA_VERSION = 14;
|
|
66
101
|
declare const MANIFEST_PATH: string;
|
|
67
102
|
interface ManifestCitation {
|
|
68
103
|
readonly id: string;
|
|
@@ -114,8 +149,9 @@ interface ManifestLesson {
|
|
|
114
149
|
/**
|
|
115
150
|
* The 1-based ordinal of the **part** this lesson sits in — its sidebar
|
|
116
151
|
* group's position in `courseConfig.parts`. It is the number before the dot
|
|
117
|
-
* in every equation, table, and
|
|
118
|
-
* first equation of part 2. See
|
|
152
|
+
* in every equation, table, figure, diagram, and knowledge-check number on
|
|
153
|
+
* the page: `(2.1)` is the first equation of part 2. See
|
|
154
|
+
* `docs/authoring/structure.md`.
|
|
119
155
|
*/
|
|
120
156
|
readonly part: number;
|
|
121
157
|
/**
|
|
@@ -133,6 +169,8 @@ interface ManifestLesson {
|
|
|
133
169
|
readonly equations: number;
|
|
134
170
|
readonly tables: number;
|
|
135
171
|
readonly figures: number;
|
|
172
|
+
readonly diagrams: number;
|
|
173
|
+
readonly checks: number;
|
|
136
174
|
};
|
|
137
175
|
readonly file: string;
|
|
138
176
|
readonly title: string;
|
|
@@ -143,6 +181,11 @@ interface ManifestLesson {
|
|
|
143
181
|
readonly requires: readonly string[];
|
|
144
182
|
readonly assumptions: readonly string[];
|
|
145
183
|
readonly assessments: readonly string[];
|
|
184
|
+
/** Numbered knowledge checks from `assessments`, in `.checks.yml` order. */
|
|
185
|
+
readonly knowledgeChecks: readonly {
|
|
186
|
+
readonly key: string;
|
|
187
|
+
readonly number: string;
|
|
188
|
+
}[];
|
|
146
189
|
readonly sources: readonly string[];
|
|
147
190
|
readonly citations: readonly ManifestCitation[];
|
|
148
191
|
/** Competency prerequisite edges this lesson introduces. */
|
|
@@ -162,6 +205,11 @@ interface ManifestLesson {
|
|
|
162
205
|
readonly key: string;
|
|
163
206
|
readonly number: string;
|
|
164
207
|
}[];
|
|
208
|
+
/** Keyed `<Diagram>` elements in appearance order. */
|
|
209
|
+
readonly diagrams: readonly {
|
|
210
|
+
readonly key: string;
|
|
211
|
+
readonly number: string;
|
|
212
|
+
}[];
|
|
165
213
|
readonly notation: {
|
|
166
214
|
readonly bindings: readonly {
|
|
167
215
|
readonly key: string;
|
|
@@ -202,8 +250,20 @@ interface ManifestAbout {
|
|
|
202
250
|
readonly disclaimer: string;
|
|
203
251
|
readonly changelogHref?: string;
|
|
204
252
|
}
|
|
253
|
+
/** One assessment item's pre-rendered `prompt` / `explanation` (v8). */
|
|
254
|
+
interface ManifestAssessmentItemContent {
|
|
255
|
+
readonly lessonId: string;
|
|
256
|
+
readonly assessmentId: string;
|
|
257
|
+
readonly itemId: string;
|
|
258
|
+
readonly promptSegments: readonly ContentSegment[];
|
|
259
|
+
readonly explanationSegments: readonly ContentSegment[];
|
|
260
|
+
}
|
|
205
261
|
interface Manifest {
|
|
206
262
|
readonly schemaVersion: number;
|
|
263
|
+
/** Normalized build/UI settings for opt-in per-lesson exports. */
|
|
264
|
+
readonly lessonExports: NormalizedLessonExports;
|
|
265
|
+
/** Initial open/closed state for "Page notation" and "References". */
|
|
266
|
+
readonly disclosureDefaults: NormalizedDisclosureDefaults;
|
|
207
267
|
/** Reader-facing identity + versions (the `[?]` About panel). */
|
|
208
268
|
readonly about: ManifestAbout;
|
|
209
269
|
readonly notation: {
|
|
@@ -217,6 +277,8 @@ interface Manifest {
|
|
|
217
277
|
readonly competencies: readonly Record<string, unknown>[];
|
|
218
278
|
readonly sources: readonly Record<string, unknown>[];
|
|
219
279
|
readonly tracks: readonly Record<string, unknown>[];
|
|
280
|
+
/** Abbreviations table (N14), for `remarkAbbreviations`. Ordered by key. */
|
|
281
|
+
readonly abbreviations: readonly Record<string, unknown>[];
|
|
220
282
|
readonly sidebar: readonly SidebarGroup[];
|
|
221
283
|
readonly lessons: readonly ManifestLesson[];
|
|
222
284
|
/** Full competency prerequisite graph. */
|
|
@@ -237,6 +299,26 @@ interface Manifest {
|
|
|
237
299
|
readonly labels: readonly FigureLabelRecord[];
|
|
238
300
|
readonly numbersBySlug: Record<string, Record<string, string>>;
|
|
239
301
|
};
|
|
302
|
+
/** Keyed-float identity for `<Diagram>` — independent of figure numbering. */
|
|
303
|
+
readonly diagrams: {
|
|
304
|
+
readonly labels: readonly DiagramLabelRecord[];
|
|
305
|
+
readonly numbersBySlug: Record<string, Record<string, string>>;
|
|
306
|
+
};
|
|
307
|
+
/** Knowledge-check identity, derived from each lesson's `.checks.yml`. */
|
|
308
|
+
readonly checks: {
|
|
309
|
+
readonly labels: readonly KnowledgeCheckLabelRecord[];
|
|
310
|
+
readonly numbersBySlug: Record<string, Record<string, string>>;
|
|
311
|
+
};
|
|
312
|
+
/**
|
|
313
|
+
* Every assessment item's `prompt` / `explanation`, pre-split into
|
|
314
|
+
* text/math segments (v8). `AssessmentSet.astro` filters this by
|
|
315
|
+
* `lessonId` + `assessmentId` and renders each `math` segment's `latex`
|
|
316
|
+
* through `renderNotationMath` before handing the result to
|
|
317
|
+
* `AssessmentRunner`. An assessment embedded in more than one lesson (via
|
|
318
|
+
* more than one `checks.yml`) appears once per lesson here, since each
|
|
319
|
+
* lesson's scope can resolve its glyphs differently.
|
|
320
|
+
*/
|
|
321
|
+
readonly assessmentContent: readonly ManifestAssessmentItemContent[];
|
|
240
322
|
readonly diagnostics: {
|
|
241
323
|
readonly curriculum: readonly CurriculumIssue[];
|
|
242
324
|
readonly notation: readonly NotationDiagnostic[];
|
|
@@ -252,6 +334,16 @@ interface Manifest {
|
|
|
252
334
|
readonly tables: readonly TableDiagnostic[];
|
|
253
335
|
/** `<Figure>` identity checks (F-tables-figures) — same codes, `fig-` prefixed. */
|
|
254
336
|
readonly figures: readonly FigureDiagnostic[];
|
|
337
|
+
/** `<Diagram>` identity checks — same codes, `dia-` prefixed. */
|
|
338
|
+
readonly diagrams: readonly DiagramDiagnostic[];
|
|
339
|
+
/** Knowledge-check identity and `[[check-…]]` reference checks. */
|
|
340
|
+
readonly checks: readonly KnowledgeCheckDiagnostic[];
|
|
341
|
+
/**
|
|
342
|
+
* A raw Markdown table, a raw HTML `<table>`, or an Explico `<Diagram>`
|
|
343
|
+
* that is unkeyed or nested outside `<Table>`/`<Figure>` — invisible to
|
|
344
|
+
* keyed-float validation above. Warnings only.
|
|
345
|
+
*/
|
|
346
|
+
readonly rawFloats: readonly RawFloatDiagnostic[];
|
|
255
347
|
};
|
|
256
348
|
readonly hashes: {
|
|
257
349
|
/** `sha256` of every content source file, keyed by repo-relative path. */
|
|
@@ -280,4 +372,4 @@ declare function readManifestSync(): Manifest;
|
|
|
280
372
|
*/
|
|
281
373
|
declare function loadManifest(courseConfig?: CourseConfig): Promise<Manifest>;
|
|
282
374
|
|
|
283
|
-
export { MANIFEST_PATH, MANIFEST_SCHEMA_VERSION, type Manifest, type ManifestAbout, type ManifestCitation, type ManifestLesson, type ManifestLessonNotation, type ManifestNotationResolution, type PrereqEdge, compileManifest, loadManifest, readManifestSync, serializeManifest, writeManifest };
|
|
375
|
+
export { MANIFEST_PATH, MANIFEST_SCHEMA_VERSION, type Manifest, type ManifestAbout, type ManifestAssessmentItemContent, type ManifestCitation, type ManifestLesson, type ManifestLessonNotation, type ManifestNotationResolution, type PrereqEdge, compileManifest, loadManifest, readManifestSync, serializeManifest, writeManifest };
|
|
@@ -28,17 +28,41 @@ import {
|
|
|
28
28
|
validateFigures
|
|
29
29
|
} from "../reference/figures-validate.js";
|
|
30
30
|
import {
|
|
31
|
-
|
|
31
|
+
validateDiagrams
|
|
32
|
+
} from "../reference/diagrams-validate.js";
|
|
33
|
+
import {
|
|
34
|
+
validateKnowledgeChecks
|
|
35
|
+
} from "../reference/knowledge-checks-validate.js";
|
|
36
|
+
import {
|
|
37
|
+
validateRawFloats
|
|
38
|
+
} from "../reference/raw-floats-validate.js";
|
|
39
|
+
import {
|
|
40
|
+
gateContentMath,
|
|
41
|
+
pageScope
|
|
32
42
|
} from "../reference/gate-math.js";
|
|
43
|
+
import {
|
|
44
|
+
gateProseNotation,
|
|
45
|
+
gateProseNotationInAssessments
|
|
46
|
+
} from "../reference/gate-prose-notation.js";
|
|
47
|
+
import { gateAbbreviations } from "../reference/gate-abbreviations.js";
|
|
48
|
+
import { gateHeadingLength } from "../reference/gate-heading-length.js";
|
|
49
|
+
import {
|
|
50
|
+
splitMixedContent
|
|
51
|
+
} from "../reference/mixed-content.js";
|
|
33
52
|
import { loadLintIgnore } from "../reference/lint-ignore.js";
|
|
34
53
|
import { buildNotationRegistry } from "../reference/registry.js";
|
|
35
54
|
import { formulaGlyphScope } from "../reference/gloss.js";
|
|
36
55
|
import { resolveReferenceId } from "../reference/references.js";
|
|
37
|
-
import {
|
|
56
|
+
import {
|
|
57
|
+
coursePartsOf,
|
|
58
|
+
normalizeLessonExports,
|
|
59
|
+
normalizeDisclosureDefaults
|
|
60
|
+
} from "./course-config.js";
|
|
38
61
|
import {
|
|
39
62
|
resolveKnowledgeVersion
|
|
40
63
|
} from "./knowledge-version.js";
|
|
41
64
|
import {
|
|
65
|
+
loadAbbreviationRegistryInput,
|
|
42
66
|
loadCurriculumCatalog,
|
|
43
67
|
loadNotationDefinitions,
|
|
44
68
|
loadNotationRegistryInput,
|
|
@@ -51,7 +75,7 @@ import {
|
|
|
51
75
|
isLessonSlug,
|
|
52
76
|
lessonIdFromSlug
|
|
53
77
|
} from "./lesson-derivation.js";
|
|
54
|
-
const MANIFEST_SCHEMA_VERSION =
|
|
78
|
+
const MANIFEST_SCHEMA_VERSION = 14;
|
|
55
79
|
const REPOSITORY_ROOT = process.cwd();
|
|
56
80
|
const CONTENT_ROOT = join(REPOSITORY_ROOT, "content");
|
|
57
81
|
const MANIFEST_PATH = join(REPOSITORY_ROOT, "build", "manifest.json");
|
|
@@ -184,9 +208,10 @@ async function compileManifest(courseConfig) {
|
|
|
184
208
|
"course.config.ts is missing a complete `about` block (required since explico 0.3.0): `about: { version, authors: [{ name }], disclaimer, materialName?, summary?, changelogHref? }`. See docs/authoring/migrating.md (\xA7 0.2.x \u2192 0.3.0) in the installed package."
|
|
185
209
|
);
|
|
186
210
|
}
|
|
187
|
-
const [catalog, notationInput] = await Promise.all([
|
|
211
|
+
const [catalog, notationInput, abbreviationsInput] = await Promise.all([
|
|
188
212
|
loadCurriculumCatalog(),
|
|
189
|
-
loadNotationRegistryInput()
|
|
213
|
+
loadNotationRegistryInput(),
|
|
214
|
+
loadAbbreviationRegistryInput()
|
|
190
215
|
]);
|
|
191
216
|
const registry = buildNotationRegistry(notationInput);
|
|
192
217
|
const cardsByKey = new Map(
|
|
@@ -202,16 +227,54 @@ async function compileManifest(courseConfig) {
|
|
|
202
227
|
const bundlesByLesson = new Map(
|
|
203
228
|
registry.bundles.map((bundle) => [bundle.lessonId, bundle])
|
|
204
229
|
);
|
|
230
|
+
const assessmentsInput = catalog.assessments;
|
|
205
231
|
const curriculumIssues = validateCurriculum(catalog);
|
|
206
232
|
const alignmentIssues = validateNotationAlignment(registry, catalog);
|
|
207
|
-
const mathIssues =
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
233
|
+
const mathIssues = [
|
|
234
|
+
...gateContentMath({
|
|
235
|
+
notationInput,
|
|
236
|
+
assessments: assessmentsInput,
|
|
237
|
+
lessonAssessments: catalog.lessons.map((lesson) => ({
|
|
238
|
+
lessonId: lesson.id,
|
|
239
|
+
assessmentIds: lesson.assessments
|
|
240
|
+
}))
|
|
241
|
+
}),
|
|
242
|
+
...gateProseNotation(notationInput.lessons),
|
|
243
|
+
...gateProseNotationInAssessments(assessmentsInput),
|
|
244
|
+
...gateAbbreviations({
|
|
245
|
+
notationInput,
|
|
246
|
+
abbreviations: abbreviationsInput,
|
|
247
|
+
courseAbbreviations: courseConfig.abbreviations
|
|
248
|
+
}),
|
|
249
|
+
...gateHeadingLength(notationInput.lessons)
|
|
250
|
+
];
|
|
251
|
+
const notationLessonById = new Map(
|
|
252
|
+
notationInput.lessons.map((lesson) => [lesson.lessonId, lesson])
|
|
253
|
+
);
|
|
254
|
+
const assessmentInputById = new Map(
|
|
255
|
+
assessmentsInput.map((assessment) => [assessment.id, assessment])
|
|
256
|
+
);
|
|
257
|
+
const assessmentContent = [];
|
|
258
|
+
for (const lesson of catalog.lessons) {
|
|
259
|
+
if (lesson.assessments.length === 0) continue;
|
|
260
|
+
const lessonInput = notationLessonById.get(lesson.id);
|
|
261
|
+
if (!lessonInput) continue;
|
|
262
|
+
const scope = pageScope(lessonInput, cardsByKey);
|
|
263
|
+
for (const assessmentId of lesson.assessments) {
|
|
264
|
+
const assessment = assessmentInputById.get(assessmentId);
|
|
265
|
+
if (!assessment?.items) continue;
|
|
266
|
+
for (const item of assessment.items) {
|
|
267
|
+
if (!item.id) continue;
|
|
268
|
+
assessmentContent.push({
|
|
269
|
+
lessonId: lesson.id,
|
|
270
|
+
assessmentId,
|
|
271
|
+
itemId: item.id,
|
|
272
|
+
promptSegments: splitMixedContent(item.prompt ?? "", scope),
|
|
273
|
+
explanationSegments: splitMixedContent(item.explanation ?? "", scope)
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
215
278
|
const prerequisitesById = new Map(
|
|
216
279
|
catalog.competencies.map((competency) => [
|
|
217
280
|
competency.id,
|
|
@@ -245,7 +308,13 @@ async function compileManifest(courseConfig) {
|
|
|
245
308
|
const lessonIds = group.items.filter((item) => typeof item === "string").map(lessonIdFromSlug);
|
|
246
309
|
if (lessonIds.length === 0) continue;
|
|
247
310
|
partCursor += 1;
|
|
248
|
-
const used = {
|
|
311
|
+
const used = {
|
|
312
|
+
equations: 0,
|
|
313
|
+
tables: 0,
|
|
314
|
+
figures: 0,
|
|
315
|
+
diagrams: 0,
|
|
316
|
+
checks: 0
|
|
317
|
+
};
|
|
249
318
|
let chapterCursor = 0;
|
|
250
319
|
for (const lessonId of lessonIds) {
|
|
251
320
|
const body = bodyByLessonId.get(lessonId) ?? "";
|
|
@@ -257,6 +326,8 @@ async function compileManifest(courseConfig) {
|
|
|
257
326
|
used.equations += countDisplayEquations(body);
|
|
258
327
|
used.tables += countKeyedJsxFloats(body, { tagName: "Table" });
|
|
259
328
|
used.figures += countKeyedJsxFloats(body, { tagName: "Figure" });
|
|
329
|
+
used.diagrams += countKeyedJsxFloats(body, { tagName: "Diagram" });
|
|
330
|
+
used.checks += lessonMetaById.get(lessonId)?.assessments.length ?? 0;
|
|
260
331
|
}
|
|
261
332
|
}
|
|
262
333
|
const floatInputs = (kind) => notationInput.lessons.map((lesson) => {
|
|
@@ -295,6 +366,45 @@ async function compileManifest(courseConfig) {
|
|
|
295
366
|
Object.entries(table).map(([key, number]) => ({ key, number }))
|
|
296
367
|
])
|
|
297
368
|
);
|
|
369
|
+
const diagramValidation = validateDiagrams(floatInputs("diagrams"));
|
|
370
|
+
const diagramsBySlug = new Map(
|
|
371
|
+
Object.entries(diagramValidation.numbersBySlug).map(([slug, table]) => [
|
|
372
|
+
slug,
|
|
373
|
+
Object.entries(table).map(([key, number]) => ({ key, number }))
|
|
374
|
+
])
|
|
375
|
+
);
|
|
376
|
+
const checkValidation = validateKnowledgeChecks(
|
|
377
|
+
notationInput.lessons.map((lesson) => {
|
|
378
|
+
const placement = placementByLessonId.get(lesson.lessonId);
|
|
379
|
+
return {
|
|
380
|
+
lessonId: lesson.lessonId,
|
|
381
|
+
slug: lesson.lessonId.replace(/\./g, "/"),
|
|
382
|
+
file: lesson.source.file,
|
|
383
|
+
body: lesson.body,
|
|
384
|
+
assessmentIds: lessonMetaById.get(lesson.lessonId)?.assessments ?? [],
|
|
385
|
+
...placement ? {
|
|
386
|
+
numbering: {
|
|
387
|
+
prefix: placement.part,
|
|
388
|
+
offset: placement.numbering.checks
|
|
389
|
+
}
|
|
390
|
+
} : {}
|
|
391
|
+
};
|
|
392
|
+
})
|
|
393
|
+
);
|
|
394
|
+
const checksBySlug = new Map(
|
|
395
|
+
Object.entries(checkValidation.numbersBySlug).map(([slug, table]) => [
|
|
396
|
+
slug,
|
|
397
|
+
Object.entries(table).map(([key, number]) => ({ key, number }))
|
|
398
|
+
])
|
|
399
|
+
);
|
|
400
|
+
const rawFloatDiagnostics = validateRawFloats(
|
|
401
|
+
notationInput.lessons.map((lesson) => ({
|
|
402
|
+
lessonId: lesson.lessonId,
|
|
403
|
+
slug: lesson.lessonId.replace(/\./g, "/"),
|
|
404
|
+
file: lesson.source.file,
|
|
405
|
+
body: lesson.body
|
|
406
|
+
}))
|
|
407
|
+
);
|
|
298
408
|
const lessons = [];
|
|
299
409
|
for (const notationLesson of notationInput.lessons) {
|
|
300
410
|
const id = notationLesson.lessonId;
|
|
@@ -316,7 +426,9 @@ async function compileManifest(courseConfig) {
|
|
|
316
426
|
numbering: placementByLessonId.get(id)?.numbering ?? {
|
|
317
427
|
equations: 0,
|
|
318
428
|
tables: 0,
|
|
319
|
-
figures: 0
|
|
429
|
+
figures: 0,
|
|
430
|
+
diagrams: 0,
|
|
431
|
+
checks: 0
|
|
320
432
|
},
|
|
321
433
|
file: notationLesson.source.file,
|
|
322
434
|
title: frontmatter.title,
|
|
@@ -326,11 +438,13 @@ async function compileManifest(courseConfig) {
|
|
|
326
438
|
requires: [...meta.requires],
|
|
327
439
|
assumptions: [...meta.assumptions],
|
|
328
440
|
assessments: [...meta.assessments],
|
|
441
|
+
knowledgeChecks: checksBySlug.get(id.replace(/\./g, "/")) ?? [],
|
|
329
442
|
sources: deriveSources(notationLesson.body),
|
|
330
443
|
citations: lessonCitations(notationLesson.body),
|
|
331
444
|
equations: equationsBySlug.get(id.replace(/\./g, "/")) ?? [],
|
|
332
445
|
tables: tablesBySlug.get(id.replace(/\./g, "/")) ?? [],
|
|
333
446
|
figures: figuresBySlug.get(id.replace(/\./g, "/")) ?? [],
|
|
447
|
+
diagrams: diagramsBySlug.get(id.replace(/\./g, "/")) ?? [],
|
|
334
448
|
prereqEdges: sortEdges(
|
|
335
449
|
teaches.flatMap(
|
|
336
450
|
(to) => (prerequisitesById.get(to) ?? []).filter((from) => !teaches.includes(from)).map((from) => ({ from, to }))
|
|
@@ -356,7 +470,8 @@ async function compileManifest(courseConfig) {
|
|
|
356
470
|
catalog,
|
|
357
471
|
lintIgnore: loadLintIgnore(),
|
|
358
472
|
conventions: courseConfig.conventions,
|
|
359
|
-
...courseConfig.operators === void 0 ? {} : { operators: courseConfig.operators }
|
|
473
|
+
...courseConfig.operators === void 0 ? {} : { operators: courseConfig.operators },
|
|
474
|
+
...courseConfig.romanLabels === void 0 ? {} : { romanLabels: courseConfig.romanLabels }
|
|
360
475
|
});
|
|
361
476
|
const rawNotation = [...loadNotationDefinitions()].sort(
|
|
362
477
|
(a, b) => String(a.key).localeCompare(String(b.key))
|
|
@@ -364,6 +479,13 @@ async function compileManifest(courseConfig) {
|
|
|
364
479
|
const competencies = catalog.competencies.map((entry) => ({ ...entry })).sort(byId);
|
|
365
480
|
const sources = [...loadSourceRecords()].sort(byId);
|
|
366
481
|
const tracks = catalog.tracks.map((entry) => ({ ...entry })).sort(byId);
|
|
482
|
+
const abbreviations = abbreviationsInput.map((entry) => ({
|
|
483
|
+
key: entry.key,
|
|
484
|
+
abbr: entry.abbr,
|
|
485
|
+
expansion: entry.expansion,
|
|
486
|
+
aliases: entry.aliases,
|
|
487
|
+
...entry.domain === void 0 ? {} : { domain: entry.domain }
|
|
488
|
+
})).sort((a, b) => a.key.localeCompare(b.key));
|
|
367
489
|
const hashes = contentHashes();
|
|
368
490
|
const knowledgeChangelogPath = join(CONTENT_ROOT, "CHANGELOG.md");
|
|
369
491
|
const knowledge = resolveKnowledgeVersion({
|
|
@@ -397,6 +519,8 @@ async function compileManifest(courseConfig) {
|
|
|
397
519
|
};
|
|
398
520
|
return {
|
|
399
521
|
schemaVersion: MANIFEST_SCHEMA_VERSION,
|
|
522
|
+
lessonExports: normalizeLessonExports(courseConfig),
|
|
523
|
+
disclosureDefaults: normalizeDisclosureDefaults(courseConfig),
|
|
400
524
|
about,
|
|
401
525
|
notation: {
|
|
402
526
|
definitions: registry.definitions,
|
|
@@ -408,6 +532,7 @@ async function compileManifest(courseConfig) {
|
|
|
408
532
|
competencies,
|
|
409
533
|
sources,
|
|
410
534
|
tracks,
|
|
535
|
+
abbreviations,
|
|
411
536
|
sidebar,
|
|
412
537
|
lessons,
|
|
413
538
|
prereqEdges,
|
|
@@ -423,6 +548,15 @@ async function compileManifest(courseConfig) {
|
|
|
423
548
|
labels: figureValidation.labels,
|
|
424
549
|
numbersBySlug: figureValidation.numbersBySlug
|
|
425
550
|
},
|
|
551
|
+
diagrams: {
|
|
552
|
+
labels: diagramValidation.labels,
|
|
553
|
+
numbersBySlug: diagramValidation.numbersBySlug
|
|
554
|
+
},
|
|
555
|
+
checks: {
|
|
556
|
+
labels: checkValidation.labels,
|
|
557
|
+
numbersBySlug: checkValidation.numbersBySlug
|
|
558
|
+
},
|
|
559
|
+
assessmentContent,
|
|
426
560
|
diagnostics: {
|
|
427
561
|
curriculum: curriculumIssues,
|
|
428
562
|
notation: registry.diagnostics,
|
|
@@ -432,7 +566,10 @@ async function compileManifest(courseConfig) {
|
|
|
432
566
|
equations: equationValidation.diagnostics,
|
|
433
567
|
tables: tableValidation.diagnostics,
|
|
434
568
|
figures: figureValidation.diagnostics,
|
|
435
|
-
|
|
569
|
+
diagrams: diagramValidation.diagnostics,
|
|
570
|
+
checks: checkValidation.diagnostics,
|
|
571
|
+
knowledge: knowledge.diagnostics,
|
|
572
|
+
rawFloats: rawFloatDiagnostics
|
|
436
573
|
},
|
|
437
574
|
hashes
|
|
438
575
|
};
|
|
@@ -9,7 +9,11 @@ import '../reference/equations-validate.js';
|
|
|
9
9
|
import '../reference/tables-validate.js';
|
|
10
10
|
import '../reference/keyed-float-validate.js';
|
|
11
11
|
import '../reference/figures-validate.js';
|
|
12
|
+
import '../reference/diagrams-validate.js';
|
|
13
|
+
import '../reference/knowledge-checks-validate.js';
|
|
14
|
+
import '../reference/raw-floats-validate.js';
|
|
12
15
|
import '../reference/gate-math.js';
|
|
16
|
+
import '../reference/mixed-content.js';
|
|
13
17
|
import '../reference/gloss.js';
|
|
14
18
|
import './knowledge-version.js';
|
|
15
19
|
import './lesson-derivation.js';
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The abbreviations a course treats as common, given its `course.config.ts`
|
|
3
|
+
* `abbreviations` block: the shipped baseline, plus `allow`, minus `deny`.
|
|
4
|
+
*
|
|
5
|
+
* @param {{ allow?: readonly string[], deny?: readonly string[] } | undefined} abbreviations
|
|
6
|
+
* @returns {Set<string>}
|
|
7
|
+
*/
|
|
8
|
+
export function effectiveAbbreviationAllowlist(abbreviations: {
|
|
9
|
+
allow?: readonly string[];
|
|
10
|
+
deny?: readonly string[];
|
|
11
|
+
} | undefined): Set<string>;
|
|
12
|
+
/** True when `token` needs no abbreviations-table entry for this course. */
|
|
13
|
+
export function isBaseAbbreviation(token: any, abbreviations: any): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* The base abbreviation baseline: acronyms and dotted abbreviations every
|
|
16
|
+
* reader is assumed to know, so a lesson never has to introduce them with an
|
|
17
|
+
* abbreviations-table entry. The `\operatorname{…}` / `\mathrm{…}` twins are
|
|
18
|
+
* `base-operators.mjs` (rule N8) / `base-roman-labels.mjs` (rule N10); this is
|
|
19
|
+
* the plain-prose equivalent for rule N14.
|
|
20
|
+
*
|
|
21
|
+
* Kept intentionally small, same rationale as `BASE_ROMAN_LABELS`: a
|
|
22
|
+
* course-specific acronym (`OLS`, `SSR`, `MSE`) is exactly the thing a reader
|
|
23
|
+
* benefits from having spelled out, so it belongs in the course's own
|
|
24
|
+
* `content/abbreviations/*.md` table, not here. This baseline only holds
|
|
25
|
+
* abbreviations so common in ordinary English/academic writing that spelling
|
|
26
|
+
* them out on every course would be noise, not help. A course adds a real
|
|
27
|
+
* one via `abbreviations.allow` in `course.config.ts`, the same shape as
|
|
28
|
+
* `operators.allow` / `romanLabels.allow`.
|
|
29
|
+
*/
|
|
30
|
+
export const BASE_ABBREVIATIONS: Readonly<Set<string>>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The base abbreviation baseline: acronyms and dotted abbreviations every
|
|
3
|
+
* reader is assumed to know, so a lesson never has to introduce them with an
|
|
4
|
+
* abbreviations-table entry. The `\operatorname{…}` / `\mathrm{…}` twins are
|
|
5
|
+
* `base-operators.mjs` (rule N8) / `base-roman-labels.mjs` (rule N10); this is
|
|
6
|
+
* the plain-prose equivalent for rule N14.
|
|
7
|
+
*
|
|
8
|
+
* Kept intentionally small, same rationale as `BASE_ROMAN_LABELS`: a
|
|
9
|
+
* course-specific acronym (`OLS`, `SSR`, `MSE`) is exactly the thing a reader
|
|
10
|
+
* benefits from having spelled out, so it belongs in the course's own
|
|
11
|
+
* `content/abbreviations/*.md` table, not here. This baseline only holds
|
|
12
|
+
* abbreviations so common in ordinary English/academic writing that spelling
|
|
13
|
+
* them out on every course would be noise, not help. A course adds a real
|
|
14
|
+
* one via `abbreviations.allow` in `course.config.ts`, the same shape as
|
|
15
|
+
* `operators.allow` / `romanLabels.allow`.
|
|
16
|
+
*/
|
|
17
|
+
export const BASE_ABBREVIATIONS = Object.freeze(
|
|
18
|
+
new Set([
|
|
19
|
+
// dotted Latin/English abbreviations
|
|
20
|
+
'e.g.',
|
|
21
|
+
'i.e.',
|
|
22
|
+
'etc.',
|
|
23
|
+
'cf.',
|
|
24
|
+
'vs.',
|
|
25
|
+
'a.m.',
|
|
26
|
+
'p.m.',
|
|
27
|
+
'a.k.a.',
|
|
28
|
+
'et al.',
|
|
29
|
+
// generic acronyms with no domain-specific meaning in this engine
|
|
30
|
+
'AI',
|
|
31
|
+
'ID',
|
|
32
|
+
'OK',
|
|
33
|
+
'URL',
|
|
34
|
+
'FAQ',
|
|
35
|
+
'US',
|
|
36
|
+
'UK',
|
|
37
|
+
'EU',
|
|
38
|
+
]),
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The abbreviations a course treats as common, given its `course.config.ts`
|
|
43
|
+
* `abbreviations` block: the shipped baseline, plus `allow`, minus `deny`.
|
|
44
|
+
*
|
|
45
|
+
* @param {{ allow?: readonly string[], deny?: readonly string[] } | undefined} abbreviations
|
|
46
|
+
* @returns {Set<string>}
|
|
47
|
+
*/
|
|
48
|
+
export function effectiveAbbreviationAllowlist(abbreviations) {
|
|
49
|
+
const allow = abbreviations?.allow ?? [];
|
|
50
|
+
const deny = new Set(abbreviations?.deny ?? []);
|
|
51
|
+
const effective = new Set(BASE_ABBREVIATIONS);
|
|
52
|
+
for (const name of allow) effective.add(name);
|
|
53
|
+
for (const name of deny) effective.delete(name);
|
|
54
|
+
return effective;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** True when `token` needs no abbreviations-table entry for this course. */
|
|
58
|
+
export function isBaseAbbreviation(token, abbreviations) {
|
|
59
|
+
return effectiveAbbreviationAllowlist(abbreviations).has(token);
|
|
60
|
+
}
|
|
@@ -36,3 +36,18 @@ export function isBaseOperator(name: any, operators: any): boolean;
|
|
|
36
36
|
* Matching is case-sensitive.
|
|
37
37
|
*/
|
|
38
38
|
export const BASE_OPERATORS: Readonly<Set<string>>;
|
|
39
|
+
/**
|
|
40
|
+
* The subset of {@link BASE_OPERATORS} that must never appear with a bare
|
|
41
|
+
* (unparenthesized) argument: every trigonometric, inverse-trigonometric, and
|
|
42
|
+
* hyperbolic function, plus the exponential and logarithms (N13). `\sin x`
|
|
43
|
+
* leaves how far the argument extends ambiguous the moment anything follows
|
|
44
|
+
* it (`\sin x + 1` — is the `+1` inside or outside the sine?); `\sin(x)`
|
|
45
|
+
* never does, even for a single-symbol argument. Kept separate from
|
|
46
|
+
* `BASE_OPERATORS`: this is a formatting rule layered on top of the "does it
|
|
47
|
+
* need a notation card" question that set answers, so an operator can be
|
|
48
|
+
* universal (N8) and still owe a parenthesized argument. Enforced by
|
|
49
|
+
* `resolveMathGlyphs` in `math-glyphs.mjs`, which every rendered-math context
|
|
50
|
+
* (lesson body, notation `formula`, assessments, `Diagram` nodes, lab math)
|
|
51
|
+
* already calls.
|
|
52
|
+
*/
|
|
53
|
+
export const PARENTHESIZED_FUNCTIONS: Readonly<Set<string>>;
|
|
@@ -81,6 +81,46 @@ export const BASE_OPERATORS = Object.freeze(
|
|
|
81
81
|
]),
|
|
82
82
|
);
|
|
83
83
|
|
|
84
|
+
/**
|
|
85
|
+
* The subset of {@link BASE_OPERATORS} that must never appear with a bare
|
|
86
|
+
* (unparenthesized) argument: every trigonometric, inverse-trigonometric, and
|
|
87
|
+
* hyperbolic function, plus the exponential and logarithms (N13). `\sin x`
|
|
88
|
+
* leaves how far the argument extends ambiguous the moment anything follows
|
|
89
|
+
* it (`\sin x + 1` — is the `+1` inside or outside the sine?); `\sin(x)`
|
|
90
|
+
* never does, even for a single-symbol argument. Kept separate from
|
|
91
|
+
* `BASE_OPERATORS`: this is a formatting rule layered on top of the "does it
|
|
92
|
+
* need a notation card" question that set answers, so an operator can be
|
|
93
|
+
* universal (N8) and still owe a parenthesized argument. Enforced by
|
|
94
|
+
* `resolveMathGlyphs` in `math-glyphs.mjs`, which every rendered-math context
|
|
95
|
+
* (lesson body, notation `formula`, assessments, `Diagram` nodes, lab math)
|
|
96
|
+
* already calls.
|
|
97
|
+
*/
|
|
98
|
+
export const PARENTHESIZED_FUNCTIONS = Object.freeze(
|
|
99
|
+
new Set([
|
|
100
|
+
// exponential / logarithms
|
|
101
|
+
'exp',
|
|
102
|
+
'log',
|
|
103
|
+
'ln',
|
|
104
|
+
'lg',
|
|
105
|
+
// trigonometric
|
|
106
|
+
'sin',
|
|
107
|
+
'cos',
|
|
108
|
+
'tan',
|
|
109
|
+
'cot',
|
|
110
|
+
'sec',
|
|
111
|
+
'csc',
|
|
112
|
+
// inverse trigonometric
|
|
113
|
+
'arcsin',
|
|
114
|
+
'arccos',
|
|
115
|
+
'arctan',
|
|
116
|
+
// hyperbolic
|
|
117
|
+
'sinh',
|
|
118
|
+
'cosh',
|
|
119
|
+
'tanh',
|
|
120
|
+
'coth',
|
|
121
|
+
]),
|
|
122
|
+
);
|
|
123
|
+
|
|
84
124
|
/**
|
|
85
125
|
* The operators a course treats as common, given its `course.config.ts`
|
|
86
126
|
* `operators` block: the shipped baseline, plus `allow`, minus `deny`.
|