@qnroa/qtype 0.0.2 → 0.0.4

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 (468) hide show
  1. package/CHANGELOG.md +44 -3
  2. package/CHANGELOG.zh.md +36 -2
  3. package/README.md +88 -56
  4. package/README.zh.md +82 -62
  5. package/dist/cli/bootstrap.js +4 -115
  6. package/dist/cli/commands/check/index.js +11 -10
  7. package/dist/cli/commands/config/action.js +5 -9
  8. package/dist/cli/commands/content/lint/action.js +38 -44
  9. package/dist/cli/commands/content/search/action.js +44 -34
  10. package/dist/cli/commands/content/search/i18n.js +6 -0
  11. package/dist/cli/commands/content/search/index.js +19 -1
  12. package/dist/cli/commands/content/validate/action.js +28 -27
  13. package/dist/cli/commands/index.js +1 -3
  14. package/dist/cli/commands/pack/index.js +2 -1
  15. package/dist/cli/commands/publish/build/action.js +88 -11
  16. package/dist/cli/commands/repo/new/action.js +23 -4
  17. package/dist/cli/commands/repo/new/i18n.js +2 -2
  18. package/dist/cli/commands/repo/sync/action.js +24 -4
  19. package/dist/cli/commands/repo/templates.js +2 -1
  20. package/dist/cli/utils/material.js +49 -0
  21. package/dist/core/encrypt.js +24 -1
  22. package/dist/core/index.js +1 -0
  23. package/dist/core/parser/frontmatter.js +39 -34
  24. package/dist/core/parser/parseMaterial.js +174 -24
  25. package/dist/core/romanization/index.js +152 -0
  26. package/dist/core/schemas/both.js +52 -0
  27. package/dist/core/schemas/index.js +3 -0
  28. package/dist/core/schemas/repo.js +55 -0
  29. package/dist/core/schemas/user.js +19 -0
  30. package/dist/core/typing/engine/TypingInput.js +243 -0
  31. package/dist/core/typing/engine/chars.js +30 -0
  32. package/dist/core/typing/engine/feedback.js +15 -0
  33. package/dist/core/typing/engine/index.js +5 -0
  34. package/dist/core/typing/engine/settings.js +8 -0
  35. package/dist/core/typing/engine/stats.js +45 -0
  36. package/dist/core/typing/index.js +9 -1
  37. package/dist/view/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  38. package/dist/view/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  39. package/dist/view/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  40. package/dist/view/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  41. package/dist/view/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  42. package/dist/view/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  43. package/dist/view/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  44. package/dist/view/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  45. package/dist/view/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  46. package/dist/view/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  47. package/dist/view/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  48. package/dist/view/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  49. package/dist/view/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  50. package/dist/view/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  51. package/dist/view/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  52. package/dist/view/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  53. package/dist/view/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  54. package/dist/view/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  55. package/dist/view/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  56. package/dist/view/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  57. package/dist/view/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  58. package/dist/view/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  59. package/dist/view/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  60. package/dist/view/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  61. package/dist/view/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  62. package/dist/view/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  63. package/dist/view/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  64. package/dist/view/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  65. package/dist/view/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  66. package/dist/view/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  67. package/dist/view/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  68. package/dist/view/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  69. package/dist/view/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  70. package/dist/view/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  71. package/dist/view/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  72. package/dist/view/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  73. package/dist/view/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  74. package/dist/view/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  75. package/dist/view/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  76. package/dist/view/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  77. package/dist/view/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  78. package/dist/view/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  79. package/dist/view/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  80. package/dist/view/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  81. package/dist/view/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  82. package/dist/view/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  83. package/dist/view/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  84. package/dist/view/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  85. package/dist/view/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  86. package/dist/view/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  87. package/dist/view/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  88. package/dist/view/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  89. package/dist/view/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  90. package/dist/view/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  91. package/dist/view/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  92. package/dist/view/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  93. package/dist/view/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  94. package/dist/view/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  95. package/dist/view/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  96. package/dist/view/assets/abap-BdImnpbu.js +1 -0
  97. package/dist/view/assets/actionscript-3-B3316cI-.js +1 -0
  98. package/dist/view/assets/ada-bCR0ucgS.js +1 -0
  99. package/dist/view/assets/ahk-CsyLZFj1.js +1 -0
  100. package/dist/view/assets/ahk2-8Zs4aa1G.js +1 -0
  101. package/dist/view/assets/andromeeda-C4gqWexZ.js +1 -0
  102. package/dist/view/assets/angular-html-DA-rfuFy.js +1 -0
  103. package/dist/view/assets/angular-ts-BrjP3tb8.js +1 -0
  104. package/dist/view/assets/apache-Pmp26Uib.js +1 -0
  105. package/dist/view/assets/apex-DhZFqWV2.js +1 -0
  106. package/dist/view/assets/apl-CORt7UWP.js +1 -0
  107. package/dist/view/assets/applescript-Co6uUVPk.js +1 -0
  108. package/dist/view/assets/ara-BRHolxvo.js +1 -0
  109. package/dist/view/assets/asciidoc-CSVQ5wI8.js +1 -0
  110. package/dist/view/assets/asm-D_Q5rh1f.js +1 -0
  111. package/dist/view/assets/astro-HNnZUWAn.js +1 -0
  112. package/dist/view/assets/aurora-x-D-2ljcwZ.js +1 -0
  113. package/dist/view/assets/awk-DMzUqQB5.js +1 -0
  114. package/dist/view/assets/ayu-dark-DYE7WIF3.js +1 -0
  115. package/dist/view/assets/ayu-light-BA47KaF1.js +1 -0
  116. package/dist/view/assets/ayu-mirage-32ctXXKs.js +1 -0
  117. package/dist/view/assets/ballerina-BFfxhgS-.js +1 -0
  118. package/dist/view/assets/bat-CickPsom.js +1 -0
  119. package/dist/view/assets/beancount-k_qm7-4y.js +1 -0
  120. package/dist/view/assets/berry-uYugtg8r.js +1 -0
  121. package/dist/view/assets/bibtex-CHM0blh-.js +1 -0
  122. package/dist/view/assets/bicep-Bmn6On1c.js +1 -0
  123. package/dist/view/assets/bird2-Bx8U0n9b.js +1 -0
  124. package/dist/view/assets/blade-2xfisSek.js +1 -0
  125. package/dist/view/assets/bsl-DlhNcFeZ.js +1 -0
  126. package/dist/view/assets/c-BIGW1oBm.js +1 -0
  127. package/dist/view/assets/c3-Dp5svz6Z.js +1 -0
  128. package/dist/view/assets/cadence-Bv_4Rxtq.js +1 -0
  129. package/dist/view/assets/cairo-KRGpt6FW.js +1 -0
  130. package/dist/view/assets/catppuccin-frappe-CZL1YF0i.js +1 -0
  131. package/dist/view/assets/catppuccin-latte-DH-8KZSZ.js +1 -0
  132. package/dist/view/assets/catppuccin-macchiato-B7yYVSCf.js +1 -0
  133. package/dist/view/assets/catppuccin-mocha-Ct7hS0mc.js +1 -0
  134. package/dist/view/assets/chapel-DTp_pixX.js +1 -0
  135. package/dist/view/assets/clarity-Dn5IMItf.js +1 -0
  136. package/dist/view/assets/clojure-P80f7IUj.js +1 -0
  137. package/dist/view/assets/cmake-D1j8_8rp.js +1 -0
  138. package/dist/view/assets/cobol-nBiQ_Alo.js +1 -0
  139. package/dist/view/assets/codeowners-Bp6g37R7.js +1 -0
  140. package/dist/view/assets/codeql-DsOJ9woJ.js +1 -0
  141. package/dist/view/assets/coffee-Ch7k5sss.js +1 -0
  142. package/dist/view/assets/common-lisp-Cg-RD9OK.js +1 -0
  143. package/dist/view/assets/coq-C7JzOVbR.js +1 -0
  144. package/dist/view/assets/cpp-BMRokrvK.js +1 -0
  145. package/dist/view/assets/crystal-DGywbUpC.js +1 -0
  146. package/dist/view/assets/csharp-DSvCPggb.js +1 -0
  147. package/dist/view/assets/css-CLj8gQPS.js +1 -0
  148. package/dist/view/assets/csv-fuZLfV_i.js +1 -0
  149. package/dist/view/assets/cue-D82EKSYY.js +1 -0
  150. package/dist/view/assets/cypher-COkxafJQ.js +1 -0
  151. package/dist/view/assets/d-85-TOEBH.js +1 -0
  152. package/dist/view/assets/dark-plus-C3mMm8J8.js +1 -0
  153. package/dist/view/assets/dart-bE4Kk8sk.js +1 -0
  154. package/dist/view/assets/dax-CEL-wOlO.js +1 -0
  155. package/dist/view/assets/desktop-BmXAJ9_W.js +1 -0
  156. package/dist/view/assets/diff-D97Zzqfu.js +1 -0
  157. package/dist/view/assets/docker-BcOcwvcX.js +1 -0
  158. package/dist/view/assets/dotenv-Da5cRb03.js +1 -0
  159. package/dist/view/assets/dracula-BzJJZx-M.js +1 -0
  160. package/dist/view/assets/dracula-soft-BXkSAIEj.js +1 -0
  161. package/dist/view/assets/dream-maker-BtqSS_iP.js +1 -0
  162. package/dist/view/assets/edge-FbVlp4U3.js +1 -0
  163. package/dist/view/assets/elixir-CkH2-t6x.js +1 -0
  164. package/dist/view/assets/elm-DbKCFpqz.js +1 -0
  165. package/dist/view/assets/emacs-lisp-C_m_b--Z.js +1 -0
  166. package/dist/view/assets/erb-DXfck5VN.js +1 -0
  167. package/dist/view/assets/erlang-DsQrWhSR.js +1 -0
  168. package/dist/view/assets/everforest-dark-BgDCqdQA.js +1 -0
  169. package/dist/view/assets/everforest-light-C8M2exoo.js +1 -0
  170. package/dist/view/assets/fennel-BYunw83y.js +1 -0
  171. package/dist/view/assets/fish-BvzEVeQv.js +1 -0
  172. package/dist/view/assets/fluent-C4IJs8-o.js +1 -0
  173. package/dist/view/assets/fortran-fixed-form-CkoXwp7k.js +1 -0
  174. package/dist/view/assets/fortran-free-form-BxgE0vQu.js +1 -0
  175. package/dist/view/assets/fsharp-CXgrBDvD.js +1 -0
  176. package/dist/view/assets/gdresource-TyuKm33G.js +1 -0
  177. package/dist/view/assets/gdscript-DqcFQ5yU.js +1 -0
  178. package/dist/view/assets/gdshader-DkwncUOv.js +1 -0
  179. package/dist/view/assets/genie-D0YGMca9.js +1 -0
  180. package/dist/view/assets/gherkin-DyxjwDmM.js +1 -0
  181. package/dist/view/assets/git-commit-F4YmCXRG.js +1 -0
  182. package/dist/view/assets/git-rebase-r7XF79zn.js +1 -0
  183. package/dist/view/assets/github-dark-DHJKELXO.js +1 -0
  184. package/dist/view/assets/github-dark-default-Cuk6v7N8.js +1 -0
  185. package/dist/view/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
  186. package/dist/view/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
  187. package/dist/view/assets/github-light-DAi9KRSo.js +1 -0
  188. package/dist/view/assets/github-light-default-D7oLnXFd.js +1 -0
  189. package/dist/view/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
  190. package/dist/view/assets/gleam-BspZqrRM.js +1 -0
  191. package/dist/view/assets/glimmer-js-ByusRIyA.js +1 -0
  192. package/dist/view/assets/glimmer-ts-BfAWNZQY.js +1 -0
  193. package/dist/view/assets/glsl-DplSGwfg.js +1 -0
  194. package/dist/view/assets/gn-n2N0HUVH.js +1 -0
  195. package/dist/view/assets/gnuplot-DdkO51Og.js +1 -0
  196. package/dist/view/assets/go-C27-OAKa.js +1 -0
  197. package/dist/view/assets/graphql-ChdNCCLP.js +1 -0
  198. package/dist/view/assets/groovy-gcz8RCvz.js +1 -0
  199. package/dist/view/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
  200. package/dist/view/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
  201. package/dist/view/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
  202. package/dist/view/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
  203. package/dist/view/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
  204. package/dist/view/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
  205. package/dist/view/assets/hack-BWmVpMyf.js +1 -0
  206. package/dist/view/assets/haml-D5jkg6IW.js +1 -0
  207. package/dist/view/assets/handlebars-BpdQsYii.js +1 -0
  208. package/dist/view/assets/haskell-Df6bDoY_.js +1 -0
  209. package/dist/view/assets/haxe-CfZj7gIn.js +1 -0
  210. package/dist/view/assets/hcl-BWvSN4gD.js +1 -0
  211. package/dist/view/assets/hjson-D5-asLiD.js +1 -0
  212. package/dist/view/assets/hlsl-D3lLCCz7.js +1 -0
  213. package/dist/view/assets/horizon-BUw7H-hv.js +1 -0
  214. package/dist/view/assets/horizon-bright-CUuTKBJd.js +1 -0
  215. package/dist/view/assets/houston-DnULxvSX.js +1 -0
  216. package/dist/view/assets/html-derivative-DlHx6ybY.js +1 -0
  217. package/dist/view/assets/html-pp8916En.js +1 -0
  218. package/dist/view/assets/http-jrhK8wxY.js +1 -0
  219. package/dist/view/assets/hurl-irOxFIW8.js +1 -0
  220. package/dist/view/assets/hxml-2-FPmUDs.js +1 -0
  221. package/dist/view/assets/hy-DFXneXwc.js +1 -0
  222. package/dist/view/assets/imba-DGztddWO.js +1 -0
  223. package/dist/view/assets/index-1XXyxiQO.js +189 -0
  224. package/dist/view/assets/index-BLryk3W4.css +1 -0
  225. package/dist/view/assets/index-DIBSxFYl.js +3 -0
  226. package/dist/view/assets/index-DgHvnMV9.js +153 -0
  227. package/dist/view/assets/ini-BEwlwnbL.js +1 -0
  228. package/dist/view/assets/java-CylS5w8V.js +1 -0
  229. package/dist/view/assets/javascript-wDzz0qaB.js +1 -0
  230. package/dist/view/assets/jinja-f2NsQr07.js +1 -0
  231. package/dist/view/assets/jison-wvAkD_A8.js +1 -0
  232. package/dist/view/assets/json-Cp-IABpG.js +1 -0
  233. package/dist/view/assets/json5-C9tS-k6U.js +1 -0
  234. package/dist/view/assets/jsonc-Des-eS-w.js +1 -0
  235. package/dist/view/assets/jsonl-DcaNXYhu.js +1 -0
  236. package/dist/view/assets/jsonnet-DFQXde-d.js +1 -0
  237. package/dist/view/assets/jssm-C2t-YnRu.js +1 -0
  238. package/dist/view/assets/jsx-g9-lgVsj.js +1 -0
  239. package/dist/view/assets/julia-5Bft2YPA.js +1 -0
  240. package/dist/view/assets/just-Cwhn7H3k.js +1 -0
  241. package/dist/view/assets/kanagawa-dragon-BuwD2xS4.js +1 -0
  242. package/dist/view/assets/kanagawa-lotus-C3DzagqV.js +1 -0
  243. package/dist/view/assets/kanagawa-wave-DFGoQZhC.js +1 -0
  244. package/dist/view/assets/katex-BYZTj3zW.css +1 -0
  245. package/dist/view/assets/katex-C5jXJg4s.js +257 -0
  246. package/dist/view/assets/kdl-DV7GczEv.js +1 -0
  247. package/dist/view/assets/kotlin-BdnUsdx6.js +1 -0
  248. package/dist/view/assets/kusto-wEQ09or8.js +1 -0
  249. package/dist/view/assets/laserwave-DUszq2jm.js +1 -0
  250. package/dist/view/assets/latex-D5pSuvFb.js +1 -0
  251. package/dist/view/assets/lean-BZvkOJ9d.js +1 -0
  252. package/dist/view/assets/less-B1dDrJ26.js +1 -0
  253. package/dist/view/assets/light-plus-B7mTdjB0.js +1 -0
  254. package/dist/view/assets/liquid-C0sCDyMI.js +1 -0
  255. package/dist/view/assets/llvm-BZoOZj88.js +1 -0
  256. package/dist/view/assets/log-2UxHyX5q.js +1 -0
  257. package/dist/view/assets/logo-BtOb2qkB.js +1 -0
  258. package/dist/view/assets/lua-BaeVxFsk.js +1 -0
  259. package/dist/view/assets/luau-BnpPk5vE.js +1 -0
  260. package/dist/view/assets/make-CHLpvVh8.js +1 -0
  261. package/dist/view/assets/markdown-Cvjx9yec.js +1 -0
  262. package/dist/view/assets/marko-DjSrsDqO.js +1 -0
  263. package/dist/view/assets/material-theme-D5KoaKCx.js +1 -0
  264. package/dist/view/assets/material-theme-darker-BfHTSMKl.js +1 -0
  265. package/dist/view/assets/material-theme-lighter-B0m2ddpp.js +1 -0
  266. package/dist/view/assets/material-theme-ocean-CyktbL80.js +1 -0
  267. package/dist/view/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
  268. package/dist/view/assets/matlab-D7o27uSR.js +1 -0
  269. package/dist/view/assets/mdc-D1_yUvq7.js +1 -0
  270. package/dist/view/assets/mdx-Cmh6b_Ma.js +1 -0
  271. package/dist/view/assets/mermaid-CQcHuHx7.js +1 -0
  272. package/dist/view/assets/min-dark-CafNBF8u.js +1 -0
  273. package/dist/view/assets/min-light-CTRr51gU.js +1 -0
  274. package/dist/view/assets/mipsasm-CKIfxQSi.js +1 -0
  275. package/dist/view/assets/mojo-DJz3ZmWd.js +1 -0
  276. package/dist/view/assets/monokai-BRVnQi9A.js +1 -0
  277. package/dist/view/assets/moonbit-CHtswR0a.js +1 -0
  278. package/dist/view/assets/move-el3G9tDJ.js +1 -0
  279. package/dist/view/assets/narrat-DRg8JJMk.js +1 -0
  280. package/dist/view/assets/nextflow-C-mBbutL.js +1 -0
  281. package/dist/view/assets/nextflow-groovy-vE_lwT2v.js +1 -0
  282. package/dist/view/assets/nginx-BpAMiNFr.js +1 -0
  283. package/dist/view/assets/night-owl-C39BiMTA.js +1 -0
  284. package/dist/view/assets/night-owl-light-CMTm3GFP.js +1 -0
  285. package/dist/view/assets/nim-BIad80T-.js +1 -0
  286. package/dist/view/assets/nix-CwoSXNpI.js +1 -0
  287. package/dist/view/assets/nord-Ddv68eIx.js +1 -0
  288. package/dist/view/assets/nsis-BlV79W_Q.js +1 -0
  289. package/dist/view/assets/nushell-D3jzshHO.js +1 -0
  290. package/dist/view/assets/objective-c-DXmwc3jG.js +1 -0
  291. package/dist/view/assets/objective-cpp-CLxacb5B.js +1 -0
  292. package/dist/view/assets/ocaml-C0hk2d4L.js +1 -0
  293. package/dist/view/assets/odin-BBf5iR-q.js +1 -0
  294. package/dist/view/assets/one-dark-pro-DVMEJ2y_.js +1 -0
  295. package/dist/view/assets/one-light-C3Wv6jpd.js +1 -0
  296. package/dist/view/assets/openscad-C4EeE6gA.js +1 -0
  297. package/dist/view/assets/org-DM6o9KBp.js +1 -0
  298. package/dist/view/assets/pascal-D93ZcfNL.js +1 -0
  299. package/dist/view/assets/perl-B9cMNwum.js +1 -0
  300. package/dist/view/assets/php-Csjmro_R.js +1 -0
  301. package/dist/view/assets/pkl-u5AG7uiY.js +1 -0
  302. package/dist/view/assets/plastic-3e1v2bzS.js +1 -0
  303. package/dist/view/assets/plsql-ChMvpjG-.js +1 -0
  304. package/dist/view/assets/po-BTJTHyun.js +1 -0
  305. package/dist/view/assets/poimandres-CS3Unz2-.js +1 -0
  306. package/dist/view/assets/polar-C0HS_06l.js +1 -0
  307. package/dist/view/assets/postcss-CXtECtnM.js +1 -0
  308. package/dist/view/assets/powerquery-CEu0bR-o.js +1 -0
  309. package/dist/view/assets/powershell-BmBUJMz7.js +1 -0
  310. package/dist/view/assets/prisma-Vru482bI.js +1 -0
  311. package/dist/view/assets/prolog-CbFg5uaA.js +1 -0
  312. package/dist/view/assets/proto-C7zT0LnQ.js +1 -0
  313. package/dist/view/assets/pug-DKIMFp6K.js +1 -0
  314. package/dist/view/assets/puppet-BMWR74SV.js +1 -0
  315. package/dist/view/assets/purescript-CklMAg4u.js +1 -0
  316. package/dist/view/assets/python-B6aJPvgy.js +1 -0
  317. package/dist/view/assets/qml-3beO22l8.js +1 -0
  318. package/dist/view/assets/qmldir-C8lEn-DE.js +1 -0
  319. package/dist/view/assets/qss-IeuSbFQv.js +1 -0
  320. package/dist/view/assets/r-Cf5RLm7j.js +1 -0
  321. package/dist/view/assets/racket-BqYA7rlc.js +1 -0
  322. package/dist/view/assets/raku-DXvB9xmW.js +1 -0
  323. package/dist/view/assets/razor-BjBPvh-w.js +1 -0
  324. package/dist/view/assets/rbs-CpoqiR4B.js +1 -0
  325. package/dist/view/assets/red-hvxz__6c.js +1 -0
  326. package/dist/view/assets/reg-C-SQnVFl.js +1 -0
  327. package/dist/view/assets/regexp-CDVJQ6XC.js +1 -0
  328. package/dist/view/assets/rel-C3B-1QV4.js +1 -0
  329. package/dist/view/assets/riscv-BM1_JUlF.js +1 -0
  330. package/dist/view/assets/ron-D8l8udqQ.js +1 -0
  331. package/dist/view/assets/rose-pine-5qJOZa0Y.js +1 -0
  332. package/dist/view/assets/rose-pine-dawn-zx0QlTCp.js +1 -0
  333. package/dist/view/assets/rose-pine-moon-C9pEdX9L.js +1 -0
  334. package/dist/view/assets/rosmsg-BJDFO7_C.js +1 -0
  335. package/dist/view/assets/rst-bs7f0vWN.js +1 -0
  336. package/dist/view/assets/ruby-C0TQ7zu5.js +1 -0
  337. package/dist/view/assets/rust-B1yitclQ.js +1 -0
  338. package/dist/view/assets/sas-DEy46yEz.js +1 -0
  339. package/dist/view/assets/sass-Cj5Yp3dK.js +1 -0
  340. package/dist/view/assets/scala-CqE71os6.js +1 -0
  341. package/dist/view/assets/scheme-C98Dy4si.js +1 -0
  342. package/dist/view/assets/scss-D5BDwBP9.js +1 -0
  343. package/dist/view/assets/sdbl-DVxCFoDh.js +1 -0
  344. package/dist/view/assets/shaderlab-Dg9Lc6iA.js +1 -0
  345. package/dist/view/assets/shellscript-Yzrsuije.js +1 -0
  346. package/dist/view/assets/shellsession-BADoaaVG.js +1 -0
  347. package/dist/view/assets/slack-dark-BthQWCQV.js +1 -0
  348. package/dist/view/assets/slack-ochin-DqwNpetd.js +1 -0
  349. package/dist/view/assets/smalltalk-BOQMe2GC.js +1 -0
  350. package/dist/view/assets/smithy-cds9vsN8.js +1 -0
  351. package/dist/view/assets/snazzy-light-Bw305WKR.js +1 -0
  352. package/dist/view/assets/solarized-dark-CpvCGNkr.js +1 -0
  353. package/dist/view/assets/solarized-light-Dlz6yCKv.js +1 -0
  354. package/dist/view/assets/solidity-DijEV5ha.js +1 -0
  355. package/dist/view/assets/soy-8wufbnw4.js +1 -0
  356. package/dist/view/assets/sparql-rVzFXLq3.js +1 -0
  357. package/dist/view/assets/splunk-BtCnVYZw.js +1 -0
  358. package/dist/view/assets/sql-CRqJ_cUM.js +1 -0
  359. package/dist/view/assets/ssh-config-_ykCGR6B.js +1 -0
  360. package/dist/view/assets/stata-DI20mbqo.js +1 -0
  361. package/dist/view/assets/stylus-BEDo0Tqx.js +1 -0
  362. package/dist/view/assets/surrealql-Cjom0U5J.js +1 -0
  363. package/dist/view/assets/svelte-Cy7k_4gC.js +1 -0
  364. package/dist/view/assets/swift-C2oV4EkX.js +1 -0
  365. package/dist/view/assets/synthwave-84-CbfX1IO0.js +1 -0
  366. package/dist/view/assets/system-verilog-0hqHdDBg.js +1 -0
  367. package/dist/view/assets/systemd-4A_iFExJ.js +1 -0
  368. package/dist/view/assets/talonscript-CkByrt1z.js +1 -0
  369. package/dist/view/assets/tasl-QIJgUcNo.js +1 -0
  370. package/dist/view/assets/tcl-dwOrl1Do.js +1 -0
  371. package/dist/view/assets/templ-DhtptRzy.js +1 -0
  372. package/dist/view/assets/terraform-BETggiCN.js +1 -0
  373. package/dist/view/assets/tex-D96PA37w.js +1 -0
  374. package/dist/view/assets/tokyo-night-hegEt444.js +1 -0
  375. package/dist/view/assets/toml-vGWfd6FD.js +1 -0
  376. package/dist/view/assets/ts-tags-D351s5mN.js +1 -0
  377. package/dist/view/assets/tsv-B_m7g4N7.js +1 -0
  378. package/dist/view/assets/tsx-COt5Ahok.js +1 -0
  379. package/dist/view/assets/turtle-BsS91CYL.js +1 -0
  380. package/dist/view/assets/twig-27uCiNez.js +1 -0
  381. package/dist/view/assets/typescript-BPQ3VLAy.js +1 -0
  382. package/dist/view/assets/typespec-CAFt9gP4.js +1 -0
  383. package/dist/view/assets/typst-BUadGCkm.js +1 -0
  384. package/dist/view/assets/v-BGw2Nkan.js +1 -0
  385. package/dist/view/assets/vala-CsfeWuGM.js +1 -0
  386. package/dist/view/assets/vb-Cu-pLBUe.js +1 -0
  387. package/dist/view/assets/verilog-nZwndyjY.js +1 -0
  388. package/dist/view/assets/vesper-DRje8inN.js +1 -0
  389. package/dist/view/assets/vhdl-CeAyd5Ju.js +1 -0
  390. package/dist/view/assets/viml-CJc9bBzg.js +1 -0
  391. package/dist/view/assets/vitesse-black-Bkuqu6BP.js +1 -0
  392. package/dist/view/assets/vitesse-dark-D0r3Knsf.js +1 -0
  393. package/dist/view/assets/vitesse-light-CVO1_9PV.js +1 -0
  394. package/dist/view/assets/vue-BqiEGhQt.js +1 -0
  395. package/dist/view/assets/vue-html-AaS7Mt5G.js +1 -0
  396. package/dist/view/assets/vue-vine-BoDAl6tE.js +1 -0
  397. package/dist/view/assets/vyper-CDx5xZoG.js +1 -0
  398. package/dist/view/assets/wasm-CG6Dc4jp.js +1 -0
  399. package/dist/view/assets/wasm-MzD3tlZU.js +1 -0
  400. package/dist/view/assets/wenyan-BV7otONQ.js +1 -0
  401. package/dist/view/assets/wgsl-Dx-B1_4e.js +1 -0
  402. package/dist/view/assets/wikitext-BhOHFoWU.js +1 -0
  403. package/dist/view/assets/wit-5i3qLPDT.js +1 -0
  404. package/dist/view/assets/wolfram-lXgVvXCa.js +1 -0
  405. package/dist/view/assets/xml-sdJ4AIDG.js +1 -0
  406. package/dist/view/assets/xsl-CtQFsRM5.js +1 -0
  407. package/dist/view/assets/yaml-Buea-lGh.js +1 -0
  408. package/dist/view/assets/zenscript-DVFEvuxE.js +1 -0
  409. package/dist/view/assets/zig-VOosw3JB.js +1 -0
  410. package/dist/view/index.html +11 -3
  411. package/docs/README.md +21 -15
  412. package/docs/README.zh.md +15 -9
  413. package/docs/en/authoring.md +85 -80
  414. package/docs/en/cli-reference.md +110 -72
  415. package/docs/en/deployment.md +183 -0
  416. package/docs/en/getting-started.md +112 -0
  417. package/docs/en/material-types.md +241 -0
  418. package/docs/en/settings.md +133 -0
  419. package/docs/en/shortcuts.md +89 -0
  420. package/docs/en/typing-rules.md +208 -0
  421. package/docs/zh/authoring.md +74 -69
  422. package/docs/zh/cli-reference.md +92 -58
  423. package/docs/zh/deployment.md +179 -0
  424. package/docs/zh/getting-started.md +104 -0
  425. package/docs/zh/material-types.md +769 -0
  426. package/docs/zh/settings.md +120 -0
  427. package/docs/zh/shortcuts.md +83 -0
  428. package/docs/zh/typing-rules.md +191 -0
  429. package/package.json +13 -11
  430. package/templates/en/QTYPE.md +155 -495
  431. package/templates/en/README.md +11 -11
  432. package/templates/en/TODO.md +1 -1
  433. package/templates/en/material/article.md +20 -0
  434. package/templates/en/material/assets/article/sunrise.svg +12 -0
  435. package/templates/en/material/assets/code/tree.svg +19 -0
  436. package/templates/en/material/assets/qa/login-flow.svg +17 -0
  437. package/templates/en/material/assets/sentence/pangram.svg +6 -0
  438. package/templates/en/material/assets/snippet/moon.svg +8 -0
  439. package/templates/en/material/assets/word/apple.svg +5 -0
  440. package/templates/en/material/code.md +55 -0
  441. package/templates/en/material/qa.md +33 -0
  442. package/templates/en/material/sentence.md +18 -0
  443. package/templates/en/material/snippet.md +17 -0
  444. package/templates/en/material/word.md +20 -0
  445. package/templates/zh/QTYPE.md +191 -473
  446. package/templates/zh/README.md +6 -6
  447. package/templates/zh/index.md +1 -1
  448. package/templates/zh/material/article.md +18 -0
  449. package/templates/zh/material/assets/article/sunrise.svg +12 -0
  450. package/templates/zh/material/assets/code/tree.svg +19 -0
  451. package/templates/zh/material/assets/qa/login-flow.svg +17 -0
  452. package/templates/zh/material/assets/sentence/pangram.svg +6 -0
  453. package/templates/zh/material/assets/snippet/moon.svg +8 -0
  454. package/templates/zh/material/assets/word/apple.svg +5 -0
  455. package/templates/zh/material/code.md +55 -0
  456. package/templates/zh/material/qa.md +33 -0
  457. package/templates/zh/material/sentence.md +19 -0
  458. package/templates/zh/material/snippet.md +20 -0
  459. package/templates/zh/material/word.md +20 -0
  460. package/dist/cli/commands/check/action.js +0 -56
  461. package/dist/cli/commands/check/i18n.js +0 -26
  462. package/dist/cli/commands/plugin/i18n.js +0 -28
  463. package/dist/cli/commands/plugin/index.js +0 -41
  464. package/dist/core/typing/engine.js +0 -112
  465. package/dist/view/assets/index-DBABH3dG.js +0 -111
  466. package/dist/view/assets/index-RTLZ-zOC.css +0 -1
  467. package/templates/en/material/example.md +0 -47
  468. package/templates/zh/material/example.md +0 -47
