dexin-content 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,12 +10,18 @@
10
10
  - **Collection**:集合声明、发现、批量编译与增量变化检测
11
11
  - **Diff**:Canonical JSON 序列化与对象级差异原语(Golden/Regress 管线使用)
12
12
 
13
+ ## 为内容作者
14
+
15
+ - **格式规范**:[docs/content-format.md](./docs/content-format.md) —— 目录结构、front-matter、块/容器/数学语法、错误码、自检命令、投稿 SOP(§14)
16
+ - **示例课**:[example/](./example/README.md) —— 可直接编译通过的最小内容仓样例
17
+ - **社区投稿**:[proposals/](./proposals/README.md) —— 课程稿件投放区,PR + 门禁校验(`npm run validate:proposals`)+ 人审后迁入私有成品仓
18
+
13
19
  ## 目录范围
14
20
 
15
21
  dexin-content 是**通用层**:
16
22
  - `core/` 编译核心(types / compiler / parser / artifact)
17
23
  - `domains/lesson/` 内置 lesson 域 parser(其余域由宿主自行注入)
18
- - `cli/` flow 编排(build / package / validate / check / serve)
24
+ - `cli/` flow 编排(`flowBuild` / `flowPackage` / `flowValidate` / `flowCheck` / `flowServe` 库 API;**CLI 子命令仅有 `build` 与 `help`**,其余 flow 经子路径作库消费)
19
25
  - `scripts/` 仓内脚本(不在发布包 files 内,仅供本地开发/测试;也不作为公开 API)
20
26
 
21
27
  宿主应用负责域扩展(DomainParser)、编排脚本与内容文件所有权,通过 `exports` 里的子路径消费本 package。
@@ -25,10 +31,6 @@ dexin-content 是**通用层**:
25
31
  > 需要 Node.js ≥ 20。Package 以 TypeScript 源码形式分发(`*.ts`),宿主应通过 `tsx`、Nuxt/Vite 等支持 TS 解析的工具链使用。
26
32
 
27
33
  ```bash
28
- # Gitee 源(当前发布前)
29
- npm install git+https://gitee.com/cuizhn/dexin-content.git
30
-
31
- # 发布到 npm 后
32
34
  npm install dexin-content
33
35
  ```
34
36
 
@@ -39,9 +41,9 @@ npm install dexin-content
39
41
  | 子路径 | 暴露内容 |
40
42
  |---|---|
41
43
  | `.` | 门面:核心类型 + `DomainParserRegistry` / `compile` + `ArtifactStore` / `ContentQuery` + `defineCollection / resolveCollections / discover / compileCollections` |
42
- | `./core/types` | 类型:`Inline` / `DocumentBlock` / `DocumentContent` / `DocumentIdentity` / `PositiveArtifact` / `Artifact` / `Meta` / `ParseError` / `DomainParser` / `Schema` / `ParseContext` |
44
+ | `./core/types` | 类型:`Inline` 家族(`TextInline` / `BoldInline` / `ItalicInline` / `CodeInline` / `LinkInline` / `FormulaInline`) / `Identity` / `DocumentIdentity` / `StructuredIdentity` / `Meta` / `PositiveArtifact` / `Artifact` / `ParseError` / `ParseContext` / `Schema` / `DomainParser` / `DomainName`(LessonAST 块类型在 `./core/types/lessonAST`) |
43
45
  | `./core/compiler` | `compile`、`CompileInput`、`CompileResult`、`DomainParserRegistry` |
44
- | `./core/discovery` | `buildDocumentIdentity` / `normaliseRel` / `readSourceFile` / `sanitizeSourceText` / `mkLineEndingError` |
46
+ | `./core/discovery` | `buildDocumentIdentity` / `buildStructuredIdentity` / `normaliseRel` / `readSourceFile` / `IdentityKind` / `CollectionConfig` / `SourceFile` |
45
47
  | `./core/frontmatter` | `splitFrontmatter` / `parseFrontmatter` / `validateSchema` / `projectMeta` |
