cotutor 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (912) hide show
  1. package/LICENSE +249 -21
  2. package/NOTICE +42 -0
  3. package/README.md +115 -20
  4. package/avatars/chinese.webp +0 -0
  5. package/avatars/english.webp +0 -0
  6. package/avatars/math.webp +0 -0
  7. package/bin/cotutor.js +15 -5
  8. package/cards/canvas/card.css +8 -0
  9. package/cards/canvas/card.md +68 -0
  10. package/cards/choice/card.css +18 -0
  11. package/cards/choice/card.md +55 -0
  12. package/cards/code/card.css +5 -0
  13. package/cards/code/card.md +46 -0
  14. package/cards/dictation/card.css +10 -0
  15. package/cards/dictation/card.md +97 -0
  16. package/cards/image/card.css +10 -0
  17. package/cards/image/card.md +52 -0
  18. package/cards/lecture/card.css +9 -0
  19. package/cards/lecture/card.md +69 -0
  20. package/cards/paint/card.css +20 -0
  21. package/cards/paint/card.md +77 -0
  22. package/cards/photo/card.css +22 -0
  23. package/cards/photo/card.md +61 -0
  24. package/cards/read/card.css +6 -0
  25. package/cards/read/card.md +51 -0
  26. package/cards/text/card.css +3 -0
  27. package/cards/text/card.md +69 -0
  28. package/cards/tianzige/card.css +17 -0
  29. package/cards/tianzige/card.md +78 -0
  30. package/cards/tutor/card.css +79 -0
  31. package/cards/tutor/card.md +149 -0
  32. package/cards/word/card.css +24 -0
  33. package/cards/word/card.md +91 -0
  34. package/cards//346/235/277/344/271/246/346/200/216/344/271/210/345/206/231.md +159 -0
  35. package/dist/app/activity-eval-rules.d.ts +62 -0
  36. package/dist/app/activity-eval-rules.js +276 -0
  37. package/dist/app/activity-eval.d.ts +65 -0
  38. package/dist/app/activity-eval.js +159 -0
  39. package/dist/app/build.d.ts +11 -0
  40. package/dist/app/build.js +45 -0
  41. package/dist/app/cli/activities.d.ts +70 -0
  42. package/dist/app/cli/activities.js +196 -0
  43. package/dist/app/cli/cert.d.ts +12 -0
  44. package/dist/app/cli/cert.js +49 -0
  45. package/dist/app/cli/chalk/bake.d.ts +10 -0
  46. package/dist/app/cli/chalk/bake.js +58 -0
  47. package/dist/app/cli/chalk/browser.d.ts +16 -0
  48. package/dist/app/cli/chalk/browser.js +157 -0
  49. package/dist/app/cli/chalk/build.d.ts +14 -0
  50. package/dist/app/cli/chalk/build.js +100 -0
  51. package/dist/app/cli/chalk/dub.d.ts +51 -0
  52. package/dist/app/cli/chalk/dub.js +106 -0
  53. package/dist/app/cli/chalk/load-scene.d.ts +34 -0
  54. package/dist/app/cli/chalk/load-scene.js +117 -0
  55. package/dist/app/cli/chalk/main.d.ts +7 -0
  56. package/dist/app/cli/chalk/main.js +199 -0
  57. package/dist/app/cli/chalk/serve.d.ts +4 -0
  58. package/dist/app/cli/chalk/serve.js +28 -0
  59. package/dist/app/cli/chalk/snap.d.ts +65 -0
  60. package/dist/app/cli/chalk/snap.js +103 -0
  61. package/dist/app/cli/chalk/typecheck.d.ts +7 -0
  62. package/dist/app/cli/chalk/typecheck.js +51 -0
  63. package/dist/app/cli/doctor.d.ts +24 -0
  64. package/dist/app/cli/doctor.js +553 -0
  65. package/dist/app/cli/firewall.d.ts +24 -0
  66. package/dist/app/cli/firewall.js +79 -0
  67. package/dist/app/cli/grades.d.ts +14 -0
  68. package/dist/app/cli/grades.js +44 -0
  69. package/dist/{cli → app/cli}/init.d.ts +3 -1
  70. package/dist/app/cli/init.js +158 -0
  71. package/dist/app/cli/main.js +1038 -0
  72. package/dist/app/cli/migrate.d.ts +49 -0
  73. package/dist/app/cli/migrate.js +195 -0
  74. package/dist/app/cli/port-holder.d.ts +16 -0
  75. package/dist/app/cli/port-holder.js +34 -0
  76. package/dist/app/cli/port.d.ts +37 -0
  77. package/dist/app/cli/port.js +116 -0
  78. package/dist/app/cli/sandbox.d.ts +25 -0
  79. package/dist/app/cli/sandbox.js +166 -0
  80. package/dist/app/cli/serve-log.d.ts +13 -0
  81. package/dist/app/cli/serve-log.js +90 -0
  82. package/dist/app/cli/serve.d.ts +51 -0
  83. package/dist/app/cli/serve.js +168 -0
  84. package/dist/app/cli/service-windows.d.ts +50 -0
  85. package/dist/app/cli/service-windows.js +117 -0
  86. package/dist/app/cli/service.d.ts +138 -0
  87. package/dist/app/cli/service.js +429 -0
  88. package/dist/app/cli/shipped.d.ts +30 -0
  89. package/dist/app/cli/shipped.js +47 -0
  90. package/dist/app/cli/skeleton.d.ts +38 -0
  91. package/dist/app/cli/skeleton.js +161 -0
  92. package/dist/app/cli/skills.d.ts +59 -0
  93. package/dist/app/cli/skills.js +199 -0
  94. package/dist/app/cli/themes.d.ts +52 -0
  95. package/dist/app/cli/themes.js +148 -0
  96. package/dist/app/cli/tray-icons.d.ts +11 -0
  97. package/dist/app/cli/tray-icons.js +87 -0
  98. package/dist/app/cli/tray-ids.d.ts +6 -0
  99. package/dist/app/cli/tray-ids.js +6 -0
  100. package/dist/app/cli/tray-mac.d.ts +8 -0
  101. package/dist/app/cli/tray-mac.js +140 -0
  102. package/dist/app/cli/tray-run.d.ts +1 -0
  103. package/dist/app/cli/tray-run.js +33 -0
  104. package/dist/app/cli/tray-windows.d.ts +5 -0
  105. package/dist/app/cli/tray-windows.js +166 -0
  106. package/dist/app/cli/tray.d.ts +205 -0
  107. package/dist/app/cli/tray.js +618 -0
  108. package/dist/{cli → app/cli}/tutors.d.ts +15 -26
  109. package/dist/{cli → app/cli}/tutors.js +86 -110
  110. package/dist/app/cli/usage.d.ts +2 -0
  111. package/dist/app/cli/usage.js +35 -0
  112. package/dist/app/dictation/recognize.d.ts +18 -0
  113. package/dist/app/dictation/recognize.js +6 -0
  114. package/dist/app/dictation/route.d.ts +52 -0
  115. package/dist/app/dictation/route.js +511 -0
  116. package/dist/app/diff.d.ts +11 -0
  117. package/dist/app/diff.js +71 -0
  118. package/dist/app/http/access.d.ts +62 -0
  119. package/dist/app/http/access.js +180 -0
  120. package/dist/app/http/app.d.ts +241 -0
  121. package/dist/app/http/app.js +1159 -0
  122. package/dist/app/http/avatars.d.ts +17 -0
  123. package/dist/app/http/avatars.js +35 -0
  124. package/dist/app/http/dev-activities.d.ts +115 -0
  125. package/dist/app/http/dev-activities.js +193 -0
  126. package/dist/app/http/dev-overview.d.ts +108 -0
  127. package/dist/app/http/dev-overview.js +209 -0
  128. package/dist/app/http/dev-tutor-files.d.ts +80 -0
  129. package/dist/app/http/dev-tutor-files.js +151 -0
  130. package/dist/app/http/parent-home.d.ts +51 -0
  131. package/dist/app/http/parent-home.js +266 -0
  132. package/dist/app/http/parent-look.d.ts +2 -0
  133. package/dist/app/http/parent-look.js +151 -0
  134. package/dist/app/http/range.d.ts +9 -0
  135. package/dist/app/http/range.js +19 -0
  136. package/dist/app/http/raw-view.d.ts +117 -0
  137. package/dist/app/http/raw-view.js +149 -0
  138. package/dist/app/http/send-file.d.ts +7 -0
  139. package/dist/app/http/send-file.js +29 -0
  140. package/dist/{server → app/http}/stage.d.ts +9 -0
  141. package/dist/{server → app/http}/stage.js +34 -6
  142. package/dist/app/http/theme.d.ts +13 -0
  143. package/dist/app/http/theme.js +41 -0
  144. package/dist/app/http/voice-preview.d.ts +4 -0
  145. package/dist/app/http/voice-preview.js +34 -0
  146. package/dist/app/machine/bailian.d.ts +26 -0
  147. package/dist/app/machine/bailian.js +87 -0
  148. package/dist/app/mock/mock-live.d.ts +16 -0
  149. package/dist/app/mock/mock-live.js +140 -0
  150. package/dist/{server → app/mock}/mock.d.ts +38 -5
  151. package/dist/app/mock/mock.js +1125 -0
  152. package/dist/app/replay.d.ts +94 -0
  153. package/dist/app/replay.js +170 -0
  154. package/dist/app/skills/analyze-doc.d.ts +3 -0
  155. package/dist/app/skills/analyze-doc.js +47 -0
  156. package/dist/app/skills/home-doc.d.ts +5 -0
  157. package/dist/app/skills/home-doc.js +112 -0
  158. package/dist/app/skills/tune-doc.d.ts +3 -0
  159. package/dist/app/skills/tune-doc.js +80 -0
  160. package/dist/app/talk/list.d.ts +11 -0
  161. package/dist/app/talk/list.js +9 -0
  162. package/dist/app/talk/route.d.ts +16 -0
  163. package/dist/app/talk/route.js +142 -0
  164. package/dist/app/workspace.d.ts +63 -0
  165. package/dist/{cli → app}/workspace.js +100 -52
  166. package/dist/app/ws/route.d.ts +31 -0
  167. package/dist/app/ws/route.js +33 -0
  168. package/dist/app/ws/ws.d.ts +31 -0
  169. package/dist/app/ws/ws.js +169 -0
  170. package/dist/board/canvas-chat.d.ts +97 -0
  171. package/dist/board/canvas-chat.js +175 -0
  172. package/dist/board/card-guards.d.ts +15 -0
  173. package/dist/board/card-guards.js +38 -0
  174. package/dist/board/cards/canvas.d.ts +41 -0
  175. package/dist/board/cards/canvas.js +83 -0
  176. package/dist/{cards → board/cards}/choice.js +0 -12
  177. package/dist/{cards → board/cards}/code.js +0 -3
  178. package/dist/board/cards/dictation.d.ts +42 -0
  179. package/dist/board/cards/dictation.js +105 -0
  180. package/dist/board/cards/docs.d.ts +47 -0
  181. package/dist/board/cards/docs.js +152 -0
  182. package/dist/{cards → board/cards}/image.js +1 -10
  183. package/dist/board/cards/index.d.ts +62 -0
  184. package/dist/board/cards/index.js +144 -0
  185. package/dist/{cards → board/cards}/kind.d.ts +10 -4
  186. package/dist/board/cards/kind.js +9 -0
  187. package/dist/board/cards/lecture.d.ts +26 -0
  188. package/dist/board/cards/lecture.js +62 -0
  189. package/dist/board/cards/paint.d.ts +38 -0
  190. package/dist/board/cards/paint.js +85 -0
  191. package/dist/board/cards/photo.d.ts +15 -0
  192. package/dist/board/cards/photo.js +29 -0
  193. package/dist/{cards → board/cards}/read.js +1 -10
  194. package/dist/board/cards/text.d.ts +20 -0
  195. package/dist/board/cards/text.js +59 -0
  196. package/dist/board/cards/tianzige.d.ts +16 -0
  197. package/dist/board/cards/tianzige.js +64 -0
  198. package/dist/board/cards/tutor.d.ts +72 -0
  199. package/dist/board/cards/tutor.js +134 -0
  200. package/dist/board/cards/word.d.ts +20 -0
  201. package/dist/board/cards/word.js +76 -0
  202. package/dist/board/lecture.d.ts +100 -0
  203. package/dist/board/lecture.js +204 -0
  204. package/dist/board/paper.d.ts +20 -0
  205. package/dist/board/paper.js +76 -0
  206. package/dist/board/parse.d.ts +118 -0
  207. package/dist/board/parse.js +324 -0
  208. package/dist/board/presentation.d.ts +289 -0
  209. package/dist/board/presentation.js +613 -0
  210. package/dist/board/sections.d.ts +16 -0
  211. package/dist/board/sections.js +130 -0
  212. package/dist/brain/api-tools.d.ts +8 -0
  213. package/dist/brain/api-tools.js +191 -0
  214. package/dist/brain/api.d.ts +57 -0
  215. package/dist/brain/api.js +394 -0
  216. package/dist/brain/index.d.ts +97 -0
  217. package/dist/brain/index.js +11 -0
  218. package/dist/brain/log.d.ts +5 -0
  219. package/dist/brain/log.js +22 -0
  220. package/dist/brain/runtime.d.ts +30 -0
  221. package/dist/brain/runtime.js +23 -0
  222. package/dist/brain/stream-json.d.ts +16 -0
  223. package/dist/brain/stream-json.js +82 -0
  224. package/dist/brain/stream.d.ts +20 -0
  225. package/dist/brain/stream.js +70 -0
  226. package/dist/brain/transcript.d.ts +45 -0
  227. package/dist/brain/transcript.js +132 -0
  228. package/dist/brain/types.d.ts +99 -0
  229. package/dist/brain/types.js +14 -0
  230. package/dist/chalk/actions.d.ts +37 -0
  231. package/dist/chalk/actions.js +69 -0
  232. package/dist/chalk/bundle.d.ts +26 -0
  233. package/dist/chalk/bundle.js +69 -0
  234. package/dist/chalk/check.d.ts +28 -0
  235. package/dist/chalk/check.js +291 -0
  236. package/dist/chalk/circle.d.ts +64 -0
  237. package/dist/chalk/circle.js +193 -0
  238. package/dist/chalk/clock.d.ts +84 -0
  239. package/dist/chalk/clock.js +84 -0
  240. package/dist/chalk/geometry.d.ts +44 -0
  241. package/dist/chalk/geometry.js +139 -0
  242. package/dist/chalk/glyphs/capitals.d.ts +12 -0
  243. package/dist/chalk/glyphs/capitals.js +264 -0
  244. package/dist/chalk/glyphs/index.d.ts +21 -0
  245. package/dist/chalk/glyphs/index.js +18 -0
  246. package/dist/chalk/glyphs/letters.d.ts +13 -0
  247. package/dist/chalk/glyphs/letters.js +280 -0
  248. package/dist/chalk/glyphs/metrics.d.ts +33 -0
  249. package/dist/chalk/glyphs/metrics.js +28 -0
  250. package/dist/chalk/glyphs/registry.d.ts +7 -0
  251. package/dist/chalk/glyphs/registry.js +8 -0
  252. package/dist/chalk/glyphs/sample.d.ts +32 -0
  253. package/dist/chalk/glyphs/sample.js +102 -0
  254. package/dist/chalk/glyphs/skeleton.d.ts +64 -0
  255. package/dist/chalk/glyphs/skeleton.js +126 -0
  256. package/dist/chalk/glyphs/types.d.ts +58 -0
  257. package/dist/chalk/glyphs/types.js +17 -0
  258. package/dist/chalk/index.d.ts +23 -0
  259. package/dist/chalk/index.js +15 -0
  260. package/dist/chalk/manifest.d.ts +41 -0
  261. package/dist/chalk/manifest.js +6 -0
  262. package/dist/chalk/paint.d.ts +180 -0
  263. package/dist/chalk/paint.js +210 -0
  264. package/dist/chalk/pointer.d.ts +8 -0
  265. package/dist/chalk/pointer.js +21 -0
  266. package/dist/chalk/prepare.d.ts +11 -0
  267. package/dist/chalk/prepare.js +15 -0
  268. package/dist/chalk/scene-schema.d.ts +199 -0
  269. package/dist/chalk/scene-schema.js +91 -0
  270. package/dist/chalk/seed.d.ts +3 -0
  271. package/dist/chalk/seed.js +10 -0
  272. package/dist/chalk/text-metrics.d.ts +8 -0
  273. package/dist/chalk/text-metrics.js +156 -0
  274. package/dist/chalk/timeline.d.ts +47 -0
  275. package/dist/chalk/timeline.js +84 -0
  276. package/dist/chalk/types.d.ts +150 -0
  277. package/dist/chalk/types.js +8 -0
  278. package/dist/lib/index.d.ts +14 -12
  279. package/dist/lib/index.js +14 -12
  280. package/dist/media/draw.d.ts +23 -0
  281. package/dist/media/draw.js +44 -0
  282. package/dist/media/mp3.d.ts +6 -0
  283. package/dist/media/mp3.js +58 -0
  284. package/dist/media/mp4.d.ts +1 -0
  285. package/dist/media/mp4.js +58 -0
  286. package/dist/media/tts-voices.d.ts +18 -0
  287. package/dist/media/tts-voices.js +606 -0
  288. package/dist/media/tts.d.ts +56 -0
  289. package/dist/media/tts.js +174 -0
  290. package/dist/media/vision.d.ts +25 -0
  291. package/dist/media/vision.js +71 -0
  292. package/dist/memory/commit.d.ts +67 -0
  293. package/dist/memory/commit.js +130 -0
  294. package/dist/memory/context.d.ts +86 -0
  295. package/dist/memory/context.js +97 -0
  296. package/dist/memory/diary.d.ts +84 -0
  297. package/dist/memory/diary.js +183 -0
  298. package/dist/memory/grade.d.ts +2 -0
  299. package/dist/memory/grade.js +7 -0
  300. package/dist/{lib → memory}/plan.d.ts +1 -1
  301. package/dist/{lib → memory}/plan.js +1 -1
  302. package/dist/memory/proposals.d.ts +11 -0
  303. package/dist/memory/proposals.js +43 -0
  304. package/dist/memory/retrieval.d.ts +44 -0
  305. package/dist/memory/retrieval.js +157 -0
  306. package/dist/{lib → memory}/timetable.d.ts +0 -3
  307. package/dist/{lib → memory}/timetable.js +0 -11
  308. package/dist/memory/vault-notes.d.ts +165 -0
  309. package/dist/memory/vault-notes.js +359 -0
  310. package/dist/memory/vault.d.ts +70 -0
  311. package/dist/memory/vault.js +169 -0
  312. package/dist/realtime/bailian.d.ts +120 -0
  313. package/dist/realtime/bailian.js +289 -0
  314. package/dist/realtime/cues.d.ts +51 -0
  315. package/dist/realtime/cues.js +94 -0
  316. package/dist/realtime/listen.d.ts +32 -0
  317. package/dist/realtime/listen.js +53 -0
  318. package/dist/realtime/preview.d.ts +23 -0
  319. package/dist/realtime/preview.js +38 -0
  320. package/dist/realtime/prompt.d.ts +8 -0
  321. package/dist/realtime/prompt.js +13 -0
  322. package/dist/realtime/voices.d.ts +21 -0
  323. package/dist/realtime/voices.js +71 -0
  324. package/dist/runtime/activities.d.ts +29 -0
  325. package/dist/runtime/activities.js +72 -0
  326. package/dist/runtime/activity-state.d.ts +22 -0
  327. package/dist/runtime/activity-state.js +83 -0
  328. package/dist/runtime/activity.d.ts +63 -0
  329. package/dist/runtime/activity.js +428 -0
  330. package/dist/runtime/agent-file.d.ts +24 -0
  331. package/dist/runtime/agent-file.js +59 -0
  332. package/dist/runtime/auto-bookkeep.d.ts +30 -0
  333. package/dist/runtime/auto-bookkeep.js +84 -0
  334. package/dist/{lib → runtime/context}/context-pack.d.ts +3 -3
  335. package/dist/runtime/context/context-pack.js +182 -0
  336. package/dist/runtime/context/thread-digest.d.ts +4 -0
  337. package/dist/runtime/context/thread-digest.js +35 -0
  338. package/dist/runtime/context/tutor-rules.d.ts +25 -0
  339. package/dist/runtime/context/tutor-rules.js +47 -0
  340. package/dist/runtime/delivery/delivery.d.ts +94 -0
  341. package/dist/runtime/delivery/delivery.js +145 -0
  342. package/dist/runtime/delivery/playback.d.ts +25 -0
  343. package/dist/runtime/delivery/playback.js +29 -0
  344. package/dist/runtime/delivery/player.d.ts +145 -0
  345. package/dist/runtime/delivery/player.js +221 -0
  346. package/dist/runtime/delivery/queue.d.ts +89 -0
  347. package/dist/runtime/delivery/queue.js +59 -0
  348. package/dist/runtime/home/edit.d.ts +39 -0
  349. package/dist/runtime/home/edit.js +132 -0
  350. package/dist/runtime/home/home.d.ts +191 -0
  351. package/dist/runtime/home/home.js +318 -0
  352. package/dist/runtime/home/store.d.ts +142 -0
  353. package/dist/runtime/home/store.js +455 -0
  354. package/dist/runtime/journal/conversation.d.ts +46 -0
  355. package/dist/runtime/journal/conversation.js +89 -0
  356. package/dist/runtime/journal/events.d.ts +27 -0
  357. package/dist/runtime/journal/events.js +136 -0
  358. package/dist/runtime/journal/kid-log.d.ts +105 -0
  359. package/dist/runtime/journal/kid-log.js +133 -0
  360. package/dist/runtime/journal/ledger.d.ts +17 -0
  361. package/dist/{lib → runtime/journal}/ledger.js +7 -40
  362. package/dist/runtime/journal/mix.d.ts +42 -0
  363. package/dist/runtime/journal/mix.js +129 -0
  364. package/dist/runtime/journal/mouth-index.d.ts +26 -0
  365. package/dist/runtime/journal/mouth-index.js +34 -0
  366. package/dist/runtime/journal/mouth.d.ts +54 -0
  367. package/dist/runtime/journal/mouth.js +102 -0
  368. package/dist/runtime/journal/recorder.d.ts +91 -0
  369. package/dist/runtime/journal/recorder.js +148 -0
  370. package/dist/runtime/journal/store.d.ts +123 -0
  371. package/dist/runtime/journal/store.js +316 -0
  372. package/dist/runtime/kid-input.d.ts +16 -0
  373. package/dist/runtime/kid-input.js +194 -0
  374. package/dist/runtime/kid-view.d.ts +124 -0
  375. package/dist/runtime/kid-view.js +157 -0
  376. package/dist/runtime/lecture.d.ts +86 -0
  377. package/dist/runtime/lecture.js +237 -0
  378. package/dist/runtime/live/call.d.ts +231 -0
  379. package/dist/runtime/live/call.js +1244 -0
  380. package/dist/runtime/live/turns.d.ts +48 -0
  381. package/dist/runtime/live/turns.js +102 -0
  382. package/dist/runtime/runner.d.ts +243 -0
  383. package/dist/runtime/runner.js +777 -0
  384. package/dist/runtime/talk-run.d.ts +21 -0
  385. package/dist/runtime/talk-run.js +34 -0
  386. package/dist/scenes/dictation/match.d.ts +57 -0
  387. package/dist/scenes/dictation/match.js +255 -0
  388. package/dist/scenes/dictation/recognize.d.ts +86 -0
  389. package/dist/scenes/dictation/recognize.js +163 -0
  390. package/dist/scenes/dictation/store.d.ts +112 -0
  391. package/dist/scenes/dictation/store.js +99 -0
  392. package/dist/scenes/look.d.ts +26 -0
  393. package/dist/scenes/look.js +1 -0
  394. package/dist/scenes/paint/painter.d.ts +84 -0
  395. package/dist/scenes/paint/painter.js +171 -0
  396. package/dist/scenes/paint/prompt.d.ts +23 -0
  397. package/dist/scenes/paint/prompt.js +36 -0
  398. package/dist/scenes/talk/en-words.d.ts +8 -0
  399. package/dist/scenes/talk/en-words.js +44 -0
  400. package/dist/scenes/talk/list.d.ts +44 -0
  401. package/dist/scenes/talk/list.js +88 -0
  402. package/dist/scenes/talk/store.d.ts +27 -0
  403. package/dist/scenes/talk/store.js +61 -0
  404. package/dist/scenes/talk/warmup.d.ts +16 -0
  405. package/dist/scenes/talk/warmup.js +39 -0
  406. package/dist/schema/activity.d.ts +149 -0
  407. package/dist/schema/activity.js +113 -0
  408. package/dist/schema/board.d.ts +143 -2
  409. package/dist/schema/board.js +15 -3
  410. package/dist/schema/config.d.ts +339 -103
  411. package/dist/schema/config.js +269 -84
  412. package/dist/schema/context-pack.d.ts +76 -14
  413. package/dist/schema/context-pack.js +91 -10
  414. package/dist/{lib/conversation.d.ts → schema/conversation-files.d.ts} +11 -33
  415. package/dist/schema/conversation-files.js +26 -0
  416. package/dist/schema/conversation.d.ts +391 -49
  417. package/dist/schema/conversation.js +135 -19
  418. package/dist/schema/events.d.ts +138 -0
  419. package/dist/schema/events.js +1 -0
  420. package/dist/schema/home.d.ts +56 -0
  421. package/dist/schema/home.js +35 -0
  422. package/dist/schema/index.d.ts +14 -3
  423. package/dist/schema/index.js +14 -3
  424. package/dist/schema/kid-device.d.ts +20 -0
  425. package/dist/schema/kid-device.js +32 -0
  426. package/dist/schema/ledger.d.ts +8 -63
  427. package/dist/schema/ledger.js +7 -31
  428. package/dist/schema/memory.d.ts +73 -0
  429. package/dist/schema/memory.js +47 -0
  430. package/dist/schema/plan.d.ts +2 -2
  431. package/dist/schema/plan.js +2 -2
  432. package/dist/schema/sections.d.ts +32 -25
  433. package/dist/schema/sections.js +21 -22
  434. package/dist/schema/theme.d.ts +25 -0
  435. package/dist/schema/theme.js +26 -0
  436. package/dist/schema/timetable.d.ts +2 -2
  437. package/dist/schema/timetable.js +2 -2
  438. package/dist/schema/workspace.d.ts +68 -0
  439. package/dist/schema/workspace.js +37 -0
  440. package/dist/shared/default-port.d.ts +2 -0
  441. package/dist/shared/default-port.js +2 -0
  442. package/dist/shared/errors.d.ts +8 -0
  443. package/dist/shared/errors.js +13 -0
  444. package/dist/shared/home.d.ts +2 -0
  445. package/dist/shared/home.js +16 -0
  446. package/dist/shared/human-notes.d.ts +5 -0
  447. package/dist/shared/human-notes.js +7 -0
  448. package/dist/shared/qianwen.d.ts +54 -0
  449. package/dist/shared/qianwen.js +113 -0
  450. package/dist/shared/site-url.d.ts +2 -0
  451. package/dist/shared/site-url.js +5 -0
  452. package/dist/shared/time.d.ts +9 -0
  453. package/dist/shared/time.js +22 -0
  454. package/dist/shared/tokens.d.ts +18 -0
  455. package/dist/shared/tokens.js +11 -0
  456. package/dist/shared/wav.d.ts +10 -0
  457. package/dist/shared/wav.js +50 -0
  458. package/dist/stage/chunk-226TNLQA.js +129 -0
  459. package/dist/stage/chunk-24LGQEKE.js +1 -0
  460. package/dist/stage/chunk-25XV6UY3.js +10 -0
  461. package/dist/stage/chunk-2AZI3NHJ.js +1 -0
  462. package/dist/stage/chunk-2BQZTTBE.js +1 -0
  463. package/dist/stage/chunk-2M7CGY3Y.js +11 -0
  464. package/dist/stage/chunk-2VR4WFQ7.js +10 -0
  465. package/dist/stage/chunk-2ZYXBUWG.js +9 -0
  466. package/dist/stage/chunk-32LAWMGY.js +1 -0
  467. package/dist/stage/chunk-3LEMTSOO.js +11 -0
  468. package/dist/stage/chunk-3QCKGZYE.js +32 -0
  469. package/dist/stage/chunk-44P66WFA.js +1 -0
  470. package/dist/stage/chunk-4AM6FRR6.js +7 -0
  471. package/dist/stage/chunk-4QDE4SUK.js +1 -0
  472. package/dist/stage/chunk-4QESHV3U.js +1 -0
  473. package/dist/stage/chunk-4SG5QPHB.js +2 -0
  474. package/dist/stage/chunk-4T4UVCOC.js +1 -0
  475. package/dist/stage/chunk-4WUEPPG3.js +34 -0
  476. package/dist/stage/chunk-4YCN6UZ5.js +99 -0
  477. package/dist/stage/chunk-5V7ZY2AP.js +1 -0
  478. package/dist/stage/chunk-5VS5Y2VJ.js +6 -0
  479. package/dist/stage/chunk-5W4NO2ZD.js +62 -0
  480. package/dist/stage/chunk-65CRNICM.js +1 -0
  481. package/dist/stage/chunk-66MULO7F.js +5 -0
  482. package/dist/stage/chunk-6COFQTBS.js +162 -0
  483. package/dist/stage/chunk-6VZG3M3I.js +8 -0
  484. package/dist/stage/chunk-7HVDH43T.js +1 -0
  485. package/dist/stage/chunk-7JLJN4EZ.js +8 -0
  486. package/dist/stage/chunk-7QDSP4LN.js +1 -0
  487. package/dist/stage/chunk-7R6OQAO3.js +1 -0
  488. package/dist/stage/chunk-7VTTHZKW.js +7 -0
  489. package/dist/stage/chunk-ATHR5KEN.js +10 -0
  490. package/dist/stage/chunk-B6R5LTP2.js +36 -0
  491. package/dist/stage/chunk-BD4WE264.js +1 -0
  492. package/dist/stage/chunk-BONANU47.js +206 -0
  493. package/dist/stage/chunk-BTVW7OFK.js +89 -0
  494. package/dist/stage/chunk-BUTJIDHG.js +8 -0
  495. package/dist/stage/chunk-C2IPYMML.js +84 -0
  496. package/dist/stage/chunk-C54FTPPV.js +9 -0
  497. package/dist/stage/chunk-C6LOUF2Z.js +90 -0
  498. package/dist/stage/chunk-CFF3PILO.js +339 -0
  499. package/dist/stage/chunk-CI2FQ2JY.js +9 -0
  500. package/dist/stage/chunk-CW5O4RV7.js +12 -0
  501. package/dist/stage/chunk-CYABNQYD.js +1 -0
  502. package/dist/stage/chunk-D7FFA4NS.js +15 -0
  503. package/dist/stage/chunk-DUNNXU6Z.js +8 -0
  504. package/dist/stage/chunk-E3E4Q6JF.js +6 -0
  505. package/dist/stage/chunk-EB3U3LH7.js +9 -0
  506. package/dist/stage/chunk-EGSZ736B.js +8 -0
  507. package/dist/stage/chunk-EOBXW5VQ.js +2 -0
  508. package/dist/stage/chunk-ERZC3B7Y.js +11 -0
  509. package/dist/stage/chunk-ESDOOBIH.js +11 -0
  510. package/dist/stage/chunk-EVU4QAL4.js +178 -0
  511. package/dist/stage/chunk-EVVZI2NY.js +39 -0
  512. package/dist/stage/chunk-FBKAZUKD.js +39 -0
  513. package/dist/stage/chunk-FDVKYQJN.js +1 -0
  514. package/dist/stage/chunk-FGPVYHVR.js +5 -0
  515. package/dist/stage/chunk-FHUD4AZC.js +1 -0
  516. package/dist/stage/chunk-FKUJ2DMY.js +96 -0
  517. package/dist/stage/chunk-FNZGVIJV.js +1 -0
  518. package/dist/stage/chunk-GBYSEGYM.js +78 -0
  519. package/dist/stage/chunk-GCO4TDHX.js +1 -0
  520. package/dist/stage/chunk-GKTML2LG.js +10 -0
  521. package/dist/stage/chunk-GMDIHHXQ.js +11 -0
  522. package/dist/stage/chunk-GOUNACI3.js +10 -0
  523. package/dist/stage/chunk-GXAYATPF.js +3 -0
  524. package/dist/stage/chunk-H2K2DDXV.js +8 -0
  525. package/dist/stage/chunk-H3UJI6ZY.js +13 -0
  526. package/dist/stage/chunk-H6PUGCME.js +7 -0
  527. package/dist/stage/chunk-HNBUIMIG.js +1 -0
  528. package/dist/stage/chunk-HRZY2BJQ.js +10 -0
  529. package/dist/stage/chunk-HSPJGALW.js +30 -0
  530. package/dist/stage/chunk-IBYORHIP.js +25 -0
  531. package/dist/stage/chunk-IHF4EWTY.js +1 -0
  532. package/dist/stage/chunk-ISBRPNKI.js +1 -0
  533. package/dist/stage/chunk-IZ3OI3C2.js +2 -0
  534. package/dist/stage/chunk-JEFZAC2R.js +1 -0
  535. package/dist/stage/chunk-JFIGRDPO.js +1 -0
  536. package/dist/stage/chunk-JFVYBOOC.js +9 -0
  537. package/dist/stage/chunk-JGD4TUTQ.js +1 -0
  538. package/dist/stage/chunk-JO4BEHDH.js +4 -0
  539. package/dist/stage/chunk-JOWBS42C.js +1 -0
  540. package/dist/stage/chunk-JP3TM2TG.js +1 -0
  541. package/dist/stage/chunk-JVK2C52O.js +1 -0
  542. package/dist/stage/chunk-K4I76XBX.js +14 -0
  543. package/dist/stage/chunk-KA6CZZ4M.js +1 -0
  544. package/dist/stage/chunk-KESSJ36M.js +1 -0
  545. package/dist/stage/chunk-LKATEVRU.js +1 -0
  546. package/dist/stage/chunk-LS4EJ2EI.js +1 -0
  547. package/dist/stage/chunk-LUKJQC2X.js +70 -0
  548. package/dist/stage/chunk-LWULR3SM.js +1 -0
  549. package/dist/stage/chunk-LWVRFMKX.js +2 -0
  550. package/dist/stage/chunk-LXC4ZBPZ.js +1 -0
  551. package/dist/stage/chunk-M2UP255H.js +1 -0
  552. package/dist/stage/chunk-MAPPXUZQ.js +231 -0
  553. package/dist/stage/chunk-MBMPW7Z5.js +1 -0
  554. package/dist/stage/chunk-MD3HMMTK.js +1 -0
  555. package/dist/stage/chunk-MLPCUIKR.js +17 -0
  556. package/dist/stage/chunk-MNQIRPDQ.js +8 -0
  557. package/dist/stage/chunk-MRG7MJVS.js +1 -0
  558. package/dist/stage/chunk-MU6EXW72.js +10 -0
  559. package/dist/stage/chunk-MVU2X6LU.js +4 -0
  560. package/dist/stage/chunk-N6UA5CZV.js +36 -0
  561. package/dist/stage/chunk-NLZIT2TJ.js +7 -0
  562. package/dist/stage/chunk-NOJR3J7M.js +1 -0
  563. package/dist/stage/chunk-NT7ARDOL.js +1 -0
  564. package/dist/stage/chunk-NWOGMPZH.js +1 -0
  565. package/dist/stage/chunk-NXE4KMG3.js +40 -0
  566. package/dist/stage/chunk-O4YCZXD2.js +257 -0
  567. package/dist/stage/chunk-OAJZ7HLK.js +1 -0
  568. package/dist/stage/chunk-OCFCFASO.js +1 -0
  569. package/dist/stage/chunk-ODGPG5RY.js +7 -0
  570. package/dist/stage/chunk-ODUEA52Q.js +3 -0
  571. package/dist/stage/chunk-OGDJ3FCI.js +1 -0
  572. package/dist/stage/chunk-OLDJ55TT.js +1 -0
  573. package/dist/stage/chunk-OOCL4ATI.js +2 -0
  574. package/dist/stage/chunk-OQEFDUDV.js +1 -0
  575. package/dist/stage/chunk-OQWVEWG7.js +4 -0
  576. package/dist/stage/chunk-OUIX6BGK.js +1 -0
  577. package/dist/stage/chunk-OV7OZYUO.js +7 -0
  578. package/dist/stage/chunk-PFGDY33O.js +1 -0
  579. package/dist/stage/chunk-PFJDUJB3.js +139 -0
  580. package/dist/stage/chunk-PGEW76ER.js +1 -0
  581. package/dist/stage/chunk-PP76TAQX.js +32 -0
  582. package/dist/stage/chunk-PTBOP4OT.js +2 -0
  583. package/dist/stage/chunk-PX2RVHCK.js +1 -0
  584. package/dist/stage/chunk-Q7K3PNAD.js +1 -0
  585. package/dist/stage/chunk-QDDPLZDB.js +1 -0
  586. package/dist/stage/chunk-QLGLZ77F.js +1 -0
  587. package/dist/stage/chunk-QOPFDFHI.js +1 -0
  588. package/dist/stage/chunk-QXT4KAEJ.js +1 -0
  589. package/dist/stage/chunk-R4YVZCIT.js +1 -0
  590. package/dist/stage/chunk-R6P6EHWA.js +62 -0
  591. package/dist/stage/chunk-RCH3OXBI.js +9 -0
  592. package/dist/stage/chunk-RHPE44I2.js +1 -0
  593. package/dist/stage/chunk-RJ2R4FVC.js +1 -0
  594. package/dist/stage/chunk-RJH2YVTD.js +1 -0
  595. package/dist/stage/chunk-RLWWW5OB.js +6 -0
  596. package/dist/stage/chunk-S3WO35Z7.js +120 -0
  597. package/dist/stage/chunk-S7ZGX6MW.js +1 -0
  598. package/dist/stage/chunk-SIAWX27B.js +1 -0
  599. package/dist/stage/chunk-SLG4X622.js +1 -0
  600. package/dist/stage/chunk-SLJRBC77.js +168 -0
  601. package/dist/stage/chunk-SRORWQJZ.js +1 -0
  602. package/dist/stage/chunk-STZTUIIF.js +2 -0
  603. package/dist/stage/chunk-SVQ6MWNV.js +9 -0
  604. package/dist/stage/chunk-TL3HLL2S.js +132 -0
  605. package/dist/stage/chunk-U3DOE6SF.js +24 -0
  606. package/dist/stage/chunk-UABQIZKW.js +6 -0
  607. package/dist/stage/chunk-UDK3R764.js +1 -0
  608. package/dist/stage/chunk-UFF4E54B.js +41 -0
  609. package/dist/stage/chunk-UFP5Q4AY.js +1 -0
  610. package/dist/stage/chunk-UMH2TSW2.js +72 -0
  611. package/dist/stage/chunk-UTASVNF5.js +10 -0
  612. package/dist/stage/chunk-UW7ML5ML.js +88 -0
  613. package/dist/stage/chunk-V3J7F6OP.js +1 -0
  614. package/dist/stage/chunk-VEHP5B5Q.js +5 -0
  615. package/dist/stage/chunk-VRXWB733.js +9 -0
  616. package/dist/stage/chunk-VVDHBBS5.js +5 -0
  617. package/dist/stage/chunk-VYFOH6FU.js +106 -0
  618. package/dist/stage/chunk-W3VZTLQS.js +3 -0
  619. package/dist/stage/chunk-W7WNRWTZ.js +8 -0
  620. package/dist/stage/chunk-WHUCV53C.js +1 -0
  621. package/dist/stage/chunk-WULMYHIF.js +1 -0
  622. package/dist/stage/chunk-X54EX7AW.js +9 -0
  623. package/dist/stage/chunk-X565ISZM.js +1 -0
  624. package/dist/stage/chunk-XGZMDECB.js +1 -0
  625. package/dist/stage/chunk-XHONC4RF.js +172 -0
  626. package/dist/stage/chunk-XIK4XQ6E.js +24 -0
  627. package/dist/stage/chunk-XJQQWFTE.js +1 -0
  628. package/dist/stage/chunk-XM4WOU3I.js +13 -0
  629. package/dist/stage/chunk-XM7Y5HP5.js +38 -0
  630. package/dist/stage/chunk-XNYYRGD5.js +292 -0
  631. package/dist/stage/chunk-XOYVR3SV.js +1 -0
  632. package/dist/stage/chunk-XQIW5HCE.js +1 -0
  633. package/dist/stage/chunk-YEWL7FUW.js +8 -0
  634. package/dist/stage/chunk-YI55PY3V.js +1 -0
  635. package/dist/stage/chunk-YM63HI6W.js +1 -0
  636. package/dist/stage/chunk-YNLGLU4P.js +9 -0
  637. package/dist/stage/chunk-YTNMNOWP.js +1 -0
  638. package/dist/stage/chunk-YV62XKTC.js +1 -0
  639. package/dist/stage/chunk-YVBA3QW6.js +1 -0
  640. package/dist/stage/chunk-Z2SY2HOC.js +1 -0
  641. package/dist/stage/chunk-ZBTCQEBK.js +10 -0
  642. package/dist/stage/chunk-ZCUFN74D.js +1 -0
  643. package/dist/stage/chunk-ZHOULXEJ.js +11 -0
  644. package/dist/stage/chunk-ZTRZIET2.js +1 -0
  645. package/dist/stage/index.html +1 -0
  646. package/dist/stage/stage.css +1 -1
  647. package/dist/stage/stage.js +1 -4383
  648. package/dist/ui/dev/dev-page.d.ts +1 -0
  649. package/dist/ui/dev/dev-page.js +2021 -0
  650. package/dist/ui/dev/dev-theme.d.ts +6 -0
  651. package/dist/ui/dev/dev-theme.js +67 -0
  652. package/dist/ui/dev/voice-gallery-page.d.ts +1 -0
  653. package/dist/ui/dev/voice-gallery-page.js +121 -0
  654. package/dist/ui/dev/voice-scenes.d.ts +21 -0
  655. package/dist/ui/dev/voice-scenes.js +30 -0
  656. package/dist/ui/dictation/en-box.d.ts +2 -0
  657. package/dist/ui/dictation/en-box.js +86 -0
  658. package/dist/ui/dictation/en-page.d.ts +1 -0
  659. package/dist/ui/dictation/en-page.js +484 -0
  660. package/dist/ui/dictation/page.d.ts +1 -0
  661. package/dist/ui/dictation/page.js +1084 -0
  662. package/dist/ui/dictation/parent-page.d.ts +1 -0
  663. package/dist/ui/dictation/parent-page.js +234 -0
  664. package/dist/ui/dictation/reel-mode.d.ts +1 -0
  665. package/dist/ui/dictation/reel-mode.js +87 -0
  666. package/dist/ui/dictation/reel-page.d.ts +1 -0
  667. package/dist/ui/dictation/reel-page.js +62 -0
  668. package/dist/ui/dictation/reel.d.ts +163 -0
  669. package/dist/ui/dictation/reel.js +436 -0
  670. package/dist/ui/dictation/ui.d.ts +44 -0
  671. package/dist/ui/dictation/ui.js +80 -0
  672. package/dist/ui/dictation/unlock.d.ts +7 -0
  673. package/dist/ui/dictation/unlock.js +16 -0
  674. package/dist/ui/kid/audio-client.d.ts +11 -0
  675. package/dist/ui/kid/audio-client.js +74 -0
  676. package/dist/ui/kid/board-mark-art.d.ts +37 -0
  677. package/dist/ui/kid/board-mark-art.js +5 -0
  678. package/dist/ui/kid/board-mark.d.ts +24 -0
  679. package/dist/ui/kid/board-mark.js +58 -0
  680. package/dist/ui/kid/home-tiles.d.ts +14 -0
  681. package/dist/ui/kid/home-tiles.js +44 -0
  682. package/dist/ui/kid/kid-log.d.ts +15 -0
  683. package/dist/ui/kid/kid-log.js +87 -0
  684. package/dist/ui/kid/kid-nav.d.ts +11 -0
  685. package/dist/ui/kid/kid-nav.js +22 -0
  686. package/dist/ui/kid/kid-page.d.ts +9 -0
  687. package/dist/ui/kid/kid-page.js +3684 -0
  688. package/dist/ui/kid/letters.d.ts +26 -0
  689. package/dist/ui/kid/letters.js +51 -0
  690. package/dist/ui/kid/photo-edit.d.ts +83 -0
  691. package/dist/ui/kid/photo-edit.js +106 -0
  692. package/dist/ui/kid/photo-ui.d.ts +23 -0
  693. package/dist/ui/kid/photo-ui.js +60 -0
  694. package/dist/ui/kid/reel.d.ts +419 -0
  695. package/dist/ui/kid/reel.js +648 -0
  696. package/dist/ui/kid/tianzige.d.ts +8 -0
  697. package/dist/ui/kid/tianzige.js +45 -0
  698. package/dist/ui/kid/tutor-face.d.ts +35 -0
  699. package/dist/ui/kid/tutor-face.js +84 -0
  700. package/dist/ui/kid/voice-ui.d.ts +70 -0
  701. package/dist/ui/kid/voice-ui.js +316 -0
  702. package/dist/ui/kid/voice-view.d.ts +71 -0
  703. package/dist/ui/kid/voice-view.js +75 -0
  704. package/dist/ui/kid/word-draw.d.ts +2 -0
  705. package/dist/ui/kid/word-draw.js +107 -0
  706. package/dist/ui/kid/word-emoji.d.ts +10 -0
  707. package/dist/ui/kid/word-emoji.js +68 -0
  708. package/dist/ui/kid/word-layout.d.ts +25 -0
  709. package/dist/ui/kid/word-layout.js +29 -0
  710. package/dist/ui/parent/parent-home-page.d.ts +1 -0
  711. package/dist/ui/parent/parent-home-page.js +722 -0
  712. package/dist/ui/parent/parent-page.d.ts +2 -0
  713. package/dist/ui/parent/parent-page.js +511 -0
  714. package/dist/ui/parent/parent-settings-page.d.ts +1 -0
  715. package/dist/ui/parent/parent-settings-page.js +133 -0
  716. package/dist/ui/parent/qr-page.d.ts +8 -0
  717. package/dist/ui/parent/qr-page.js +66 -0
  718. package/dist/ui/shared/brand.d.ts +38 -0
  719. package/dist/ui/shared/brand.js +53 -0
  720. package/dist/ui/shared/favicon.d.ts +12 -0
  721. package/dist/ui/shared/favicon.js +96 -0
  722. package/dist/ui/shared/icon.d.ts +9 -0
  723. package/dist/ui/shared/icon.js +35 -0
  724. package/dist/ui/shared/inline-lib.d.ts +1 -0
  725. package/dist/ui/shared/inline-lib.js +28 -0
  726. package/dist/ui/shared/logo.d.ts +31 -0
  727. package/dist/ui/shared/logo.js +43 -0
  728. package/dist/ui/shared/page-dom.d.ts +12 -0
  729. package/dist/ui/shared/page-dom.js +36 -0
  730. package/dist/ui/shared/png.d.ts +1 -0
  731. package/dist/ui/shared/png.js +45 -0
  732. package/dist/ui/shared/qr-svg.d.ts +3 -0
  733. package/dist/ui/shared/qr-svg.js +32 -0
  734. package/dist/ui/shared/qr.d.ts +14 -0
  735. package/dist/ui/shared/qr.js +281 -0
  736. package/dist/ui/shared/reel-dock.d.ts +22 -0
  737. package/dist/ui/shared/reel-dock.js +365 -0
  738. package/dist/ui/shared/tutor-color.d.ts +15 -0
  739. package/dist/ui/shared/tutor-color.js +47 -0
  740. package/dist/ui/talk/page.d.ts +6 -0
  741. package/dist/ui/talk/page.js +294 -0
  742. package/grades/grade-2/README.md +11 -0
  743. package/grades/grade-2/VALIDATION.md +41 -0
  744. package/grades/grade-2/agents/chinese-tutor/activities//344/275/240/350/257/264/346/210/221/347/224/273.md +157 -0
  745. package/grades/grade-2/agents/chinese-tutor/activities//344/275/240/350/257/264/346/210/221/347/224/273./344/276/213/345/255/220.md +78 -0
  746. package/grades/grade-2/agents/chinese-tutor/activities//344/276/213/345/255/220/345/233/276//345/200/237/344/274/236.jpg +0 -0
  747. package/grades/grade-2/agents/chinese-tutor/activities//344/276/213/345/255/220/345/233/276//345/260/217/347/214/264/346/221/230/346/241/203.jpg +0 -0
  748. package/grades/grade-2/agents/chinese-tutor/activities//345/206/231/350/257/235.md +64 -0
  749. package/grades/grade-2/agents/chinese-tutor/activities//345/206/231/350/257/235./344/276/213/345/255/220.md +31 -0
  750. package/grades/grade-2/agents/chinese-tutor/activities//345/220/254/345/206/231.md +48 -0
  751. package/grades/grade-2/agents/chinese-tutor/activities//345/244/215/344/271/240.md +79 -0
  752. package/grades/grade-2/agents/chinese-tutor/activities//345/244/215/344/271/240./344/276/213/345/255/220.md +35 -0
  753. package/grades/grade-2/agents/chinese-tutor/activities//345/260/217/350/257/276/345/240/202.md +53 -0
  754. package/grades/grade-2/agents/chinese-tutor/activities//346/243/200/346/237/245/344/275/234/344/270/232.md +81 -0
  755. package/grades/grade-2/agents/chinese-tutor/activities//346/243/200/346/237/245/344/275/234/344/270/232./344/276/213/345/255/220.md +35 -0
  756. package/grades/grade-2/agents/chinese-tutor/activities//347/234/213/345/233/276/350/257/264/350/257/235.md +94 -0
  757. package/grades/grade-2/agents/chinese-tutor/activities//347/234/213/345/233/276/350/257/264/350/257/235./344/276/213/345/255/220.md +63 -0
  758. package/grades/grade-2/agents/chinese-tutor/activities//351/242/204/344/271/240.md +82 -0
  759. package/grades/grade-2/agents/chinese-tutor/activities//351/242/204/344/271/240./344/276/213/345/255/220.md +35 -0
  760. package/grades/grade-2/agents/chinese-tutor.md +59 -0
  761. package/grades/grade-2/agents/english-tutor/activities//345/217/243/350/257/255/350/257/276.md +149 -0
  762. package/grades/grade-2/agents/english-tutor/activities//345/217/243/350/257/255/350/257/276./344/276/213/345/255/220.md +13 -0
  763. package/grades/grade-2/agents/english-tutor/activities//345/220/254/345/206/231.md +47 -0
  764. package/grades/grade-2/agents/english-tutor/activities//345/244/215/344/271/240.md +78 -0
  765. package/grades/grade-2/agents/english-tutor/activities//345/244/215/344/271/240./344/276/213/345/255/220.md +35 -0
  766. package/grades/grade-2/agents/english-tutor/activities//345/260/217/350/257/276/345/240/202.md +53 -0
  767. package/grades/grade-2/agents/english-tutor/activities//346/243/200/346/237/245/344/275/234/344/270/232.md +80 -0
  768. package/grades/grade-2/agents/english-tutor/activities//346/243/200/346/237/245/344/275/234/344/270/232./344/276/213/345/255/220.md +35 -0
  769. package/grades/grade-2/agents/english-tutor/activities//351/242/204/344/271/240.md +86 -0
  770. package/grades/grade-2/agents/english-tutor/activities//351/242/204/344/271/240./344/276/213/345/255/220.md +35 -0
  771. package/grades/grade-2/agents/english-tutor.md +57 -0
  772. package/grades/grade-2/agents/math-tutor/activities//345/244/215/344/271/240.md +78 -0
  773. package/grades/grade-2/agents/math-tutor/activities//345/244/215/344/271/240./344/276/213/345/255/220.md +35 -0
  774. package/grades/grade-2/agents/math-tutor/activities//345/260/217/350/257/276/345/240/202.md +56 -0
  775. package/grades/grade-2/agents/math-tutor/activities//345/260/217/350/257/276/345/240/202./344/276/213/345/255/220.md +52 -0
  776. package/grades/grade-2/agents/math-tutor/activities//346/243/200/346/237/245/344/275/234/344/270/232.md +80 -0
  777. package/grades/grade-2/agents/math-tutor/activities//346/243/200/346/237/245/344/275/234/344/270/232./344/276/213/345/255/220.md +35 -0
  778. package/grades/grade-2/agents/math-tutor/activities//351/242/204/344/271/240.md +81 -0
  779. package/grades/grade-2/agents/math-tutor/activities//351/242/204/344/271/240./344/276/213/345/255/220.md +35 -0
  780. package/grades/grade-2/agents/math-tutor.md +51 -0
  781. package/grades/grade-2/examples/2026-10-27-pingjunfen/audio/index.json +44 -0
  782. package/grades/grade-2/examples/2026-10-27-pingjunfen/audio/step-1.mp3 +0 -0
  783. package/grades/grade-2/examples/2026-10-27-pingjunfen/audio/step-2.mp3 +0 -0
  784. package/grades/grade-2/examples/2026-10-27-pingjunfen/audio/step-3.mp3 +0 -0
  785. package/grades/grade-2/examples/2026-10-27-pingjunfen/audio/step-4.mp3 +0 -0
  786. package/grades/grade-2/examples/2026-10-27-pingjunfen/audio/step-5.mp3 +0 -0
  787. package/grades/grade-2/examples/2026-10-27-pingjunfen/bake.json +1 -0
  788. package/grades/grade-2/examples/2026-10-27-pingjunfen/manifest.json +63 -0
  789. package/grades/grade-2/examples/2026-10-27-pingjunfen/scene.json +247 -0
  790. package/grades/grade-2/examples/2026-10-27-pingjunfen.md +40 -0
  791. package/grades/grade-2/examples/2026-10-27-pingjunfen.ts +26 -0
  792. package/grades/grade-2/examples//345/271/263/345/235/207/345/210/206./344/276/213/345/255/220.md +24 -0
  793. package/grades/grade-2/examples//346/225/260/345/255/246/345/271/263/345/235/207/345/210/206.md +42 -0
  794. package/grades/grade-2/validation/2026-10-27.json +4410 -0
  795. package/package.json +27 -15
  796. package/skills/chalk-cli/SKILL.md +62 -0
  797. package/skills/chalk-scene/SKILL.md +91 -0
  798. package/skills/chalk-teaching/SKILL.md +82 -0
  799. package/skills/chalk-teaching/models//344/272/244/351/224/231/346/225/260/345/210/227/345/210/206/350/241/214.md +8 -0
  800. package/skills/chalk-teaching/models//345/207/221/345/215/201.md +7 -0
  801. package/skills/chalk-teaching/models//345/221/250/346/234/237/350/247/204/345/276/213.md +6 -0
  802. package/skills/chalk-teaching/models//345/256/236/347/211/251/345/233/276-/345/242/236/345/207/217.md +6 -0
  803. package/skills/chalk-teaching/models//345/256/236/347/211/251/345/233/276-/346/257/224/345/244/232/345/260/221-/346/261/202/345/244/247/346/225/260/346/261/202/345/260/217/346/225/260.md +7 -0
  804. package/skills/chalk-teaching/models//345/256/236/347/211/251/345/233/276-/346/257/224/345/244/232/345/260/221-/346/261/202/345/267/256.md +6 -0
  805. package/skills/chalk-teaching/models//345/260/217/346/243/222/350/277/233/344/275/215/345/212/240.md +7 -0
  806. package/skills/chalk-teaching/models//345/260/217/346/243/222/351/200/200/344/275/215/345/207/217.md +6 -0
  807. package/skills/chalk-teaching/models//346/216/222/351/230/237/347/202/271/351/230/265.md +7 -0
  808. package/skills/chalk-teaching/models//346/227/266/351/227/264/346/225/260/350/275/264.md +6 -0
  809. package/skills/chalk-teaching/models//346/245/274/345/261/202/351/227/264/351/232/224/345/233/276.md +7 -0
  810. package/skills/chalk-teaching/models//347/240/264/345/215/201.md +7 -0
  811. package/skills/chalk-teaching/models//347/253/226/345/274/217/351/200/200/344/275/215.md +6 -0
  812. package/skills/chalk-teaching/models//347/272/277/346/256/265/345/233/276.md +7 -0
  813. package/skills/chalk-teaching/models-index.md +24 -0
  814. package/skills/chalk-teaching/references//350/256/262/350/247/243/346/250/241/346/235/277/344/270/211/346/241/243.md +32 -0
  815. package/skills/chalk-teaching/references//350/257/264/346/230/216/346/226/207/344/273/266.md +102 -0
  816. package/skills/chalk-verify/SKILL.md +57 -0
  817. package/skills/chalk-verify/references//350/257/204/345/210/244/346/240/207/345/207/206.md +69 -0
  818. package/skills/cotutor-analyze/SKILL.md +106 -0
  819. package/skills/cotutor-analyze/references//345/221/275/344/273/244/344/270/216/346/226/207/344/273/266.md +31 -0
  820. package/skills/cotutor-board/SKILL.md +373 -0
  821. package/skills/cotutor-board/references/README.md +38 -0
  822. package/skills/cotutor-board/references/canvas.md +68 -0
  823. package/skills/cotutor-board/references/choice.md +55 -0
  824. package/skills/cotutor-board/references/code.md +46 -0
  825. package/skills/cotutor-board/references/image.md +52 -0
  826. package/skills/cotutor-board/references/lecture.md +69 -0
  827. package/skills/cotutor-board/references/paint.md +77 -0
  828. package/skills/cotutor-board/references/photo.md +61 -0
  829. package/skills/cotutor-board/references/read.md +51 -0
  830. package/skills/cotutor-board/references/text.md +69 -0
  831. package/skills/cotutor-board/references/tianzige.md +78 -0
  832. package/skills/cotutor-board/references/word.md +91 -0
  833. package/skills/cotutor-home/SKILL.md +170 -0
  834. package/skills/cotutor-home/references/dictation.md +97 -0
  835. package/skills/cotutor-home/references/tutor.md +149 -0
  836. package/skills/cotutor-home/references//345/221/275/344/273/244.md +21 -0
  837. package/skills/cotutor-home/references//351/246/226/351/241/265/350/257/255/346/263/225.md +143 -0
  838. package/skills/cotutor-tune/SKILL.md +147 -0
  839. package/skills/cotutor-tune/references//345/255/227/346/256/265.md +53 -0
  840. package/skills/cotutor-tutor/SKILL.md +253 -0
  841. package/skills/cotutor-tutor/references//344/270/212/344/270/213/346/226/207/345/214/205.md +78 -0
  842. package/themes/default/kid.css +86 -0
  843. package/themes/default/theme.json +26 -0
  844. package/agents/chinese-tutor.md +0 -67
  845. package/agents/homework-tutor.md +0 -17
  846. package/agents/math-tutor.md +0 -68
  847. package/agents/planner.md +0 -18
  848. package/agents/reading-tutor.md +0 -65
  849. package/agents/scene-maker.md +0 -41
  850. package/dist/cards/canvas.d.ts +0 -22
  851. package/dist/cards/canvas.js +0 -65
  852. package/dist/cards/fill.d.ts +0 -14
  853. package/dist/cards/fill.js +0 -64
  854. package/dist/cards/index.d.ts +0 -48
  855. package/dist/cards/index.js +0 -115
  856. package/dist/cards/kind.js +0 -7
  857. package/dist/cards/scene.d.ts +0 -24
  858. package/dist/cards/scene.js +0 -67
  859. package/dist/cards/text.d.ts +0 -18
  860. package/dist/cards/text.js +0 -41
  861. package/dist/cli/cert.d.ts +0 -7
  862. package/dist/cli/cert.js +0 -36
  863. package/dist/cli/doctor.d.ts +0 -22
  864. package/dist/cli/doctor.js +0 -355
  865. package/dist/cli/init.js +0 -116
  866. package/dist/cli/main.js +0 -243
  867. package/dist/cli/serve.d.ts +0 -30
  868. package/dist/cli/serve.js +0 -56
  869. package/dist/cli/skeleton.d.ts +0 -39
  870. package/dist/cli/skeleton.js +0 -184
  871. package/dist/cli/skills.d.ts +0 -40
  872. package/dist/cli/skills.js +0 -170
  873. package/dist/cli/workspace.d.ts +0 -69
  874. package/dist/lib/agent-file.d.ts +0 -10
  875. package/dist/lib/agent-file.js +0 -16
  876. package/dist/lib/board.d.ts +0 -15
  877. package/dist/lib/board.js +0 -107
  878. package/dist/lib/context-pack.js +0 -55
  879. package/dist/lib/conversation.js +0 -88
  880. package/dist/lib/kid-board.d.ts +0 -148
  881. package/dist/lib/kid-board.js +0 -294
  882. package/dist/lib/kid-view.d.ts +0 -63
  883. package/dist/lib/kid-view.js +0 -93
  884. package/dist/lib/ledger.d.ts +0 -29
  885. package/dist/lib/run-plan.d.ts +0 -30
  886. package/dist/lib/run-plan.js +0 -32
  887. package/dist/lib/sections.d.ts +0 -12
  888. package/dist/lib/sections.js +0 -151
  889. package/dist/lib/stream.d.ts +0 -15
  890. package/dist/lib/stream.js +0 -69
  891. package/dist/lib/transcript.d.ts +0 -36
  892. package/dist/lib/transcript.js +0 -95
  893. package/dist/server/app.d.ts +0 -74
  894. package/dist/server/app.js +0 -406
  895. package/dist/server/kid-page.d.ts +0 -1
  896. package/dist/server/kid-page.js +0 -768
  897. package/dist/server/mock.js +0 -403
  898. package/dist/server/parent-page.d.ts +0 -8
  899. package/dist/server/parent-page.js +0 -393
  900. package/dist/server/runner.d.ts +0 -64
  901. package/dist/server/runner.js +0 -277
  902. package/dist/server/scene-props.d.ts +0 -16
  903. package/dist/server/scene-props.js +0 -39
  904. package/dist/server/store.d.ts +0 -43
  905. package/dist/server/store.js +0 -210
  906. package/dist/server/tts.d.ts +0 -51
  907. package/dist/server/tts.js +0 -99
  908. /package/dist/{cli → app/cli}/main.d.ts +0 -0
  909. /package/dist/{cards → board/cards}/choice.d.ts +0 -0
  910. /package/dist/{cards → board/cards}/code.d.ts +0 -0
  911. /package/dist/{cards → board/cards}/image.d.ts +0 -0
  912. /package/dist/{cards → board/cards}/read.d.ts +0 -0