@@ -0,0 +1,52 @@
1
+ /**
2
+ * "both" schema: user can set a global default and any single repo
3
+ * can override it. Registered on BOTH kv stores. Read precedence at
4
+ * runtime: repo > user > schema default.
5
+ */
6
+ import { enumValidator, boolValidator, positiveIntValidator, } from '../../store/index.js';
7
+ export const bothSchema = {
8
+ // Repo language. Drives:
9
+ // • which template locale (examples/zh vs examples/en) new repos are
10
+ // scaffolded from
11
+ // • the web UI language — the reader is locked to repo.lang so the UI
12
+ // matches the material's own language
13
+ //
14
+ // Values are BCP 47 codes (`zh-CN` / `en-US`); the short forms `zh` /
15
+ // `en` are accepted for backwards compatibility with older `.qtype/
16
+ // config.json` files and normalised at read time.
17
+ 'repo.lang': {
18
+ validator: enumValidator(['zh-CN', 'en-US', 'zh', 'en']),
19
+ default: 'en-US',
20
+ },
21
+ 'typing.theme': {
22
+ validator: enumValidator(['mocha', 'latte']),
23
+ default: 'mocha',
24
+ },
25
+ 'typing.targetWpm': {
26
+ validator: positiveIntValidator(),
27
+ },
28
+ 'typing.tabBehavior': {
29
+ validator: enumValidator(['reset', 'input', 'disabled']),
30
+ default: 'reset',
31
+ },
32
+ 'typing.hint': {
33
+ validator: enumValidator(['none', 'ghost']),
34
+ default: 'none',
35
+ },
36
+ 'typing.showKeyboard': {
37
+ validator: boolValidator(),
38
+ default: true,
39
+ },
40
+ 'typing.showHands': {
41
+ validator: boolValidator(),
42
+ default: true,
43
+ },
44
+ // Mermaid pre-render mode. `prerender` (default) uses puppeteer at build
45
+ // time to bake SVGs into dist/material/mermaid.json; `client` ships raw
46
+ // ```mermaid fences and lets the browser render them; `off` treats mermaid
47
+ // as plain code blocks.
48
+ 'publish.mermaid': {
49
+ validator: enumValidator(['prerender', 'client', 'off']),
50
+ default: 'prerender',
51
+ },
52
+ };
@@ -0,0 +1,3 @@
1
+ export { userOnlySchema } from './user.js';
2
+ export { repoOnlySchema } from './repo.js';
3
+ export { bothSchema } from './both.js';
@@ -0,0 +1,55 @@
1
+ /**
2
+ * repo-only config schema: keys tied to a specific repo, never
3
+ * useful as a user-wide default. Registered on the repo KV only.
4
+ */
5
+ import { enumValidator, nonEmptyStringValidator, boolValidator, } from '../../store/index.js';
6
+ export const repoOnlySchema = {
7
+ 'repo.name': {
8
+ validator: nonEmptyStringValidator(),
9
+ },
10
+ // ── Password protection for `publish build` ─────────────
11
+ // On/off toggle. When true, `publish build` reads the other three fields
12
+ // and encrypts every material/*.md before writing to dist.
13
+ 'publish.password.enabled': {
14
+ validator: boolValidator(),
15
+ default: false,
16
+ },
17
+ // Argon2id PHC-style hash of the plaintext password, used to verify the
18
+ // password entered at build time. NOT the AES key — that's derived
19
+ // separately via `publish.password.aesSalt`. Empty string when protection
20
+ // is off; shape enforced by `publish password set` writing a well-formed
21
+ // PHC string.
22
+ 'publish.password.hash': {
23
+ validator: (v) => typeof v === 'string'
24
+ ? null
25
+ : { code: 'err.schema.nonEmptyString', fallback: 'must be a string' },
26
+ default: '',
27
+ },
28
+ // Base64 salt used with argon2id (password → AES-256 key). Rotated only
29
+ // when the user calls `publish password set` — stable across rebuilds so
30
+ // the same password unlocks previously published sites.
31
+ 'publish.password.aesSalt': {
32
+ validator: (v) => typeof v === 'string'
33
+ ? null
34
+ : { code: 'err.schema.nonEmptyString', fallback: 'must be a string' },
35
+ default: '',
36
+ },
37
+ // What to encrypt. `all` covers material/ + any future encrypted assets;
38
+ // `material-only` limits encryption to material/*.md (same behavior in
39
+ // v0 since qtype has no other content streams).
40
+ 'publish.password.scope': {
41
+ validator: enumValidator(['all', 'material-only', 'selected']),
42
+ default: 'all',
43
+ },
44
+ // When scope='selected', the list of file names (relative to material/)
45
+ // to encrypt. Ignored otherwise.
46
+ 'publish.password.selected': {
47
+ validator: (v) => Array.isArray(v) && v.every((x) => typeof x === 'string')
48
+ ? null
49
+ : {
50
+ code: 'err.schema.nonEmptyString',
51
+ fallback: 'must be an array of strings',
52
+ },
53
+ default: [],
54
+ },
55
+ };
@@ -0,0 +1,19 @@
1
+ /**
2
+ * user-only config schema: keys that describe the person, not any
3
+ * particular repo. Registered on the user KV only. Kept in its own
4
+ * file so adding a new user preference is one focused edit.
5
+ */
6
+ import { enumValidator } from '../../store/index.js';
7
+ export const userOnlySchema = {
8
+ // CLI display language. Values are BCP 47 codes; the short forms
9
+ // `zh` / `en` are accepted for backwards compatibility with older
10
+ // config files and normalised at read time by the i18n layer.
11
+ 'cli.lang': {
12
+ validator: enumValidator(['zh-CN', 'en-US', 'zh', 'en']),
13
+ default: 'en-US',
14
+ },
15
+ 'cli.color': {
16
+ validator: enumValidator(['always', 'never', 'auto']),
17
+ default: 'auto',
18
+ },
19
+ };
@@ -0,0 +1,243 @@
1
+ /**
2
+ * TypingInput — qtype's typing state machine.
3
+ *
4
+ * Inspired by Keybr's `TextInput` but rewritten to fit qtype:
5
+ * • Single event model (onKeyDown / onBackspace) — no Keybr-style
6
+ * "garbage buffer" or word-skip. Every user keystroke maps to one
7
+ * Char slot at `position`.
8
+ * • Chars are pre-allocated with `attrs = 0`. Typing mutates only the
9
+ * Char at `position`; backspace decrements position and marks the
10
+ * reverted char with Recover.
11
+ * • Per-Char `timestamp` records when that slot was resolved. Stats
12
+ * use first/last non-zero timestamps.
13
+ * • Subscribers get a stable Char[] reference between notifications;
14
+ * a new array is emitted on every state transition so React can
15
+ * compare cheaply.
16
+ *
17
+ * The class does NOT know about pinyin, IME composition, or DOM. Those
18
+ * live in the events layer above.
19
+ */
20
+ import { CharAttr } from './chars.js';
21
+ import { Feedback } from './feedback.js';
22
+ import { defaultTypingSettings, } from './settings.js';
23
+ import { computeStats } from './stats.js';
24
+ export class TypingInput {
25
+ #target;
26
+ #targetCodepoints;
27
+ #length;
28
+ settings;
29
+ #chars;
30
+ #position;
31
+ #listeners = new Set();
32
+ constructor(text, settings) {
33
+ this.#target = text;
34
+ this.#targetCodepoints = Array.from(text, (ch) => ch.codePointAt(0) ?? 0);
35
+ this.#length = this.#targetCodepoints.length;
36
+ this.settings = { ...defaultTypingSettings, ...settings };
37
+ this.#chars = this.#buildInitialChars();
38
+ this.#position = 0;
39
+ }
40
+ #buildInitialChars() {
41
+ const chars = new Array(this.#length);
42
+ for (let i = 0; i < this.#length; i++) {
43
+ chars[i] = {
44
+ target: this.#targetCodepoints[i],
45
+ typed: null,
46
+ attrs: CharAttr.Normal,
47
+ timestamp: 0,
48
+ };
49
+ }
50
+ return chars;
51
+ }
52
+ /**
53
+ * User pressed a printable key. Returns feedback describing the result.
54
+ *
55
+ * Hit → Char.attrs |= Hit, position advances.
56
+ * Miss → Char.attrs |= Miss, position advances unless stopOnError.
57
+ */
58
+ onKeyDown(codePoint, timestamp) {
59
+ if (this.#position >= this.#length) {
60
+ return Feedback.Correct;
61
+ }
62
+ let expected = this.#targetCodepoints[this.#position];
63
+ // ignorePunctuation: fast-forward through any punctuation slots
64
+ // before matching the user's keystroke against the next
65
+ // typable character. Uses a broad Unicode range so it works
66
+ // for both ASCII and CJK punctuation.
67
+ if (this.settings.ignorePunctuation) {
68
+ while (this.#position < this.#length &&
69
+ isPunctuation(this.#targetCodepoints[this.#position])) {
70
+ this.#autoAdvance(timestamp);
71
+ }
72
+ if (this.#position >= this.#length)
73
+ return Feedback.Correct;
74
+ expected = this.#targetCodepoints[this.#position];
75
+ }
76
+ const isHit = this.settings.ignoreCase
77
+ ? sameLetter(codePoint, expected)
78
+ : codePoint === expected;
79
+ // Clone the array so subscribers see a new reference. Only the touched
80
+ // Char is a new object — the rest are shared.
81
+ const next = this.#chars.slice();
82
+ const prev = next[this.#position];
83
+ const nextAttrs = (prev.attrs & ~(CharAttr.Hit | CharAttr.Miss | CharAttr.Cursor)) |
84
+ (isHit ? CharAttr.Hit : CharAttr.Miss);
85
+ const recovered = isHit && (prev.attrs & CharAttr.Miss) !== 0;
86
+ next[this.#position] = {
87
+ target: expected,
88
+ typed: codePoint,
89
+ attrs: recovered ? nextAttrs | CharAttr.Recover : nextAttrs,
90
+ timestamp,
91
+ };
92
+ this.#chars = next;
93
+ if (isHit || !this.settings.stopOnError) {
94
+ this.#position++;
95
+ }
96
+ this.#notify();
97
+ if (recovered)
98
+ return Feedback.Recover;
99
+ return isHit ? Feedback.Correct : Feedback.Incorrect;
100
+ }
101
+ /**
102
+ * User pressed Backspace. Rewinds one slot and flags the reverted
103
+ * Char as Recover so downstream UI can dim / rehighlight it.
104
+ */
105
+ onBackspace(_timestamp) {
106
+ if (!this.settings.allowBackspace) {
107
+ // Silently swallow — the "commit and move on" mode.
108
+ return Feedback.Correct;
109
+ }
110
+ if (this.#position === 0) {
111
+ // Signal that the user is trying to rewind past start.
112
+ return Feedback.Restart;
113
+ }
114
+ this.#position--;
115
+ const next = this.#chars.slice();
116
+ const prev = next[this.#position];
117
+ // Clear Hit/Miss (this slot is now pending again) but remember it
118
+ // was once touched so we can render a "recovered" ghost if desired.
119
+ // `typed` resets to null — pending slots are "no user input yet".
120
+ next[this.#position] = {
121
+ target: this.#targetCodepoints[this.#position],
122
+ typed: null,
123
+ attrs: (prev.attrs & ~(CharAttr.Hit | CharAttr.Miss | CharAttr.Cursor)) |
124
+ CharAttr.Recover,
125
+ timestamp: 0,
126
+ };
127
+ this.#chars = next;
128
+ this.#notify();
129
+ return Feedback.Recover;
130
+ }
131
+ /** Wipe all progress and re-initialize Char slots. */
132
+ reset() {
133
+ this.#chars = this.#buildInitialChars();
134
+ this.#position = 0;
135
+ this.#notify();
136
+ }
137
+ get position() {
138
+ return this.#position;
139
+ }
140
+ get chars() {
141
+ return this.#chars;
142
+ }
143
+ get target() {
144
+ return this.#target;
145
+ }
146
+ get length() {
147
+ return this.#length;
148
+ }
149
+ get completed() {
150
+ return this.#position >= this.#length;
151
+ }
152
+ /** Codepoint the engine expects next, or null when done. */
153
+ get nextChar() {
154
+ return this.#position < this.#length
155
+ ? this.#targetCodepoints[this.#position]
156
+ : null;
157
+ }
158
+ get stats() {
159
+ return computeStats(this.#chars, this.#position, this.completed, Date.now());
160
+ }
161
+ statsAt(now) {
162
+ return computeStats(this.#chars, this.#position, this.completed, now);
163
+ }
164
+ subscribe(listener) {
165
+ this.#listeners.add(listener);
166
+ return () => {
167
+ this.#listeners.delete(listener);
168
+ };
169
+ }
170
+ #notify() {
171
+ for (const l of this.#listeners)
172
+ l();
173
+ }
174
+ /**
175
+ * Mark the current slot as auto-advanced Hit (used by
176
+ * `ignorePunctuation`). Records the timestamp so stats include the
177
+ * auto-advance in `startedAt` timing, but doesn't count it toward
178
+ * user keystroke totals (still shows as Hit — the punctuation was
179
+ * "correctly typed" via the auto-advance rule).
180
+ */
181
+ #autoAdvance(timestamp) {
182
+ if (this.#position >= this.#length)
183
+ return;
184
+ const expected = this.#targetCodepoints[this.#position];
185
+ const next = this.#chars.slice();
186
+ const prev = next[this.#position];
187
+ next[this.#position] = {
188
+ target: expected,
189
+ typed: expected,
190
+ attrs: (prev.attrs & ~(CharAttr.Miss | CharAttr.Cursor)) | CharAttr.Hit,
191
+ timestamp,
192
+ };
193
+ this.#chars = next;
194
+ this.#position++;
195
+ }
196
+ }
197
+ /**
198
+ * ASCII case-insensitive comparison. Non-ASCII (CJK, accented Latin,
199
+ * etc.) codepoints compare strictly.
200
+ */
201
+ function sameLetter(a, b) {
202
+ if (a === b)
203
+ return true;
204
+ // ASCII A-Z (65-90) vs a-z (97-122)
205
+ if (a >= 65 && a <= 90 && b === a + 32)
206
+ return true;
207
+ if (a >= 97 && a <= 122 && b === a - 32)
208
+ return true;
209
+ return false;
210
+ }
211
+ /**
212
+ * Rough Unicode punctuation test — ASCII punctuation + CJK
213
+ * fullwidth range + common typographic marks. Kept explicit
214
+ * (no `\p{P}` regex) so the check is fast and predictable.
215
+ */
216
+ function isPunctuation(cp) {
217
+ // ASCII punctuation: ! " # $ % & ' ( ) * + , - . / : ; < = > ? @
218
+ // [ \ ] ^ _ ` { | } ~
219
+ if (cp >= 0x21 && cp <= 0x2f)
220
+ return true;
221
+ if (cp >= 0x3a && cp <= 0x40)
222
+ return true;
223
+ if (cp >= 0x5b && cp <= 0x60)
224
+ return true;
225
+ if (cp >= 0x7b && cp <= 0x7e)
226
+ return true;
227
+ // CJK Symbols and Punctuation
228
+ if (cp >= 0x3000 && cp <= 0x303f)
229
+ return true;
230
+ // Fullwidth ASCII forms (includes fullwidth punctuation)
231
+ if (cp >= 0xff01 && cp <= 0xff0f)
232
+ return true;
233
+ if (cp >= 0xff1a && cp <= 0xff20)
234
+ return true;
235
+ if (cp >= 0xff3b && cp <= 0xff40)
236
+ return true;
237
+ if (cp >= 0xff5b && cp <= 0xff65)
238
+ return true;
239
+ // General Punctuation block
240
+ if (cp >= 0x2000 && cp <= 0x206f)
241
+ return true;
242
+ return false;
243
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * qtype's per-character record. Inspired by Keybr's Attr bitmask + Char
3
+ * pair, but re-typed for qtype (mutable timestamp field, no cls, no
4
+ * StyledText spans — qtype renders characters uniformly).
5
+ *
6
+ * Each Char corresponds to exactly one target codepoint. The `attrs`
7
+ * bitmask carries the render state; `timestamp` is the wall-clock time
8
+ * (ms) the char was resolved, 0 if not yet.
9
+ *
10
+ * `target` and `typed` are separate so consumers never have to guess
11
+ * "which one does codePoint mean in this state?" —
12
+ *
13
+ * • `target` — fixed at construction time. Always the character the
14
+ * material asks for at this slot. Consumers use this for layout
15
+ * decisions (is-newline / is-space) that depend on the material's
16
+ * structure, not on user input.
17
+ * • `typed` — what the user actually typed. Equals `target` on
18
+ * pending / hit / recovered slots; differs only when Miss is set.
19
+ * Consumers use this for what to RENDER (the visible glyph).
20
+ */
21
+ export const CharAttr = {
22
+ Normal: 0,
23
+ Hit: 1 << 0,
24
+ Miss: 1 << 1,
25
+ Cursor: 1 << 2,
26
+ Recover: 1 << 3,
27
+ };
28
+ export function hasAttr(attrs, flag) {
29
+ return (attrs & flag) !== 0;
30
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Return value from TypingInput's public methods.
3
+ *
4
+ * Correct — typed the expected char.
5
+ * Incorrect — typed something else.
6
+ * Restart — the input rewound past position 0 or was reset.
7
+ * Recover — user cleared a wrong char via backspace or re-typed
8
+ * correctly after a miss.
9
+ */
10
+ export const Feedback = {
11
+ Correct: 'correct',
12
+ Incorrect: 'incorrect',
13
+ Restart: 'restart',
14
+ Recover: 'recover',
15
+ };
@@ -0,0 +1,5 @@
1
+ export { TypingInput } from './TypingInput.js';
2
+ export { CharAttr, hasAttr } from './chars.js';
3
+ export { Feedback } from './feedback.js';
4
+ export { defaultTypingSettings } from './settings.js';
5
+ export { computeStats } from './stats.js';
@@ -0,0 +1,8 @@
1
+ export const defaultTypingSettings = {
2
+ stopOnError: false,
3
+ forgiveErrors: false,
4
+ spaceSkipsWords: false,
5
+ ignoreCase: false,
6
+ ignorePunctuation: false,
7
+ allowBackspace: true,
8
+ };
@@ -0,0 +1,45 @@
1
+ import { CharAttr, hasAttr } from './chars.js';
2
+ const MS_PER_MINUTE = 60_000;
3
+ export function computeStats(chars, position, completed, now) {
4
+ let correctCount = 0;
5
+ let wrongCount = 0;
6
+ let firstTs = null;
7
+ let lastTs = null;
8
+ for (let i = 0; i < position; i++) {
9
+ const c = chars[i];
10
+ if (hasAttr(c.attrs, CharAttr.Hit))
11
+ correctCount++;
12
+ else if (hasAttr(c.attrs, CharAttr.Miss))
13
+ wrongCount++;
14
+ if (c.timestamp > 0) {
15
+ if (firstTs === null || c.timestamp < firstTs)
16
+ firstTs = c.timestamp;
17
+ if (lastTs === null || c.timestamp > lastTs)
18
+ lastTs = c.timestamp;
19
+ }
20
+ }
21
+ const startedAt = firstTs;
22
+ const finishedAt = completed ? lastTs : null;
23
+ const endedAt = finishedAt ?? (startedAt !== null ? now : null);
24
+ const elapsedMs = startedAt !== null && endedAt !== null
25
+ ? Math.max(1, endedAt - startedAt)
26
+ : 0;
27
+ const totalTyped = correctCount + wrongCount;
28
+ const accuracy = totalTyped === 0 ? 1 : correctCount / totalTyped;
29
+ const cpm = elapsedMs > 0 ? (correctCount / elapsedMs) * MS_PER_MINUTE : 0;
30
+ const wpm = cpm / 5;
31
+ return {
32
+ wpm: round(wpm),
33
+ cpm: round(cpm),
34
+ accuracy: round(accuracy, 4),
35
+ elapsedMs,
36
+ correctCount,
37
+ wrongCount,
38
+ startedAt,
39
+ finishedAt,
40
+ };
41
+ }
42
+ function round(n, digits = 2) {
43
+ const p = 10 ** digits;
44
+ return Math.round(n * p) / p;
45
+ }
@@ -1,2 +1,10 @@
1
- export * from './engine.js';
1
+ /**
2
+ * qtype's typing domain module.
3
+ *
4
+ * Everything here is platform-agnostic: no React, no DOM, no Node
5
+ * fs / process. CLI can drive the engine to analyze event logs; View
6
+ * subscribes to it for real-time UI updates; anyone else (future
7
+ * desktop shell, TUI, batch benchmark) can consume the same code.
8
+ */
9
+ export * from './engine/index.js';
2
10
  export * from './metrics.js';