46
48
  | `./store` | `ArtifactStore` 接口 + `createFsArtifactStore` / `createMemoryArtifactStore` + `ContentIndex` / `IndexEntry` |
47
49
  | `./collection` | `defineCollection` / `resolveCollections` / `compileCollections` / `recompileChanged` / `createLocalSource` / `createMemorySource` / `SourceAdapter` / `ResolvedCollection` / `CollectionDefinition` |
@@ -60,46 +62,43 @@ npm install dexin-content
60
62
 
61
63
  ## 快速示例
62
64
 
65
+ 最简用法是走 CLI(仅编译内置 lesson 域):
66
+
67
+ ```bash
68
+ # 单文件:AST 打印到 stdout,失败非零退出
69
+ npx dexin-content build example/lessons/getting-started/hello-lesson/minimal.md --domain lesson
70
+
71
+ # 整树:编译 + 按 manifest 打包 ContentPackage
72
+ npx dexin-content build example --manifest example/content-manifest.json --out /tmp/dexin-out
73
+ ```
74
+
75
+ 库 API(宿主自行编排时)用内置 `lessonParser` 经 `DomainParserRegistry` 注入:
76
+
63
77
  ```ts
64
- import {
65
- defineCollection,
66
- resolveCollections,
67
- createLocalSource,
68
- compileCollections,
69
- } from 'dexin-content/collection'
70
- import { createFsArtifactStore } from 'dexin-content/store'
71
- import { ContentQuery } from 'dexin-content/query'
72
- import { DomainParserRegistry } from 'dexin-content/core/compiler'
73
- import type {
74
- DomainParser,
75
- DocumentContent,
76
- ParseContext,
77
- } from 'dexin-content/core/types'
78
-
79
- // 1) 注入你的域 parser;parser 决定中性 AST 如何映射到目标 Artifact 内容结构
80
- const documentParser: DomainParser = {
81
- domain: 'document',
82
- parse(content: DocumentContent, _ctx: ParseContext) {
83
- return { version: 1, blocks: content.blocks as unknown[] }
84
- },
85
- }
86
- const registry = new DomainParserRegistry()
87
- registry.register(documentParser)
78
+ import { DomainParserRegistry, compile } from 'dexin-content/core/compiler'
79
+ import { lessonParser } from 'dexin-content/domains/lesson'
80
+ import { buildLessonIdentity } from 'dexin-content/domains/lesson'
88
81
 
89
- // 2) 声明集合 + 编译
90
- const collections = resolveCollections(
91
- [defineCollection({ name: 'legal', source: 'legal', domain: 'document' })],
92
- '/path/to/content/root',
82
+ const registry = new DomainParserRegistry()
83
+ registry.register(lessonParser)
84
+
85
+ const result = compile(
86
+ {
87
+ fixture: 'getting-started/hello-lesson/minimal',
88
+ domain: 'lesson',
89
+ identity: buildLessonIdentity('minimal', 'getting-started', 'hello-lesson', 'lessons/getting-started/hello-lesson/minimal.md'),
90
+ source: '---\ntitle: 最小的一节课\norder: 1\n---\n\n## 这就是全部了\n',
91
+ file: 'lessons/getting-started/hello-lesson/minimal.md',
92
+ },
93
+ registry,
93
94
  )
94
- const source = createLocalSource('/path/to/content/root')
95
- const store = createFsArtifactStore('/path/to/artifact-store')
96
- await compileCollections(source, collections, registry, store)
97
95
 
98
- // 3) 运行时只读查询(不解析任何 Markdown)
99
- const query = new ContentQuery(store)
100
- const entry = await query.byPath('/legal/privacy')
96
+ if (result.kind === 'error') throw result.error
97
+ else console.log(result.artifact.content.blocks)
101
98
  ```
102
99
 