@@ -0,0 +1,124 @@
1
+ import { type Bookkeeping, type ConversationMessage } from '../schema/index.ts';
2
+ import { type CardAssets, type CardStates } from '../schema/index.ts';
3
+ import type { BoardSection, KidDrawing, KidLecture } from '../board/presentation.ts';
4
+ import type { Transcript } from '../brain/transcript.ts';
5
+ export interface KidView {
6
+ /** 给孩子的话(讲稿各句,换行分隔);null = 这次运行没有(出错、还在跑、或最终文本剥完是空的) */
7
+ kidText: string | null;
8
+ truncated: boolean;
9
+ /** 记账任务的回答(「## 记账」段);应用按它写日记 */
10
+ bookkeeping: Bookkeeping | null;
11
+ /** 「## 记忆」段的条目;应用追加进 vault 的记忆文件 */
12
+ memory: string[];
13
+ /** 运行是否正常收尾 */
14
+ ok: boolean;
15
+ /** 板书节(卡 + 讲稿);没有卡也没有讲稿 = null */
16
+ section: BoardSection | null;
17
+ /** 解析时的提醒(卡没解析成等),家长视图转录里显示 */
18
+ warnings: string[];
19
+ /** 第一个 H2 起给家长的尾巴(老师写的任何标题段);没有 = 空串 */
20
+ parentText: string;
21
+ }
22
+ /** 按码点数截断;能在句末断就在句末断(不加省略号),否则硬截加 … */
23
+ export declare function truncateReply(text: string, max: number): {
24
+ text: string;
25
+ truncated: boolean;
26
+ };
27
+ /**
28
+ * 孩子看的正文从哪来:`result.result` 只是这轮**最后一段**顶层文本——老师写完板书再用一次工具、再补一句,
29
+ * 板书和「## 记账」段就没了。所以正文 = 这轮里**带围栏(卡)或带 H2 固定段的顶层文本段** + **最后一段**(顺序不变,重复不算两次);
30
+ * 中间那些「我先看看账本」之类没卡没段的话不进。子代理的话本来就不在顶层。
31
+ */
32
+ export declare function kidSource(t: Transcript): string | null;
33
+ /** opts.live = 实时的一轮:解析器不补提问卡(src/board/parse.ts) */
34
+ export declare function deriveKidView(t: Transcript, policy: {
35
+ replyMaxChars: number;
36
+ }, opts?: {
37
+ live?: boolean;
38
+ }): KidView;
39
+ /** 孩子端的一条:自己问的话(别人问的不显示)+ 老师给孩子的话 + 配音;出错的运行什么都不出现(问句还在) */
40
+ export interface KidMessage {
41
+ job: string;
42
+ /** 话题 id(话题第一条的 job) */
43
+ thread: string;
44
+ at: string;
45
+ /** 孩子自己说的;系统发的不给孩子看,为 null */
46
+ question: string | null;
47
+ /** 老师给孩子的话;还在跑或这轮没有 = null */
48
+ reply: string | null;
49
+ /** 老师还在想 */
50
+ pending: boolean;
51
+ /** 本次运行新增的产物 id */
52
+ artifacts: string[];
53
+ /** 板书节(卡 + 讲稿;答案等秘密已剥;配音在讲稿句上) */
54
+ section?: BoardSection | null;
55
+ /** 孩子这条带的作业照片(相对 workspace 根;页面经 /api/kid/image?p= 取);只在孩子自己的问句上 */
56
+ photos?: string[];
57
+ /** 这条是看完小课堂后的第一条:哪份课包、课名、画过的几处(节前的小课堂卡与画的卡从这里画);again = 再看一遍又画的那条,节前只画画的卡 */
58
+ lecture?: KidLecture;
59
+ /** 孩子从左栏的纸交的画:节前一张孩子的画卡 */
60
+ drawing?: KidDrawing;
61
+ /** 孩子的话怎么上板:交卡不写;首页按钮发的(正文就是按钮上的字)不写,从按钮进来后孩子自己说的照写 */
62
+ action?: 'submit';
63
+ fromHome?: true;
64
+ /** 这一轮是实时的:讲稿不配音、嘴照念;不出卡的一轮板上留讲稿的字,还在跑时有句就出节 */
65
+ live?: true;
66
+ }
67
+ /**
68
+ * 对话索引 → 孩子端条目(的机械过滤在服务端做):不带 result / error / 费用 / 家长尾巴;
69
+ * 卡上的答案剥掉,孩子自己做的状态(states)与已生成的资产(assets,都从 .cards/ 读)并到卡上。出错的运行:没有 question 的直接不出现;有 question 的只留问句(老师头像不灰,下一条照常)。
70
+ */
71
+ export declare function kidConversation(index: {
72
+ messages: readonly ConversationMessage[];
73
+ }, states?: CardStates, assets?: CardAssets): KidMessage[];
74
+ /**
75
+ * 家长板书页的一条:形状同 KidMessage,家长多看到的都是可选字段;
76
+ * `section` **不剥答案**;`question` 不管谁发的都给;记账 / 整理记忆那两种轮也在(旁注写「已记进日记」「整理了记忆」)。
77
+ */
78
+ export interface ParentMessage extends KidMessage {
79
+ from: ConversationMessage['from'];
80
+ via?: ConversationMessage['via'];
81
+ action?: ConversationMessage['action'];
82
+ /** 这条带给老师的卡(上一轮之后孩子改过状态的):「孩子在板书上做的」;took = 在弹窗里想了多久(毫秒,没改过 = null)、改过几次(有实录才有,app.ts parentDay 挂上) */
83
+ cards?: (NonNullable<ConversationMessage['cards']>[number] & {
84
+ took?: {
85
+ think: number | null;
86
+ changes: number;
87
+ };
88
+ })[];
89
+ parentText?: string;
90
+ remembered?: string[];
91
+ warnings?: string[];
92
+ /** 不 ok 时的原因;孩子端什么都不出现,家长页出一行 */
93
+ error?: string;
94
+ bookkeep?: ConversationMessage['bookkeep'];
95
+ tidy?: true;
96
+ /** 家长端多看到看的情况:看了多久、看完没、停过几次 */
97
+ lectureWatch?: Pick<NonNullable<ConversationMessage['lecture']>, 'watchedMs' | 'finished' | 'pauses'>;
98
+ }
99
+ /** 对话索引 → 家长板书页条目:和 kidConversation 同一个循环,差集恰好是 ParentMessage 里多出的字段与「答案不剥」 */
100
+ export declare function parentConversation(index: {
101
+ messages: readonly ConversationMessage[];
102
+ }, states?: CardStates, assets?: CardAssets): ParentMessage[];
103
+ export interface KidThread {
104
+ thread: string;
105
+ /** 话题第一条的时间 */
106
+ at: string;
107
+ /** 话题名:孩子的第一句话截 20 字;没有孩子的话 → 「老师主动说的」 */
108
+ title: string;
109
+ /** 老师讲了几节 */
110
+ sections: number;
111
+ /** 一共几张卡 */
112
+ cards: number;
113
+ }
114
+ /**
115
+ * 「以前的」列表:一天的孩子端条目按话题分组。一句孩子的话都没有的话题不列;还在跑的、出错的轮不算节。
116
+ */
117
+ export declare function kidThreads(messages: readonly KidMessage[]): KidThread[];
118
+ /**
119
+ * 今天交给老师过的卡(`<job>/<n>`):交答案那条的 focus.card。交过的卡锁住——不能改、不能再交(2026-10-21,Jack:
120
+ * 孩子改了交过的卡的答案又交一次,老师再答一遍,孩子糊涂)。
121
+ */
122
+ export declare function submittedCards(index: {
123
+ messages: readonly ConversationMessage[];
124
+ }): Set<string>;
@@ -0,0 +1,157 @@
1
+ /**
2
+ * 孩子视图(2026-09-10 改板书):最终文本剥掉「记账」段后,正文就是板书——
3
+ * 普通段落是讲稿(一行一句),围栏是卡(src/board/parse.ts 解析);kidText = 讲稿各句拼起来(家长视图的「孩子看到」摘要),
4
+ * 每句按 replyMaxChars 截;第一个 H2 起是给家长的尾巴(parentText),孩子看不到。出错什么都不出现。机械规则,不靠模型判断。
5
+ * 正文取自 kidSource:带卡 / 带固定段的顶层文本段 + 最后一段(老师板书之后又用了工具也不丢)。
6
+ */
7
+ import { stripSecrets } from "../board/cards/index.js";
8
+ import {} from "../schema/index.js";
9
+ import { parseBoard } from "../board/parse.js";
10
+ import { threads } from "../schema/index.js";
11
+ import { parseSections } from "../board/sections.js";
12
+ const SENTENCE_END = new Set(['。', '!', '!', '?', '?', ';', ';', '\n']);
13
+ /** 按码点数截断;能在句末断就在句末断(不加省略号),否则硬截加 … */
14
+ export function truncateReply(text, max) {
15
+ const cps = Array.from(text);
16
+ if (cps.length <= max)
17
+ return { text, truncated: false };
18
+ let cut = -1;
19
+ for (let i = Math.min(max, cps.length) - 1; i >= Math.floor(max * 0.4); i--) {
20
+ if (SENTENCE_END.has(cps[i])) {
21
+ cut = i + 1;
22
+ break;
23
+ }
24
+ }
25
+ if (cut > 0)
26
+ return { text: cps.slice(0, cut).join('').trim(), truncated: true };
27
+ return { text: `${cps.slice(0, max).join('').trim()}…`, truncated: true };
28
+ }
29
+ const none = (ok, bookkeeping = null) => ({ kidText: null, truncated: false, bookkeeping, memory: [], ok, section: null, warnings: [], parentText: '' });
30
+ const FENCE_OR_H2 = /^\s*(?:```|~~~|## )/m;
31
+ /**
32
+ * 孩子看的正文从哪来:`result.result` 只是这轮**最后一段**顶层文本——老师写完板书再用一次工具、再补一句,
33
+ * 板书和「## 记账」段就没了。所以正文 = 这轮里**带围栏(卡)或带 H2 固定段的顶层文本段** + **最后一段**(顺序不变,重复不算两次);
34
+ * 中间那些「我先看看账本」之类没卡没段的话不进。子代理的话本来就不在顶层。
35
+ */
36
+ export function kidSource(t) {
37
+ if (!t.final?.text)
38
+ return null;
39
+ const last = t.final.text.trim();
40
+ const blocks = t.items.filter((i) => i.kind === 'text' && !i.sub).map((i) => i.text.trim());
41
+ // 已经拼在最后一段里的(工具调用写成正文那次的正文,runner 接在 resume 那次前面)不再算一次
42
+ const kept = blocks.filter((b) => !last.includes(b) && FENCE_OR_H2.test(b));
43
+ return [...kept, last].join('\n\n');
44
+ }
45
+ /** opts.live = 实时的一轮:解析器不补提问卡(src/board/parse.ts) */
46
+ export function deriveKidView(t, policy, opts = {}) {
47
+ if (!t.final)
48
+ return none(false);
49
+ if (!t.final.ok || !t.final.text)
50
+ return none(t.final.ok);
51
+ const { body, bookkeeping, memory } = parseSections(kidSource(t) ?? t.final.text);
52
+ const board = parseBoard(body, { live: opts.live });
53
+ const { cards, lines } = board.section;
54
+ if (!cards.length && !lines.length)
55
+ return { ...none(true, bookkeeping), memory, warnings: board.warnings.map((w) => w.text), parentText: board.tail };
56
+ let truncated = false;
57
+ const cut = lines.map((l) => {
58
+ const r = truncateReply(l.text, policy.replyMaxChars);
59
+ truncated = truncated || r.truncated;
60
+ return { ...l, text: r.text };
61
+ });
62
+ const section = { ...board.section, lines: cut };
63
+ const kidText = cut.map((l) => l.text).join('\n');
64
+ return { kidText: kidText || null, truncated, bookkeeping, memory, ok: true, section, warnings: board.warnings.map((w) => w.text), parentText: board.tail };
65
+ }
66
+ /** 索引里的小课堂 → 孩子端(画不带那段话)/ 家长端(带) */
67
+ /** 孩子的画卡要的:图、几笔、画了多久 */
68
+ function drawingView(d) {
69
+ return { image: d.image, strokes: d.strokes, ...(d.ms !== undefined ? { ms: d.ms } : {}) };
70
+ }
71
+ function lectureView(l, parent) {
72
+ return { bundle: l.bundle, title: l.title, ...(l.again ? { again: true } : {}), ...(l.video ? { video: true } : {}), ...(l.marks?.length ? { marks: l.marks.map((m) => ({ atMs: m.atMs, svgMs: m.svgMs, strokes: m.strokes, ...(m.image ? { image: m.image } : {}), ...(parent && m.text ? { text: m.text } : {}) })) } : {}) };
73
+ }
74
+ /** 孩子做的状态(states)与已生成的资产(assets,都从 .cards/ 读)并到这轮的卡上;都没有就原样 */
75
+ function cardsWithState(m, states, assets, submitted = new Set()) {
76
+ const per = states[m.job];
77
+ const files = assets[m.job];
78
+ const done = [...submitted].some((id) => id.startsWith(`${m.job}/`));
79
+ return m.section && (per || files || done) ? { ...m.section, cards: m.section.cards.map((c, n) => ({ ...c, ...(submitted.has(`${m.job}/${n}`) ? { submitted: true } : {}), ...(per?.[n] ? { state: per[n].state } : {}), ...(files?.[n]?.length ? { assets: files[n] } : {}) })) } : m.section;
80
+ }
81
+ /**
82
+ * 对话索引 → 孩子端条目(的机械过滤在服务端做):不带 result / error / 费用 / 家长尾巴;
83
+ * 卡上的答案剥掉,孩子自己做的状态(states)与已生成的资产(assets,都从 .cards/ 读)并到卡上。出错的运行:没有 question 的直接不出现;有 question 的只留问句(老师头像不灰,下一条照常)。
84
+ */
85
+ export function kidConversation(index, states = {}, assets = {}) {
86
+ const out = [];
87
+ const ths = threads(index.messages);
88
+ const submitted = submittedCards(index);
89
+ for (const [i, m] of index.messages.entries()) {
90
+ // 记账那轮是家长晚上起的任务,老师回的「记好了」不是给孩子的话
91
+ if (m.bookkeep || m.tidy)
92
+ continue;
93
+ const question = m.from === 'kid' ? m.text : null;
94
+ const reply = m.result === 'ok' ? (m.kidText ?? null) : null;
95
+ const pending = m.result === 'running';
96
+ if (question === null && reply === null && !pending)
97
+ continue;
98
+ const withState = cardsWithState(m, states, assets, submitted);
99
+ const section = m.result === 'ok' && withState ? stripSecrets(withState) : undefined;
100
+ out.push({ job: m.job, thread: ths[i], at: m.at, question, reply, pending, artifacts: reply ? [...m.artifacts] : [], ...(section ? { section } : {}), ...(question !== null && m.photos?.length ? { photos: [...m.photos] } : {}), ...(question !== null && m.drawing ? { drawing: drawingView(m.drawing) } : {}), ...(m.lecture ? { lecture: lectureView(m.lecture, false) } : {}), ...(question !== null && m.action ? { action: m.action } : {}), ...(question !== null && m.via && question.trim() === m.via.label.trim() ? { fromHome: true } : {}), ...(m.live ? { live: true } : {}) });
101
+ }
102
+ return out;
103
+ }
104
+ /** 对话索引 → 家长板书页条目:和 kidConversation 同一个循环,差集恰好是 ParentMessage 里多出的字段与「答案不剥」 */
105
+ export function parentConversation(index, states = {}, assets = {}) {
106
+ const out = [];
107
+ const ths = threads(index.messages);
108
+ for (const [i, m] of index.messages.entries()) {
109
+ const reply = m.result === 'ok' ? (m.kidText ?? null) : null;
110
+ const pending = m.result === 'running';
111
+ const withState = cardsWithState(m, states, assets);
112
+ const section = m.result === 'ok' && withState ? withState : undefined;
113
+ out.push({
114
+ job: m.job, thread: ths[i], at: m.at, from: m.from, question: m.text, reply, pending, artifacts: reply ? [...m.artifacts] : [],
115
+ ...(section ? { section } : {}),
116
+ ...(m.photos?.length ? { photos: [...m.photos] } : {}),
117
+ ...(m.drawing ? { drawing: drawingView(m.drawing) } : {}),
118
+ ...(m.via ? { via: m.via } : {}),
119
+ ...(m.action ? { action: m.action } : {}),
120
+ ...(m.cards?.length ? { cards: m.cards } : {}),
121
+ ...(m.parentText ? { parentText: m.parentText } : {}),
122
+ ...(m.remembered?.length ? { remembered: m.remembered } : {}),
123
+ ...(m.warnings?.length ? { warnings: m.warnings } : {}),
124
+ ...(m.result === 'error' ? { error: m.error ?? '没成' } : {}),
125
+ ...(m.bookkeep ? { bookkeep: m.bookkeep } : {}),
126
+ ...(m.tidy ? { tidy: true } : {}),
127
+ ...(m.lecture ? { lecture: lectureView(m.lecture, true), lectureWatch: { watchedMs: m.lecture.watchedMs, finished: m.lecture.finished, pauses: m.lecture.pauses } } : {}),
128
+ });
129
+ }
130
+ return out;
131
+ }
132
+ /**
133
+ * 「以前的」列表:一天的孩子端条目按话题分组。一句孩子的话都没有的话题不列;还在跑的、出错的轮不算节。
134
+ */
135
+ export function kidThreads(messages) {
136
+ const by = new Map();
137
+ for (const m of messages) {
138
+ const t = by.get(m.thread) ?? { thread: m.thread, at: m.at, title: '', sections: 0, cards: 0, asked: false };
139
+ if (m.question && !t.asked) {
140
+ t.asked = true;
141
+ t.title = m.question.trim().slice(0, 20);
142
+ }
143
+ if (m.reply !== null || (m.section && !m.pending)) {
144
+ t.sections++;
145
+ t.cards += m.section?.cards.length ?? 0;
146
+ }
147
+ by.set(m.thread, t);
148
+ }
149
+ return [...by.values()].filter((t) => t.asked).map(({ asked: _a, ...t }) => t);
150
+ }
151
+ /**
152
+ * 今天交给老师过的卡(`<job>/<n>`):交答案那条的 focus.card。交过的卡锁住——不能改、不能再交(2026-10-21,Jack:
153
+ * 孩子改了交过的卡的答案又交一次,老师再答一遍,孩子糊涂)。
154
+ */
155
+ export function submittedCards(index) {
156
+ return new Set(index.messages.flatMap((m) => (m.action === 'submit' && m.focus?.card ? [m.focus.card] : [])));
157
+ }
@@ -0,0 +1,86 @@
1
+ import { type LectureClock, type LectureMark, type LecturePicture } from '../board/lecture.ts';
2
+ import type { ContextPack } from '../schema/index.ts';
3
+ import type { BoardSection } from '../board/presentation.ts';
4
+ export interface Lecture {
5
+ id: string;
6
+ title: string;
7
+ problem: string;
8
+ /** 课包的科目(scene.json 的 subject);没写 = null */
9
+ subject: string | null;
10
+ clock: LectureClock;
11
+ /** 画面的语义(课包的 groups、blocks 与 build 时量好的 bounds):画的地方盖着什么靠它说;视频没有 */
12
+ picture: LecturePicture;
13
+ /** 视频(lectures/<id>/video.mp4);false = 课包 */
14
+ video: boolean;
15
+ /** 课包烤过(bake.json 和画面对得上):孩子端不用 excalidraw 现画,缩略图服务端出 */
16
+ baked?: boolean;
17
+ }
18
+ /** 读小课堂要的目录:课包,与视频(没给就只认课包) */
19
+ export interface LectureDirs {
20
+ dirs: {
21
+ bundles: string;
22
+ lectures?: string;
23
+ };
24
+ }
25
+ export declare function readLecture(ws: LectureDirs, id: string): Promise<Lecture | null>;
26
+ /** 读一份小课堂:课包先,再视频;读不出来的,problems 说为什么(首页检查用) */
27
+ export declare function inspectLecture(ws: LectureDirs, id: string): Promise<{
28
+ lecture: Lecture | null;
29
+ problems: string[];
30
+ }>;
31
+ /** 孩子看完带来的:看了多久、看完没、停过几次;again = 问过以后「再看一遍」又画了(这条只为带画的) */
32
+ export interface LectureWatch {
33
+ watchedMs: number;
34
+ finished: boolean;
35
+ pauses: number;
36
+ again?: boolean;
37
+ }
38
+ /** 孩子带来的一处画;一处一张截好的图(captures/ 里,画面叠上这一刻的几笔;截不出来就没有) */
39
+ export interface IncomingMark extends LectureMark {
40
+ image?: string;
41
+ /** 画的地方盖着什么,写死的(cotutor activity eval 的例子「画:」):不按笔画现算,时刻与那句讲稿照接 */
42
+ hit?: string;
43
+ /** 画下去的那一刻:离这条消息发出去多少毫秒(≤ 0;录像用) */
44
+ t?: number;
45
+ }
46
+ /** 落盘的一处画:时刻、SVG 停在哪(画缩略图用,毫秒)、几笔(课包坐标;视频是视频自己的像素坐标)、给老师的那段话(读不出来时没有)、截图 */
47
+ export interface StoredMark extends IncomingMark {
48
+ svgMs: number;
49
+ text?: string;
50
+ }
51
+ /**
52
+ * 孩子带来的画 → 落盘的样子:那段话 = 时刻与那时在讲哪句 + 截图是上下文包 photos 的第几张(photoBase = 前面已经有几张孩子自己拍的)
53
+ * + 课包的再说画的地方盖着什么(按课包现算,用课里的叫法)。读不出来就只留时刻与笔画
54
+ */
55
+ export declare function storedMarks(l: Lecture | null, marks: readonly IncomingMark[], photoBase?: number): StoredMark[];
56
+ /** 上下文包 lecture: 段:标题、来源、课长、每句起点与原话、看的情况、画过的几处 */
57
+ export declare function lecturePack(l: Lecture, w: LectureWatch, marks?: readonly StoredMark[]): NonNullable<ContextPack['lecture']>;
58
+ /**
59
+ * 小课堂卡(cards/lecture)下发时补的快照:课名、真放的起止(对齐段界)、末帧停在 SVG 的哪一刻、能不能放。
60
+ * 课包每次现读(家长重做了课包,下一次下发就跟上);读不出来或起止对不上 → ready: false,讲稿 [[play]] 不停。
61
+ */
62
+ export declare function enrichLectures(ws: LectureDirs, section: BoardSection): Promise<BoardSection>;
63
+ /**
64
+ * 课包没烤过(或画面改过没重烤):起一次 cotutor chalk bake(同一份课包同时只烤一次;要本机的 Chrome,src/app/cli/chalk/)。
65
+ * 烤好 true;烤不了(没浏览器、课包坏了、超时)false,不抛。
66
+ */
67
+ export declare function ensureBaked(bundlesDir: string, id: string): Promise<boolean>;
68
+ /**
69
+ * 孩子端要用到的课包(首页的小课堂、lecture 卡)没烤过:后台烤一次,不等(下一次下发就是烤好的)。
70
+ * 一个服务进程里每份课包只看一次;烤不了照样放(孩子端在浏览器里现烤)。
71
+ */
72
+ export declare function bakeSoon(bundlesDir: string, id: string): void;
73
+ /** 一节板书里 lecture 卡用到的课包,没烤过的后台烤 */
74
+ export declare function bakeSectionSoon(bundlesDir: string, section: BoardSection): void;
75
+ /**
76
+ * 课包在 SVG 时刻 svgMs 的画面(画的卡、lecture 卡的缩略图,播放器截图的底):用烤好的画面(bake.json)在服务端现画成一张 SVG,
77
+ * 有画就叠上那几笔(课包坐标,加上导出时的平移)。没烤过、画面改过没重烤:给了 bake(路由给 ensureBaked)就先烤再画,烤不了 → null。
78
+ * 背景图内嵌成 data URI:SVG 当 <img> 用时不去取外面的文件。
79
+ */
80
+ export declare function lectureFrameSvg(ws: Pick<LectureDirs, 'dirs'>, id: string, svgMs: number, ink?: readonly (readonly [number, number][])[], opts?: {
81
+ bake?: (bundlesDir: string, id: string) => Promise<boolean>;
82
+ }): Promise<string | null>;
83
+ /** frame.svg 的 svg 参数:SVG 时刻(毫秒);`end` = 最后一帧(小课堂卡的缩略图);认不出来 = 0 */
84
+ export declare function parseSvgMs(s: string | null): number;
85
+ /** frame.svg 的 ring 参数:一笔「x,y;x,y;…」,笔与笔之间「~」(课包坐标,取整;一共最多 MARK_MAX_POINTS 个点);认不出来 = 没有 */
86
+ export declare function parseRing(s: string | null): [number, number][][];
@@ -0,0 +1,237 @@
1
+ /**
2
+ * 小课堂读盘的那一半:从 bundles/<id>/ 读课包,或从 lectures/<id>/ 读视频(lecture.md + video.mp4,第 4 步),拼时钟(src/board/lecture.ts),
3
+ * 给首页按钮的标题与课长、首页检查、上下文包 lecture: 用。同一个 id 两边都有,课包先。坏了、不在都回 null,不抛;为什么读不出来给首页检查(inspectLecture)。
4
+ */
5
+ import { spawn } from 'node:child_process';
6
+ import { readFile, stat } from 'node:fs/promises';
7
+ import { join } from 'node:path';
8
+ import { fileURLToPath } from 'node:url';
9
+ import { BUNDLE_ID_RE } from "../board/cards/kind.js";
10
+ import { mp4DurationMs } from "../media/mp4.js";
11
+ import { LECTURE_FILE, LECTURE_VIDEO, MARK_MAX_POINTS, clockLabel, inkHint, lectureAt, lectureClock, lectureLines, lectureRange, markHead, parseLectureDoc, videoClock } from "../board/lecture.js";
12
+ import { bakeMatches, BundleError, elementsAtSvgTime, parseBundle, renderFrameSvg } from "../chalk/index.js";
13
+ export async function readLecture(ws, id) {
14
+ return (await inspectLecture(ws, id)).lecture;
15
+ }
16
+ /** 读一份小课堂:课包先,再视频;读不出来的,problems 说为什么(首页检查用) */
17
+ export async function inspectLecture(ws, id) {
18
+ if (!BUNDLE_ID_RE.test(id))
19
+ return { lecture: null, problems: [`${id} 不是合规的 id(小写字母、数字、连字符)`] };
20
+ const bundle = await readBundleLecture(ws, id);
21
+ if (bundle.lecture)
22
+ return { lecture: bundle.lecture, problems: [] };
23
+ if (bundle.problem)
24
+ return { lecture: null, problems: [bundle.problem] };
25
+ if (!ws.dirs.lectures)
26
+ return { lecture: null, problems: [`bundles/${id}/ 不在或读不出来(要有 scene.json 与带步的 manifest.json)`] };
27
+ const dir = join(ws.dirs.lectures, id);
28
+ const md = await readFile(join(dir, LECTURE_FILE), 'utf8').catch(() => null);
29
+ const hasVideo = Boolean((await stat(join(dir, LECTURE_VIDEO)).catch(() => null))?.isFile());
30
+ if (md === null && !hasVideo)
31
+ return { lecture: null, problems: [`bundles/${id}/ 与 lectures/${id}/ 都没有(课包要有 scene.json 与带步的 manifest.json;视频要有 ${LECTURE_VIDEO} 与 ${LECTURE_FILE})`] };
32
+ const problems = [];
33
+ if (!hasVideo)
34
+ problems.push(`lectures/${id}/ 没有 ${LECTURE_VIDEO}`);
35
+ if (md === null)
36
+ problems.push(`lectures/${id}/ 没有 ${LECTURE_FILE}(# 标题,一行一句「分:秒 原话」)`);
37
+ const doc = parseLectureDoc(md ?? '');
38
+ if (md !== null)
39
+ for (const i of doc.issues.filter((x) => x.level === 'fix'))
40
+ problems.push(`lectures/${id}/${LECTURE_FILE}${i.line ? ` 第 ${i.line} 行` : ''}:${i.text}`);
41
+ const total = hasVideo ? await mp4DurationMs(join(dir, LECTURE_VIDEO)) : null;
42
+ if (hasVideo && total === null)
43
+ problems.push(`lectures/${id}/${LECTURE_VIDEO} 读不出时长(要是 mp4)`);
44
+ if (total !== null)
45
+ for (const c of doc.chapters.filter((x) => x.start >= total))
46
+ problems.push(`lectures/${id}/${LECTURE_FILE} 第 ${c.at} 行:${clockLabel(c.start)} 超过了视频的长度 ${clockLabel(total)}`);
47
+ if (problems.length || total === null)
48
+ return { lecture: null, problems };
49
+ return { lecture: { id, title: doc.title, problem: '', subject: doc.subject, clock: videoClock(doc.chapters, total), picture: {}, video: true }, problems: [] };
50
+ }
51
+ /** 读课包:拼法与校验是 src/chalk 的 parseBundle;读不出来时 problem 说为什么(不在 = 没有 problem,让给视频) */
52
+ async function readBundleLecture(ws, id) {
53
+ const dir = join(ws.dirs.bundles, id);
54
+ let sceneJson;
55
+ let manifestJson;
56
+ try {
57
+ [sceneJson, manifestJson] = await Promise.all(['scene.json', 'manifest.json'].map(async (f) => JSON.parse(await readFile(join(dir, f), 'utf8'))));
58
+ }
59
+ catch {
60
+ return { lecture: null };
61
+ }
62
+ let bundle;
63
+ try {
64
+ bundle = parseBundle(sceneJson, manifestJson);
65
+ }
66
+ catch (e) {
67
+ return { lecture: null, problem: `bundles/${id}/ ${e instanceof BundleError ? e.message : String(e)};用源 TS 重新 cotutor chalk build` };
68
+ }
69
+ const { scene, picture } = bundle;
70
+ if (!scene.steps.length)
71
+ return { lecture: null, problem: `bundles/${id}/ 的 manifest.json 没有步(小课堂要逐步讲的课包)` };
72
+ const clock = lectureClock(scene.skeletons, scene.steps);
73
+ const baked = await readFile(join(dir, 'bake.json'), 'utf8').then((t) => bakeMatches(JSON.parse(t), sceneJson), () => false);
74
+ return { lecture: { id, title: scene.title, problem: scene.problem, subject: scene.subject ?? null, clock, picture, video: false, baked } };
75
+ }
76
+ /**
77
+ * 孩子带来的画 → 落盘的样子:那段话 = 时刻与那时在讲哪句 + 截图是上下文包 photos 的第几张(photoBase = 前面已经有几张孩子自己拍的)
78
+ * + 课包的再说画的地方盖着什么(按课包现算,用课里的叫法)。读不出来就只留时刻与笔画
79
+ */
80
+ export function storedMarks(l, marks, photoBase = 0) {
81
+ let k = photoBase;
82
+ return marks.map((m) => {
83
+ const shot = m.image ? ++k : null;
84
+ const base = { atMs: m.atMs, svgMs: l && !l.video ? Math.round(lectureAt(l.clock, m.atMs).svgMs * 10) / 10 : 0, strokes: m.strokes, ...(m.image ? { image: m.image } : {}), ...(m.t !== undefined ? { t: m.t } : {}) };
85
+ if (!l)
86
+ return base;
87
+ const head = markHead(l.clock, m.atMs);
88
+ if (m.hit !== undefined)
89
+ return { ...base, text: `${head}${m.hit ? `;${m.hit}` : ''}` };
90
+ const pic = shot ? `画在截图上(photos 第 ${shot} 张)` : l.video ? '没有截图,看不出画的是什么' : '没有截图';
91
+ const hint = l.video ? '' : inkHint(l.clock, m, l.picture).text;
92
+ return { ...base, text: `${head};${pic}${hint ? `;${hint}` : ''}` };
93
+ });
94
+ }
95
+ /** 上下文包 lecture: 段:标题、来源、课长、每句起点与原话、看的情况、画过的几处 */
96
+ export function lecturePack(l, w, marks = []) {
97
+ const seen = w.finished ? (w.again ? '又看完了一遍' : '看完了') : `${w.again ? '又看了一遍,' : ''}看到 ${clockLabel(w.watchedMs)}`;
98
+ const watched = `${seen}${w.pauses ? `,停过 ${w.pauses} 次` : ''}${marks.length ? `,画了 ${marks.length} 处` : ''}`;
99
+ return { title: l.title, source: `${l.video ? 'video' : 'bundle'} ${l.id}`, length: clockLabel(l.clock.total), lines: lectureLines(l.clock), watched, marks: marks.flatMap((m) => (m.text ? [m.text] : [])) };
100
+ }
101
+ /**
102
+ * 小课堂卡(cards/lecture)下发时补的快照:课名、真放的起止(对齐段界)、末帧停在 SVG 的哪一刻、能不能放。
103
+ * 课包每次现读(家长重做了课包,下一次下发就跟上);读不出来或起止对不上 → ready: false,讲稿 [[play]] 不停。
104
+ */
105
+ export async function enrichLectures(ws, section) {
106
+ if (!section.cards.some((c) => c.kind === 'lecture'))
107
+ return section;
108
+ const seen = new Map();
109
+ const cards = [];
110
+ for (const c of section.cards) {
111
+ const p = c.props;
112
+ if (c.kind !== 'lecture' || typeof p.bundle !== 'string') {
113
+ cards.push(c);
114
+ continue;
115
+ }
116
+ if (!seen.has(p.bundle))
117
+ seen.set(p.bundle, readLecture(ws, p.bundle));
118
+ const l = await seen.get(p.bundle);
119
+ const r = l ? lectureRange(l.clock, typeof p.from === 'number' ? p.from : undefined, typeof p.to === 'number' ? p.to : undefined) : null;
120
+ // 视频没有 SVG:末帧的缩略图页面用 <video> 停在 end 前一点
121
+ cards.push({ ...c, props: { ...c.props, ...(l ? { title: l.title } : {}), ...(l?.video ? { video: true } : {}), ...(l && r ? { start: r.start, end: r.end, ...(l.video ? {} : { still: Math.round(lectureAt(l.clock, Math.max(r.start, r.end - 1)).svgMs * 10) / 10 }) } : {}), ready: Boolean(l && r) } });
122
+ }
123
+ return { ...section, cards };
124
+ }
125
+ const baking = new Map();
126
+ /**
127
+ * 课包没烤过(或画面改过没重烤):起一次 cotutor chalk bake(同一份课包同时只烤一次;要本机的 Chrome,src/app/cli/chalk/)。
128
+ * 烤好 true;烤不了(没浏览器、课包坏了、超时)false,不抛。
129
+ */
130
+ export function ensureBaked(bundlesDir, id) {
131
+ const key = join(bundlesDir, id);
132
+ let p = baking.get(key);
133
+ if (!p) {
134
+ p = new Promise((resolve) => {
135
+ const child = spawn(process.execPath, [COTUTOR_BIN, 'chalk', 'bake', id, '--bundles', bundlesDir, '--json'], { stdio: 'ignore' });
136
+ const timer = setTimeout(() => child.kill(), BAKE_TIMEOUT_MS);
137
+ child.on('error', () => { clearTimeout(timer); resolve(false); });
138
+ child.on('exit', (code) => { clearTimeout(timer); resolve(code === 0); });
139
+ }).finally(() => baking.delete(key));
140
+ baking.set(key, p);
141
+ }
142
+ return p;
143
+ }
144
+ const BAKE_TIMEOUT_MS = 120_000;
145
+ /** 本包的命令入口(仓库里与 npm 装的都是包根 bin/cotutor.js;src/runtime 与 dist/runtime 到包根同样两级) */
146
+ const COTUTOR_BIN = fileURLToPath(new URL('../../bin/cotutor.js', import.meta.url));
147
+ const tried = new Set();
148
+ /**
149
+ * 孩子端要用到的课包(首页的小课堂、lecture 卡)没烤过:后台烤一次,不等(下一次下发就是烤好的)。
150
+ * 一个服务进程里每份课包只看一次;烤不了照样放(孩子端在浏览器里现烤)。
151
+ */
152
+ export function bakeSoon(bundlesDir, id) {
153
+ const key = join(bundlesDir, id);
154
+ if (tried.has(key) || !BUNDLE_ID_RE.test(id))
155
+ return;
156
+ tried.add(key);
157
+ void (async () => {
158
+ const [scene, baked] = await Promise.all(['scene.json', 'bake.json'].map((f) => readFile(join(key, f), 'utf8').then((t) => JSON.parse(t), () => null)));
159
+ if (scene && !bakeMatches(baked, scene))
160
+ await ensureBaked(bundlesDir, id);
161
+ })().catch(() => { });
162
+ }
163
+ /** 一节板书里 lecture 卡用到的课包,没烤过的后台烤 */
164
+ export function bakeSectionSoon(bundlesDir, section) {
165
+ for (const c of section.cards)
166
+ if (c.kind === 'lecture' && typeof c.props.bundle === 'string' && !c.props.video)
167
+ bakeSoon(bundlesDir, c.props.bundle);
168
+ }
169
+ /**
170
+ * 课包在 SVG 时刻 svgMs 的画面(画的卡、lecture 卡的缩略图,播放器截图的底):用烤好的画面(bake.json)在服务端现画成一张 SVG,
171
+ * 有画就叠上那几笔(课包坐标,加上导出时的平移)。没烤过、画面改过没重烤:给了 bake(路由给 ensureBaked)就先烤再画,烤不了 → null。
172
+ * 背景图内嵌成 data URI:SVG 当 <img> 用时不去取外面的文件。
173
+ */
174
+ export async function lectureFrameSvg(ws, id, svgMs, ink = [], opts = {}) {
175
+ if (!BUNDLE_ID_RE.test(id))
176
+ return null;
177
+ const dir = join(ws.dirs.bundles, id);
178
+ try {
179
+ const read = () => Promise.all(['scene.json', 'manifest.json', 'bake.json'].map((f) => readFile(join(dir, f), 'utf8').catch(() => '')));
180
+ const [sceneRaw, manifestRaw, bakedRaw] = await read();
181
+ if (!sceneRaw || !manifestRaw)
182
+ return null;
183
+ const scene = JSON.parse(sceneRaw);
184
+ const parse = (raw) => { try {
185
+ return JSON.parse(raw);
186
+ }
187
+ catch {
188
+ return null;
189
+ } };
190
+ let baked = parse(bakedRaw);
191
+ if (!bakeMatches(baked, scene) && opts.bake && (await opts.bake(ws.dirs.bundles, id)))
192
+ baked = parse(await readFile(join(dir, 'bake.json'), 'utf8').catch(() => ''));
193
+ if (!bakeMatches(baked, scene))
194
+ return null;
195
+ const { scene: parsed } = parseBundle(scene, JSON.parse(manifestRaw));
196
+ const clock = lectureClock(parsed.skeletons, parsed.steps);
197
+ const [dx, dy] = baked.offset;
198
+ const d = ink.filter((st) => st.length >= 2).map((st) => st.map((p, i) => `${i ? 'L' : 'M'}${p[0] + dx} ${p[1] + dy}`).join(' ')).join(' ');
199
+ const overlay = d ? `<path d="${d}" fill="none" stroke="#2f6fd6" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" vector-effect="non-scaling-stroke"/>` : '';
200
+ // 背景图(换过画板的课每块板一张)内嵌成 data URI;画哪块板、淡没淡由 src/chalk 按那一刻算
201
+ const hrefs = new Map();
202
+ for (const b of (baked.boards ?? [baked]).map((x) => x.background)) {
203
+ if (!b || hrefs.has(b.src))
204
+ continue;
205
+ const ext = b.src.split('.').pop()?.toLowerCase() ?? 'png';
206
+ const mime = ext === 'jpg' || ext === 'jpeg' ? 'image/jpeg' : ext === 'webp' ? 'image/webp' : 'image/png';
207
+ const data = await readFile(join(dir, b.src)).catch(() => null);
208
+ if (data)
209
+ hrefs.set(b.src, `data:${mime};base64,${data.toString('base64')}`);
210
+ }
211
+ return renderFrameSvg(baked, { elements: elementsAtSvgTime(clock, svgMs) }, { overlay, assetHref: (src) => hrefs.get(src) });
212
+ }
213
+ catch {
214
+ return null;
215
+ }
216
+ }
217
+ /** frame.svg 的 svg 参数:SVG 时刻(毫秒);`end` = 最后一帧(小课堂卡的缩略图);认不出来 = 0 */
218
+ export function parseSvgMs(s) {
219
+ return s === 'end' ? Infinity : Number(s) || 0;
220
+ }
221
+ /** frame.svg 的 ring 参数:一笔「x,y;x,y;…」,笔与笔之间「~」(课包坐标,取整;一共最多 MARK_MAX_POINTS 个点);认不出来 = 没有 */
222
+ export function parseRing(s) {
223
+ if (!s)
224
+ return [];
225
+ let left = MARK_MAX_POINTS;
226
+ const strokes = [];
227
+ for (const one of s.split('~')) {
228
+ if (left <= 0)
229
+ break;
230
+ const pts = one.split(';').slice(0, left).map((p) => p.split(',').map(Number));
231
+ if (!pts.every((p) => p.length === 2 && p.every((x) => Number.isFinite(x) && Math.abs(x) < 1e5)))
232
+ return [];
233
+ left -= pts.length;
234
+ strokes.push(pts);
235
+ }
236
+ return strokes;
237
+ }