100
+ `compile()` 永不抛异常,失败以 `{ kind: 'error', error }` 返回。域扩展由宿主注入其它 `DomainParser`。
101
+
103
102
  ## 开发
104
103
 
105
104
  ```bash
@@ -0,0 +1,191 @@
1
+ # 课程内容格式规范(Lesson Markdown)
2
+
3
+ 本仓 `example/` 下有一节按本规范写成的示例课,可直接对照(见 [example/README.md](../example/README.md))。
4
+
5
+ 适用对象:为 dexinlabs 生态编写数学思维课的作者。课程正文是带 front-matter 的 Markdown,经 `dexin-content` 编译为 LessonAST。本规范以工具链代码实际行为为准(dexin-content v0.3 时代);文中「代码」路径均相对本仓仓根。
6
+
7
+ 总原则:**白名单之外即报错**。编译器对块级结构 fail-fast——写了不允许的语法会编译失败而不是静默降级,这是为了让作者当场发现问题。
8
+
9
+ ---
10
+
11
+ ## 1. 目录与文件
12
+
13
+ ```text
14
+ <内容仓根>/
15
+ ├── content-manifest.json # taxonomy 骨架(courses/topics/chapters),无逐课条目
16
+ └── lessons/
17
+ └── <topic>/<chapter>/<slug>.md # 固定三层,不再递归
18
+ ```
19
+
20
+ - 一课 = 一个 `.md` 文件;`<topic>/<chapter>` 目录名即二者 slug;文件名去 `.md` 即课 slug。
21
+ - 课身份:`id = <topic>/<chapter>/<slug>`,URL path = `/` + id(`domains/lesson/identity.ts`)。
22
+ - 新增一课 = 直接建 md 文件;只有增删 topic/chapter 才改 `content-manifest.json`。
23
+ - `index.md` 被扫描器跳过;`lessons/` 或 topic 目录下直接放的 md 不会被扫到。
24
+ - 文件系统排序是字母序,**不等于展示顺序**——展示顺序由 front-matter `order` 决定(§2)。
25
+ - 编码要求:**UTF-8 无 BOM、LF 行尾**。BOM 会使 front-matter 识别失败(整文件被当成无 front-matter);含 `\r` 的源在通用 collection 路径直接报 `LINE_ENDING_CONTAMINATION`。
26
+
27
+ ## 2. Front-matter
28
+
29
+ 以第一行精确的 `---` 开始、以单独一行 `---` 结束,中间为合法 YAML(`core/parser/frontmatter.ts`)。
30
+
31
+ 字段模型是 **SCHEMA-FREE**:
32
+
33
+ - 所有**标量**字段(string / number / boolean)原样进入产物 `meta`;未知字段不报错。
34
+ - **非标高量一律静默丢弃**:数组、对象、null、多行字符串等不会报错,但也拿不到——需要结构化信息时请改用正文容器(§5)。
35
+
36
+ 约定字段(消费方依赖,务必写):
37
+
38
+ | 字段 | 类型 | 说明 | 缺失后果 |
39
+ | ------- | ------ | --------------------------------------------------- | --------------------------------------------- |
40
+ | `title` | string | 课标题 | 打包时回退为 slug;包校验(flowValidate)报错 |
41
+ | `order` | number | 章内展示顺序(小→大) | 按 0 处理,排序失效 |
42
+
43
+ ## 3. 标题与分节
44
+
45
+ | Markdown | 行为 |
46
+ | ------------ | ----------------------------------------------------------- |
47
+ | `# h1` | **吸收丢弃**(标题已由 front-matter `title` 提供),不报错、不出现在产物中——因此不要写 |
48
+ | `## h2` | 映射为 heading level 1,并**新开一个 section**,其后内容归入该节 |
49
+ | `### h3` | level 2,留在当前 section 内 |
50
+ | `#### h4` | level 3 |
51
+ | `##### h5` | level 4 |
52
+ | `###### h6`+ | 编译失败 `LESSON_HEADING_DEPTH_UNDEFINED` |
53
+
54
+ 第一个 h2 之前的块平铺在顶层(不包 section)。一节课建议以 h2 组织小节。规则见 `domains/lesson/parser.ts`。
55
+
56
+ ## 4. 块级语法
57
+
58
+ 产物 block 类型共 14 类(其中 `image` 暂无可用写法,见下);作者可用的书写方式如下(`section` 由 h2 自动产生,无书写语法):
59
+
60
+ | 写法 | 产物 block | 注意 |
61
+ | ---------------- | ------------------------------------------ | ----------------------------------------------------------- |
62
+ | 普通段落 | `paragraph` | — |
63
+ | `> 引用` | `quote` | 内部可放段落/列表等子块 |
64
+ | 独占一行 `---` | `divider` | 与上文之间必须留空行,否则会被解析为 setext 标题下划线 |
65
+ | `- 项` / `1. 项` | `list` | **列表项内只允许一个段落**:嵌套列表、代码块等编译失败 |
66
+ | GFM 管道表格 | `table`(headers + rows) | 单元格走内联规则(§7) |
67
+ | ` ```lang ` 围栏 | `code`(`lang` 可为空串) | — |
68
+ | 块级数学 | `formula`(`display: true`) | 见 §6 |
69
+ | `:::hint` 等 | `hint` / `definition` / `example` / `question` | 见 §5 |
70
+
71
+ **图片暂不可用**:独立图片行与行内图片都会被降级为纯文本(只剩 alt 文字),请不要写 `![...]`,等待渲染侧支持后再开放。
72
+
73
+ ## 5. 容器指令(教学结构的核心)
74
+
75
+ 开栏 `:::名称{key="value"}`,闭栏独占一行 `:::`。容器内部可嵌套任意块(含数学、列表、嵌套段落)。**合法容器只有以下 4 个**(`domains/lesson/blocks.ts`):
76
+
77
+ ```markdown
78
+ :::hint{level="tip"}
79
+ 推荐写法。level 取值:`info` | `tip` | `warning` | `danger` | `reflect`。
80
+ 缺失或非法值编译失败(`LESSON_INVALID_HINT_LEVEL`)。
81
+ :::
82
+
83
+ :::definition{term="一元一次方程"}
84
+ 含有一个未知数、且未知数次数为一的整式方程。
85
+ :::
86
+
87
+ :::example{title="解方程三步法"}
88
+ `title` 可选。
89
+ :::
90
+
91
+ :::question{hint="先移项,再系数化为 1"}
92
+ 题干与作答要求写在容器体内(体即 prompt,可为任意块)。
93
+ 除 `hint` 外的属性会被静默丢弃——**不要写 `{title=…}`**。
94
+ :::
95
+ ```
96
+
97
+ `:::note`、`:::tip` 之类一律编译失败(`LESSON_UNKNOWN_CONTAINER`)。
98
+
99
+ ## 6. 数学
100
+
101
+ - **行内**:`$x + 1 = 2$` → inline `math{latex}`。
102
+ - **块级、两种等价写法**(均产出 `formula{display:true}`):
103
+
104
+ ```markdown
105
+ 单行写法:独占一段、前后空行、段内无其它文字。
106
+
107
+ $$ax + b = 0 \quad (a \neq 0)$$
108
+
109
+ 多行写法:
110
+
111
+ $$
112
+ \frac{9}{5}C + 32 = F
113
+ $$
114
+ ```
115
+
116
+ 单行 `$$…$$` 若与文字同段则回落为行内数学;单个 `$` 永远不会变块级。
117
+
118
+ ## 7. 内联
119
+
120
+ 可用:`**粗体**`、`*斜体*`、`` `代码` ``、`[文字](https://url)`、行内数学、软/硬换行。
121
+
122
+ 避免(不报错但产出坏味道):
123
+
124
+ - 行内 HTML(如 `<b>`):降级为字面文本;
125
+ - 引用式链接 `[ref][id]`:会变成一个 url 为 `id` 的坏链接——请始终用行内 `[文字](url)`;
126
+ - 图片(§4)。
127
+
128
+ ## 8. 明确禁止(编译失败)
129
+
130
+ 块级原始 HTML(`<div>`…)、脚注(`[^1]` 及定义块)、链接引用定义(`[id]: url`)、列表项内的嵌套块、h6、未知容器、非法 hint level。错误码全表见 §10。
131
+
132
+ ## 9. 产物结构(选读)
133
+
134
+ 每课编译为 Artifact:`{ fixture, domain:'lesson', identity, meta, content:{version:1, blocks} }`。`content.blocks` 即 LessonAST,节点契约见 `core/types/lessonAST.ts`(类型即文档:`Block` 联合 15 成员 = 14 类产物 block + 中性层临时节点 `container`)。
135
+
136
+ ## 10. 校验错误码
137
+
138
+ | 错误码 | 触发 |
139
+ | --- | --- |
140
+ | `SCHEMA_VALIDATION_FAILED` | front-matter YAML 解析失败 |
141
+ | `LINE_ENDING_CONTAMINATION` | 源文件含 `\r`(collection 路径) |
142
+ | `MDAST_UNSUPPORTED_NODE` | 块级语法越出 §4/§8 白名单;列表项内嵌套块 |
143
+ | `LESSON_HEADING_DEPTH_UNDEFINED` | h6 及更深 |
144
+ | `LESSON_UNKNOWN_CONTAINER` | `:::` 容器名不在 4 类之内 |
145
+ | `LESSON_INVALID_HINT_LEVEL` | hint level 非法 |
146
+ | `COMPILER_NO_DOMAIN_PARSER` | 宿主未注册 lesson 域(工具链集成问题,与作者无关) |
147
+ | `UNKNOWN_ERROR` | 兜底 |
148
+
149
+ ## 11. 自检
150
+
151
+ 写完后在内容仓根逐文件自检(失败 → stderr + **非零退出码**,适合 CI):
152
+
153
+ ```bash
154
+ npx dexin-content build lessons/<topic>/<chapter>/<slug>.md --domain lesson
155
+ ```
156
+
157
+ 省略 `--out` 时产物 AST 打印到 stdout,可肉眼核对结构;`--out <file.json>` 则落盘。
158
+
159
+ 目录整体编译(发布链路,非作者门禁):
160
+
161
+ ```bash
162
+ npx dexin-content build <内容仓根> --manifest content-manifest.json --out <输出目录>
163
+ ```
164
+
165
+ > ⚠ 已知行为:目录模式对编译失败的文件只打印 `✗ <路径>: <错误码>` 后继续,整体退出码仍为 0。**作者/CI 门禁请以单文件模式为准**。
166
+
167
+ ## 12. 与宿主的关系
168
+
169
+ 本规范只约束「Markdown → LessonAST」这一段。产物如何打包为 ContentPackage、渲染成什么样式、进度数据如何存储,由闭源宿主(dexinlabs)决定,不在本规范范围。
170
+
171
+ ## 13. 已知工具链不一致(待修,不改变本规范效力)
172
+
173
+ - `core/types/lessonAST.ts` 的常量 `HINT_LEVELS` 缺 `reflect`;实际校验枚举以 `domains/lesson/blocks.ts` 的 `LESSON_HINT_LEVELS`(5 级)为准。
174
+ - 目录模式对失败文件仍 exit 0(§11)计划在后续版本提供失败计数退出。
175
+
176
+ ## 14. 投稿与迁入 SOP
177
+
178
+ 本工具链开源共建的对象是「怎么写课」(规范/示例/校验器),成品课程内容保存在**私有成品仓**,不公开。两条投稿路径:
179
+
180
+ **A. 社区投稿(proposals/)**
181
+
182
+ 1. **撰写**:按本规范写课,放入本仓 [`proposals/<topic>/<chapter>/<slug>.md`](../proposals/README.md),本地自检(§11)。
183
+ 2. **提 PR**:gitee 网页端编辑 Markdown 亦可;填写 PR 模板的自查与声明项。
184
+ 3. **门禁校验**:合并前必须格式全绿。投稿人本地跑 `npm run validate:proposals`(或 `npx dexin-content@latest` 逐文件,见 §11),维护者合并前复跑同一脚本。校验器 = 单文件模式编译,任一失败即非零退出。
185
+ 4. **人审**:门禁全绿后由维护者 PR review,审教学正确性与结构合理性(格式问题门禁已拦截)。
186
+ 5. **迁入(单向)**:通过后由维护者将稿件迁入私有成品仓——正式 topic/chapter/slug 以成品仓目录与 `content-manifest.json` 骨架为准(可能与投稿时不同,会在 review 中说明)——随后删除本仓 `proposals/` 中的对应文件。下次内容构建时上线。
187
+ 6. **授权**:投稿即视为原创并同意被收录评估;内容版权归作者,收录成品仓的具体授权条款在迁入前另行确认(本仓 Apache-2.0 许可证覆盖的是代码与文档,不自动覆盖课程文字内容)。
188
+
189
+ **B. 签约/受雇作者**
190
+
191
+ 不进公共投稿区:直接对私有成品仓有写权限,以内部 PR 提交与评审。格式校验同样适用(`npx dexin-content@latest build <文件> --domain lesson`)。
@@ -0,0 +1,37 @@
1
+ # example/ — 示例课
2
+
3
+ 这是按 [格式规范](../docs/content-format.md) 写成的最小内容仓样例:
4
+
5
+ ```text
6
+ example/
7
+ ├── content-manifest.json # taxonomy 骨架(courses/topics/chapters)
8
+ └── lessons/
9
+ └── getting-started/hello-lesson/
10
+ ├── minimal.md # 最小的一节课(order 1)
11
+ └── syntax-tour.md # 全部合法语法速览(order 2)
12
+ ```
13
+
14
+ ## 自检
15
+
16
+ 在本仓仓根(或任何安装了 `dexin-content` 的内容仓根)执行:
17
+
18
+ ```bash
19
+ # 整树编译 + 打包(产物写到临时目录,不要写进 example/)
20
+ npx dexin-content build example \
21
+ --manifest example/content-manifest.json \
22
+ --out /tmp/dexin-example-out
23
+
24
+ # 单文件自检(AST 打印到 stdout;失败时非零退出码)
25
+ npx dexin-content build example/lessons/getting-started/hello-lesson/syntax-tour.md --domain lesson
26
+ ```
27
+
28
+ 预期输出:
29
+
30
+ ```text
31
+ Build complete: 2 artifacts in ...ms
32
+ Packaged manifest.json: 2 lessons, 0 pages
33
+ ```
34
+
35
+ 任何 `✗ <路径>: <错误码>` 行都表示该文件编译失败,含义见 [格式规范 §10](../docs/content-format.md#10-校验错误码)。
36
+
37
+ > 提示:`--out` 缺省时目录模式会把产物写进 `example/output/`。该目录被 gitignore 与打包排除,但请尽量显式指定临时输出目录,保持示例目录干净。
@@ -0,0 +1,16 @@
1
+ {
2
+ "courses": [
3
+ { "slug": "math-thinking", "title": "数学思维", "order": 1 }
4
+ ],
5
+ "topics": [
6
+ { "slug": "getting-started", "title": "上手指南", "order": 1 }
7
+ ],
8
+ "chapters": [
9
+ {
10
+ "slug": "hello-lesson",
11
+ "title": "示例课",
12
+ "order": 1,
13
+ "topic_slug": "getting-started"
14
+ }
15
+ ]
16
+ }
@@ -0,0 +1,10 @@
1
+ ---
2
+ title: 最小的一节课
3
+ order: 1
4
+ ---
5
+
6
+ ## 这就是全部了
7
+
8
+ 一节能编译通过的课,最少只需要:front-matter 里的 `title` 和 `order`,加正文段落。
9
+
10
+ 一个 h2 标题会开启一个小节,下面的内容都归入它。
@@ -0,0 +1,74 @@
1
+ ---
2
+ title: 语法速览
3
+ order: 2
4
+ ---
5
+
6
+ ## 标题与分节
7
+
8
+ `##` 开一个新小节,`###` 是小节内的次级标题,`####`、`#####` 依次更深。不要用 `#`(会被吸收)也不要用到第六级标题。
9
+
10
+ ### 一个三级标题
11
+
12
+ 它留在当前小节内,不会另起一节。
13
+
14
+ ## 列表
15
+
16
+ - 无序列表项一
17
+ - 无序列表项二
18
+
19
+ 1. 有序列表项一
20
+ 2. 有序列表项二
21
+
22
+ 注意:列表项内只能放一个段落,不要在列表项里再套列表或代码块。
23
+
24
+ ## 表格
25
+
26
+ | 未知数个数 | 最高次数 | 是否一元一次 |
27
+ | --- | --- | --- |
28
+ | 1 | 1 | 是 |
29
+ | 1 | 2 | 否 |
30
+ | 2 | 1 | 否 |
31
+
32
+ ## 代码围栏
33
+
34
+ ```text
35
+ 步骤一:去分母
36
+ 步骤二:去括号
37
+ 步骤三:移项
38
+ ```
39
+
40
+ ## 引用
41
+
42
+ > 理解优先,而非记忆步骤。
43
+
44
+ ---
45
+
46
+ ## 数学
47
+
48
+ 行内数学直接写在句子中,例如 $ax + b = 0$(其中 $a \neq 0$)。
49
+
50
+ 块级数学推荐独占一行:
51
+
52
+ $$x = -\frac{b}{a}$$
53
+
54
+ ## 教学容器
55
+
56
+ 四种容器承载教学结构,闭栏统一用独占一行的 `:::`:
57
+
58
+ :::definition{term="一元一次方程"}
59
+ 只含一个未知数、且未知数最高次数为 1 的整式方程。
60
+ :::
61
+
62
+ :::hint{level="tip"}
63
+ 判断是否为一元一次方程,先看化简后未知数的最高次数。
64
+ :::
65
+
66
+ :::example{title="识别方程"}
67
+ 下列哪个是一元一次方程?
68
+ :::
69
+
70
+ :::question{hint="先移项,再把系数化为 1"}
71
+ 解方程 $3x - 6 = 0$。
72
+ :::
73
+
74
+ hint 的 `level` 合法取值:`info`、`tip`、`warning`、`danger`、`reflect`。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dexin-content",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Open content toolchain — Markdown → LessonAST/ContentPackage compiler, CLI and validator (dexinlabs ecosystem).",
5
5
  "keywords": [
6
6
  "markdown",
@@ -33,6 +33,10 @@
33
33
  "cli/**/*.ts",
34
34
  "domains/**/*.ts",
35
35
  "bin/**",
36
+ "docs/**/*.md",
37
+ "example/README.md",
38
+ "example/content-manifest.json",
39
+ "example/lessons/**/*.md",
36
40
  "README.md",
37
41
  "LICENSE"
38
42
  ],
@@ -58,6 +62,7 @@
58
62
  "scripts": {
59
63
  "typecheck": "tsc --noEmit",
60
64
  "runtime:check": "tsx scripts/runtime-check.ts",
65
+ "validate:proposals": "tsx scripts/validate-proposals.ts",
61
66
  "pack": "npm pack --dry-run",
62
67
  "build": "tsx cli/build.ts",
63
68
  "package": "tsx cli/package.ts"