@qnroa/qtype 0.0.1 → 0.0.3

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 +1 -3
  2. package/CHANGELOG.zh.md +1 -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 +39 -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-BLryk3W4.css +1 -0
  224. package/dist/view/assets/index-ByMJp0a5.js +3 -0
  225. package/dist/view/assets/index-CRh4USfC.js +189 -0
  226. package/dist/view/assets/index-JNx7Ohoj.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 +10 -2
  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
package/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Changelog
2
2
 
3
- Chinese: [CHANGELOG.zh.md](./CHANGELOG.zh.md)
3
+ Chinese: [CHANGELOG.zh.md](https://www.npmjs.com/package/@qnroa/qtype?activeTab=code&filepath=CHANGELOG.zh.md)
4
4
 
5
5
  All notable changes to this project are documented in this file.
6
6
 
@@ -93,5 +93,3 @@ with this version.
93
93
  - `content search` is plain substring — no tag / type filters
94
94
  - Welcome feature is a skeleton
95
95
  - E2E automation is minimal
96
-
97
- See [TODO.md](./TODO.md) for the tracked backlog.
package/CHANGELOG.zh.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # 变更记录
2
2
 
3
- English: [CHANGELOG.md](./CHANGELOG.md)
3
+ English: [CHANGELOG.md](https://www.npmjs.com/package/@qnroa/qtype?activeTab=code&filepath=CHANGELOG.md)
4
4
 
5
5
  本文件记录本项目的所有值得注意的变更。
6
6
 
@@ -79,4 +79,3 @@ English: [CHANGELOG.md](./CHANGELOG.md)
79
79
  - Welcome 特性只是骨架
80
80
  - E2E 自动化很少
81
81
 
82
- 追踪清单见 [TODO.zh.md](./TODO.zh.md)。
package/README.md CHANGED
@@ -1,79 +1,106 @@
1
1
  # qtype
2
2
 
3
- **English** | [中文](./README.zh.md)
3
+ **English** | [中文](https://www.npmjs.com/package/@qnroa/qtype?activeTab=code&filepath=README.zh.md)
4
4
 
5
- **Markdown-driven typing review / practice tool.** Write a `## Q:` / `## A:` card, and qtype makes you **type the answer** to review it.
6
-
7
- qtype is the sister project of [qrism](https://github.com/qnroa/qrism): qrism focuses on **graph-navigated knowledge reading**, qtype focuses on **active typing to internalise knowledge**. Both share the same "MD is the single source of truth" value.
5
+ **Markdown-driven typing practice tool.** Author cards in Markdown, and qtype makes you **type them** to internalise them. Six card shapes (`word` / `sentence` / `article` / `snippet` / `qa` / `code`) cover everything from GRE vocab drills to code snippets to poems.
8
6
 
9
7
  ## Quick start
10
8
 
11
9
  ```bash
12
- # One-time
13
- npm install -g @qnroa/qtype
14
-
15
- # Or run directly via npx
16
- npx @qnroa/qtype init my-deck
10
+ mkdir my-deck
11
+ npx @qnroa/qtype init my-deck --lang en
17
12
  cd my-deck
18
- # Author your material/*.md files (see QTYPE.md)
13
+ # `qtype init` ships one starter file per material type under material/
14
+ # — edit any of them, or add your own:
15
+ # material/word.md sentence.md article.md snippet.md qa.md code.md
19
16
  npx @qnroa/qtype publish build
20
- npx @qnroa/qtype publish serve # http://localhost:4000
17
+ npx @qnroa/qtype publish serve # http://localhost:4000
21
18
  ```
22
19
 
23
20
  Requires Node ≥ 20.
24
21
 
25
- ## CLI
26
-
27
- | Command | What it does |
28
- |---|---|
29
- | `qtype init [dir]` | Scaffold a material repo (with `QTYPE.md` spec + AGENTS/CLAUDE/GEMINI symlinks) |
30
- | `qtype check [dir]` | Validate card format (read-only) |
31
- | `qtype publish build` | Assemble the static site into `dist/` |
32
- | `qtype publish serve` | Local preview |
33
- | `qtype publish init` | Write `.gitignore` + GitHub Pages workflow |
34
- | `qtype pack` | Bundle the material repo into `.tar.gz` |
35
- | `qtype config` | Get / set / unset / list settings (user + repo layers) |
36
- | `qtype repo new / sync` | Create a new repo / fill in conventions |
37
-
38
- Run `qtype <cmd> --help` for full options.
39
-
40
22
  ## Card format
41
23
 
42
- A single card:
24
+ A file is one deck; its `type` (in frontmatter) picks the shape. The
25
+ simplest example — one sentence card:
43
26
 
44
27
  ```markdown
45
28
  ---
46
- tags: [js, basics]
47
- type: normal
29
+ title: Warm-up
30
+ type: sentence
31
+ tags: [warmup]
48
32
  ---
49
33
 
50
- ## Q:
34
+ # The quick brown fox jumps over the lazy dog.
35
+ ### The classic English pangram.
36
+ ```
51
37
 
52
- What are the three core concepts of React?
38
+ `#` H1 splits cards. `sentence` uses H2 for a phonetic and H3 for a
39
+ translation. Other types differ:
53
40
 
54
- ## A:
41
+ | type | Typing target | Extras |
42
+ |---|---|---|
43
+ | `word` | H1 word | H2 phonetic · H3 gloss · H4 image |
44
+ | `sentence` | H1 sentence | H2 phonetic · H3 translation · H4 image |
45
+ | `article` | Body under H1 | inline images extracted |
46
+ | `snippet` | Body under H1 | centered layout (poems / aphorisms) |
47
+ | `qa` | `## A` block | `## Q` prompt (full markdown) |
48
+ | `code` | Fenced block in `## A` | Optional Shiki highlight |
55
49
 
56
- Components, props, and state.
57
- ```
50
+ Full spec: [`templates/en/QTYPE.md`](https://www.npmjs.com/package/@qnroa/qtype?activeTab=code&filepath=templates/en/QTYPE.md) (dropped
51
+ into every new repo). Deep reference:
52
+ [docs/en/material-types.md](https://www.npmjs.com/package/@qnroa/qtype?activeTab=code&filepath=docs/en/material-types.md).
58
53
 
59
- Multiple cards in one file are separated by `\n\n---\n\n` (on its own line).
54
+ **Rich rendering** (display side, not typed): mermaid diagrams
55
+ (pre-rendered at build time), KaTeX math (`$…$` / `$$…$$`, lazy-loaded),
56
+ code highlighting via Shiki. See material-types.md for details.
60
57
 
61
- Supported types: `normal | word | sentence`. `word` / `sentence` cards may carry `phonetic` / `gloss` / `translation`. Full spec lives in `templates/{zh,en}/QTYPE.md` (copied into your repo by `qtype init`).
58
+ ## CLI
59
+
60
+ | Command | Notes |
61
+ |---|---|
62
+ | `qtype init [dir]` | Scaffold a material repo (`QTYPE.md` + 6 starter types + `.qtype/`) |
63
+ | `qtype check [dir]` | Validate cards (read-only, exit 1 on error) |
64
+ | `qtype content search <q>` | Full-text search, supports `--tag` / `--type` / `--json` |
65
+ | `qtype publish build` | Assemble static site into `dist/` (mermaid pre-render, optional AES-GCM encrypt) |
66
+ | `qtype publish serve` | Local preview on `:4000` |
67
+ | `qtype publish init` | Write `.gitignore` (+ `--ci github` for a Pages workflow) |
68
+ | `qtype publish password set` | Password-protect the site (argon2id → AES-256) |
69
+ | `qtype pack` | Bundle repo into `.tar.gz` |
70
+ | `qtype config get/set/list` | User + repo layer settings (`~/.config/qtype/` + `<repo>/.qtype/`) |
71
+ | `qtype repo new / sync` | Create a new repo / re-sync conventions |
72
+
73
+ Run `qtype <cmd> --help` for full options. Full listing:
74
+ [docs/en/cli-reference.md](https://www.npmjs.com/package/@qnroa/qtype?activeTab=code&filepath=docs/en/cli-reference.md).
75
+
76
+ ## Password protection
77
+
78
+ ```bash
79
+ qtype publish password set # interactive
80
+ QTYPE_PUBLISH_PASSWORD=hunter2 qtype publish build
81
+ ```
82
+
83
+ Content encrypts client-side with AES-256-GCM; the argon2id-derived key
84
+ is cached in the user's sessionStorage so a reload in the same tab
85
+ doesn't re-prompt. Threat model: prevents casual URL-holders from
86
+ reading, not a motivated attacker who downloads and brute-forces the
87
+ KDF offline.
62
88
 
63
89
  ## Naming
64
90
 
65
- - `qrism = Q + prism` — the prism of questions, refracting MD into an explorable knowledge graph.
66
- - `qtype = Q + type` — typing your way through questions, turning MD into typable cards.
91
+ `qtype = Q + type` — typing your way through questions, turning MD into typable cards.
67
92
 
68
93
  ## Documentation
69
94
 
70
- - [Architecture](./docs/en/architecture.md) — how `core` / `cli` / `view` relate
71
- - [CLI reference](./docs/en/cli-reference.md) — brief overview of every command
72
- - [Authoring guide](./docs/en/authoring.md) — quick primer on writing `material/*.md`
73
- - [`CONTRIBUTING.md`](./CONTRIBUTING.md) — dev setup + PR flow
74
- - [`CHANGELOG.md`](./CHANGELOG.md) — release history
75
-
76
- Full authoring spec: [`templates/en/QTYPE.md`](./templates/en/QTYPE.md) / [`templates/zh/QTYPE.md`](./templates/zh/QTYPE.md). Copied into every new repo as `QTYPE.md` based on `qtype init --lang zh|en`.
95
+ - [Getting started](https://www.npmjs.com/package/@qnroa/qtype?activeTab=code&filepath=docs/en/getting-started.md) — 5-minute walkthrough
96
+ - [Authoring guide](https://www.npmjs.com/package/@qnroa/qtype?activeTab=code&filepath=docs/en/authoring.md) — quick primer on writing `material/*.md`
97
+ - [Material types](https://www.npmjs.com/package/@qnroa/qtype?activeTab=code&filepath=docs/en/material-types.md) — deep reference for all 6 types
98
+ - [CLI reference](https://www.npmjs.com/package/@qnroa/qtype?activeTab=code&filepath=docs/en/cli-reference.md) — every command + config key
99
+ - [Settings](https://www.npmjs.com/package/@qnroa/qtype?activeTab=code&filepath=docs/en/settings.md) — every user-facing setting
100
+ - [Deployment](https://www.npmjs.com/package/@qnroa/qtype?activeTab=code&filepath=docs/en/deployment.md) — GitHub Pages / Cloudflare / nginx
101
+ - [Keyboard shortcuts](https://www.npmjs.com/package/@qnroa/qtype?activeTab=code&filepath=docs/en/shortcuts.md) key bindings + rebinding
102
+ - [Typing rules](https://www.npmjs.com/package/@qnroa/qtype?activeTab=code&filepath=docs/en/typing-rules.md) — ignoreCase / retryOnFail / etc.
103
+ - [`CHANGELOG.md`](https://www.npmjs.com/package/@qnroa/qtype?activeTab=code&filepath=CHANGELOG.md) — release history
77
104
 
78
105
  ## Development
79
106
 
@@ -81,33 +108,38 @@ Full authoring spec: [`templates/en/QTYPE.md`](./templates/en/QTYPE.md) / [`temp
81
108
  npm install
82
109
  npm run dev:view # http://localhost:5173 (view hot dev)
83
110
  npm test # vitest
84
- npm run build # build:cli + build:view
111
+ npm run build # CLI (tsc)
112
+ npm run build:view # SPA bundle (vite)
113
+ npm run build:all # both
85
114
  npm run lint # tsc --noEmit (cli + view tsconfigs)
86
115
  ```
87
116
 
117
+ Point the dev server at a specific material directory:
118
+
119
+ ```bash
120
+ QTYPE_DEV_MATERIAL=templates/zh/material npm run dev:view
121
+ ```
122
+
88
123
  ## Repo layout
89
124
 
90
125
  ```
91
126
  qtype/
92
127
  ├── src/
93
- │ ├── core/ # pure logic, no IO (parser / typing / keystroke / ...)
94
- │ ├── view/ # React SPA
128
+ │ ├── core/ # pure logic, no IO (parser / typing engine / keystroke / encrypt)
129
+ │ ├── view/ # React SPA (TanStack Router + Vite)
95
130
  │ ├── store/ # persistence (KvStore + JsonStorage / MemoryStorage / BrowserStorage)
96
131
  │ └── cli/ # commander + registry/mount pattern
97
- ├── templates/{zh,en}/ # copied into user repo by init
98
- ├── tsconfig.{cli,view}.json
132
+ ├── templates/{zh,en}/ # copied into user repos by init
133
+ ├── examples/{zh,en}/ # real-shape repos for smoke tests + dev
134
+ ├── docs/ # this documentation
99
135
  └── vite.config.ts # outputs to dist/view
100
136
  ```
101
137
 
102
138
  Publish structure:
103
139
  - `dist/cli/` — CLI executable (`bin: qtype`)
104
- - `dist/core/` — re-usable for external APIs (`exports "./core"`)
140
+ - `dist/core/` — reusable types (`exports "./core"`)
105
141
  - `dist/view/` — pre-built SPA; `publish build` copies this into user `dist/`
106
142
 
107
- ## Relationship to qrism
108
-
109
- The CLI structure (registry / mount / i18n / bootstrap) mirrors qrism almost one-for-one. QTYPE.md is the counterpart of QRISM.md — an LLM-authoring spec for structured Markdown. The only real difference is that qtype cards are "meant to be typed" while qrism nodes are "meant to be read".
110
-
111
143
  ## License
112
144
 
113
- Apache-2.0 — see [LICENSE](./LICENSE).
145
+ Apache-2.0 — see [LICENSE](https://www.npmjs.com/package/@qnroa/qtype?activeTab=code&filepath=LICENSE).
package/README.zh.md CHANGED
@@ -1,89 +1,112 @@
1
1
  # qtype
2
2
 
3
- [English](./README.md) | **中文**
3
+ [English](https://www.npmjs.com/package/@qnroa/qtype?activeTab=code&filepath=README.md) | **中文**
4
4
 
5
- **Markdown 驱动的打字复习/练习工具。** 写一份 `## Q:` / `## A:` 卡片,qtype 让你把答案**打一遍**来复习。
6
-
7
- qtype 是 [qrism](https://github.com/qnroa/qrism) 的姊妹项目:qrism 关注**图导航的知识阅读**,qtype 关注**主动打字的知识内化**。共享同一个"MD 是唯一真相源"的价值观。
5
+ **Markdown 驱动的打字练习工具。** Markdown 写卡片,qtype 让你把它**打一遍**来内化。6 种卡片形态(`word` / `sentence` / `article` / `snippet` / `qa` / `code`)覆盖从背单词到练代码到抄诗的所有场景。
8
6
 
9
7
  ## 快速开始
10
8
 
11
9
  ```bash
12
- # 一次性
13
- npm install -g @qnroa/qtype
14
-
15
- # 或直接 npx
16
- npx @qnroa/qtype init my-deck
10
+ mkdir my-deck
11
+ npx @qnroa/qtype init my-deck --lang zh
17
12
  cd my-deck
18
- # 用你的编辑器/AI 助手写 material/*.md(详见 QTYPE.md)
13
+ # `qtype init` 会在 material/ 下放 6 张类型模板 —— 直接改任意一张,
14
+ # 或复制一份重命名当新卡组:
15
+ # material/word.md sentence.md article.md snippet.md qa.md code.md
19
16
  npx @qnroa/qtype publish build
20
17
  npx @qnroa/qtype publish serve # http://localhost:4000
21
18
  ```
22
19
 
23
20
  需要 Node ≥ 20。
24
21
 
25
- ## CLI
26
-
27
- | 命令 | 作用 |
28
- |---|---|
29
- | `qtype init [dir]` | 初始化资料仓库(含 QTYPE.md 规范 + AGENTS/CLAUDE/GEMINI 软链) |
30
- | `qtype check [dir]` | 校验卡片格式,只读 |
31
- | `qtype publish build` | 拼装静态站到 `dist/` |
32
- | `qtype publish serve` | 本地预览 |
33
- | `qtype publish init` | 写 `.gitignore` + GitHub Pages workflow |
34
- | `qtype pack` | 打包整个资料为 `.tar.gz` |
35
- | `qtype config` | 读 / 写 / 删 / 列配置(user + repo 双层) |
36
- | `qtype repo new / sync` | 新建仓库 / 补齐约定 |
37
-
38
- 每个命令 `qtype <cmd> --help` 看完整选项。
39
-
40
22
  ## 卡片格式
41
23
 
42
- 一张卡:
24
+ 一个文件 = 一副卡组,`type`(在 frontmatter)决定形态。最简单的例子 —— 一张 sentence 卡:
43
25
 
44
26
  ```markdown
45
27
  ---
46
- tags: [js, basics]
47
- type: normal
28
+ title: 热身
29
+ type: sentence
30
+ tags: [热身]
48
31
  ---
49
32
 
50
- ## Q:
33
+ # The quick brown fox jumps over the lazy dog.
34
+ ### 英文全字母句。
35
+ ```
51
36
 
52
- React 三大核心概念是什么?
37
+ `#` H1 分卡。`sentence` 卡用 H2 放音标、H3 放翻译。其他类型:
53
38
 
54
- ## A:
39
+ | type | 打字目标 | 附加项 |
40
+ |---|---|---|
41
+ | `word` | H1 单词 | H2 音标 · H3 释义 · H4 图片 |
42
+ | `sentence` | H1 句子 | H2 音标 · H3 翻译 · H4 图片 |
43
+ | `article` | H1 之下正文 | 内嵌图片自动抽出 |
44
+ | `snippet` | H1 之下正文 | 居中排版(诗句/格言) |
45
+ | `qa` | `## A` 块 | `## Q` 题干(完整 markdown) |
46
+ | `code` | `## A` 里的 fenced 块 | 可选 Shiki 语法高亮 |
55
47
 
56
- 组件、props 和 state
57
- ```
48
+ 完整规范:[`templates/zh/QTYPE.md`](https://www.npmjs.com/package/@qnroa/qtype?activeTab=code&filepath=templates/zh/QTYPE.md)(会拷进每个新仓库)。深度参考:[docs/zh/material-types.md](https://www.npmjs.com/package/@qnroa/qtype?activeTab=code&filepath=docs/zh/material-types.md)
58
49
 
59
- 一份 MD 里多张卡片用 `\n\n---\n\n`(独占行)分隔。
50
+ **富渲染**(展示侧,不参与打字):Mermaid 流程图(构建时预渲染)、KaTeX 数学公式(`$…$` / `$$…$$`,懒加载)、Shiki 代码高亮。详见 material-types.md。
60
51
 
61
- 支持 `type: normal | word | sentence`,`word` / `sentence` 可带 `phonetic` / `gloss` / `translation`。完整规范见 `templates/{zh,en}/QTYPE.md`(由 `qtype init` 复制到你的仓库)。
52
+ ## CLI
53
+
54
+ | 命令 | 作用 |
55
+ |---|---|
56
+ | `qtype init [dir]` | 初始化资料仓库(QTYPE.md + 6 种类型起手模板 + `.qtype/`) |
57
+ | `qtype check [dir]` | 校验卡片(只读,错则退出码 1) |
58
+ | `qtype content search <q>` | 全文搜索,支持 `--tag` / `--type` / `--json` |
59
+ | `qtype publish build` | 拼装静态站到 `dist/`(mermaid 预渲染、可选 AES-GCM 加密) |
60
+ | `qtype publish serve` | 本地 `:4000` 预览 |
61
+ | `qtype publish init` | 写 `.gitignore`(加 `--ci github` 写 Pages workflow) |
62
+ | `qtype publish password set` | 密码保护(argon2id → AES-256) |
63
+ | `qtype pack` | 打包资料为 `.tar.gz` |
64
+ | `qtype config get/set/list` | 双层配置(`~/.config/qtype/` + `<repo>/.qtype/`) |
65
+ | `qtype repo new / sync` | 新建仓库 / 补齐约定 |
66
+
67
+ 每个命令 `qtype <cmd> --help` 看完整选项。完整列表:[docs/zh/cli-reference.md](https://www.npmjs.com/package/@qnroa/qtype?activeTab=code&filepath=docs/zh/cli-reference.md)。
68
+
69
+ ## 密码保护
70
+
71
+ ```bash
72
+ qtype publish password set # 交互输入
73
+ QTYPE_PUBLISH_PASSWORD=hunter2 qtype publish build
74
+ ```
75
+
76
+ 内容用 AES-256-GCM **客户端**加密,argon2id 派生的密钥缓存在浏览器 sessionStorage(同 tab 刷新不再问密码)。威胁模型:防止拿到 URL 的路人随便看,不防拿到 dist 后离线暴力破解 KDF 的高动机攻击者。
62
77
 
63
78
  ## 命名
64
79
 
65
- - `qrism = Q + prism`(问题的棱镜)—— 把 MD 折射成可探索的知识图。
66
- - `qtype = Q + type`(问题的打字)—— 把 MD 转成可打字复现的卡片。
80
+ `qtype = Q + type`(问题的打字)—— 把 MD 转成可打字复现的卡片
67
81
 
68
82
  ## 文档
69
83
 
70
- - [架构总览](./docs/zh/architecture.md) —— `core` / `cli` / `view` 怎么分工
71
- - [CLI 简介](./docs/zh/cli-reference.md) —— 每个命令的简单说明
72
- - [撰写指南](./docs/zh/authoring.md) —— `material/*.md` 的快速入门
73
- - [`CONTRIBUTING.zh.md`](./CONTRIBUTING.zh.md) —— 开发环境 + PR 流程
74
- - [`CHANGELOG.zh.md`](./CHANGELOG.zh.md) —— 版本历史
75
-
76
- 规范全文:[`templates/en/QTYPE.md`](./templates/en/QTYPE.md) /
77
- [`templates/zh/QTYPE.md`](./templates/zh/QTYPE.md)。`qtype init --lang zh|en` 时拷成用户仓库的 `QTYPE.md`。
84
+ - [Getting Started](https://www.npmjs.com/package/@qnroa/qtype?activeTab=code&filepath=docs/zh/getting-started.md) —— 5 分钟教程
85
+ - [撰写指南](https://www.npmjs.com/package/@qnroa/qtype?activeTab=code&filepath=docs/zh/authoring.md) —— `material/*.md` 的快速入门
86
+ - [Material 类型详解](https://www.npmjs.com/package/@qnroa/qtype?activeTab=code&filepath=docs/zh/material-types.md) —— 6 种类型的深度参考
87
+ - [CLI 简介](https://www.npmjs.com/package/@qnroa/qtype?activeTab=code&filepath=docs/zh/cli-reference.md) —— 所有命令 + 配置键
88
+ - [Settings 参考](https://www.npmjs.com/package/@qnroa/qtype?activeTab=code&filepath=docs/zh/settings.md) —— 所有用户可见的设置项
89
+ - [部署指南](https://www.npmjs.com/package/@qnroa/qtype?activeTab=code&filepath=docs/zh/deployment.md) —— GitHub Pages / Cloudflare / nginx
90
+ - [快捷键](https://www.npmjs.com/package/@qnroa/qtype?activeTab=code&filepath=docs/zh/shortcuts.md) —— 按键绑定 + 重绑
91
+ - [打字规则](https://www.npmjs.com/package/@qnroa/qtype?activeTab=code&filepath=docs/zh/typing-rules.md) —— ignoreCase / retryOnFail / 等
92
+ - [`CHANGELOG.zh.md`](https://www.npmjs.com/package/@qnroa/qtype?activeTab=code&filepath=CHANGELOG.zh.md) —— 版本历史
78
93
 
79
94
  ## 开发
80
95
 
81
96
  ```bash
82
97
  npm install
83
- npm run dev:view # http://localhost:5173view 端热开发)
98
+ npm run dev:view # http://localhost:5173(view 端热开发)
84
99
  npm test # vitest
85
- npm run build # build:cli + build:view
86
- npm run lint # tsc --noEmit(cli + view 两份 tsconfig)
100
+ npm run build # CLI(tsc)
101
+ npm run build:view # SPA bundle(vite)
102
+ npm run build:all # 一起
103
+ npm run lint # tsc --noEmit(cli + view 两份 tsconfig)
104
+ ```
105
+
106
+ 让 dev server 指向别的 material 目录:
107
+
108
+ ```bash
109
+ QTYPE_DEV_MATERIAL=templates/zh/material npm run dev:view
87
110
  ```
88
111
 
89
112
  ## 目录结构
@@ -91,24 +114,21 @@ npm run lint # tsc --noEmit(cli + view 两份 tsconfig)
91
114
  ```
92
115
  qtype/
93
116
  ├── src/
94
- │ ├── core/ # 纯逻辑,无 IOparser / typing / keystroke / ...)
95
- │ ├── view/ # React SPA
96
- │ ├── store/ # 持久化(KvStore + JsonStorage / MemoryStorage / BrowserStorage
117
+ │ ├── core/ # 纯逻辑,无 IO(parser / typing engine / keystroke / encrypt)
118
+ │ ├── view/ # React SPA(TanStack Router + Vite)
119
+ │ ├── store/ # 持久化(KvStore + JsonStorage / MemoryStorage / BrowserStorage)
97
120
  │ └── cli/ # commander + registry/mount 模式
98
- ├── templates/{zh,en}/ # init 时复制到用户仓库
99
- ├── tsconfig.{cli,view}.json
121
+ ├── templates/{zh,en}/ # init 时拷进用户仓库
122
+ ├── examples/{zh,en}/ # smoke test + dev 用的真实形状仓库
123
+ ├── docs/ # 这份文档
100
124
  └── vite.config.ts # 输出到 dist/view
101
125
  ```
102
126
 
103
- 发布结构:
104
- - `dist/cli/` — CLI 可执行文件(`bin: qtype`)
105
- - `dist/core/` — 复用给外部 API(`exports "./core"`)
106
- - `dist/view/` — 预编译的 SPA,`publish build` 拷贝这里到用户 `dist/`
107
-
108
- ## 与 qrism 的关系
109
-
110
- CLI 结构(registry / mount / i18n / bootstrap)几乎一比一镜像 qrism。QTYPE.md 对应 QRISM.md —— 一份让 LLM 生成结构化 Markdown 的规范。区别只在于 qtype 的卡片是"要被打字重现的",而 qrism 的节点是"要被阅读的"。
127
+ 发布结构:
128
+ - `dist/cli/` — CLI 可执行(`bin: qtype`)
129
+ - `dist/core/` — 类型复用(`exports "./core"`)
130
+ - `dist/view/` — 预编译 SPA;`publish build` 时拷到用户 `dist/`
111
131
 
112
132
  ## License
113
133
 
114
- Apache-2.0 —— 详见 [LICENSE](./LICENSE)。
134
+ Apache-2.0 —— 详见 [LICENSE](https://www.npmjs.com/package/@qnroa/qtype?activeTab=code&filepath=LICENSE)。
@@ -16,9 +16,9 @@
16
16
  * ## Schema (git-style layering)
17
17
  *
18
18
  * Both KVs enable `strict: true` (`qtype config set` needs to reject typo'd keys).
19
- * Each key is one of:
19
+ * Schemas live in `./schemas/`, one file per scope:
20
20
  * - user-only → registered on userKv only (cli.*)
21
- * - repo-only → registered on repoKv only (repo.name)
21
+ * - repo-only → registered on repoKv only (repo.name, publish.*)
22
22
  * - both → registered on BOTH sides (repo.lang, typing.*).
23
23
  * Users can set a global default; individual repos override.
24
24
  * Read precedence: repo > user > schema default.
@@ -26,119 +26,8 @@
26
26
  import fs from 'node:fs';
27
27
  import os from 'node:os';
28
28
  import path from 'node:path';
29
- import { KvStore, JsonStorage, enumValidator, nonEmptyStringValidator, boolValidator, positiveIntValidator, } from '../store/index.js';
30
- // ═══════════════════════════════════════════════════════════════
31
- // Schemas grouped by scope (git-style layering)
32
- // ═══════════════════════════════════════════════════════════════
33
- /** user-only: settings tied to the person, not the repo. */
34
- const userOnlySchema = {
35
- // CLI display language. Values are BCP 47 codes; the short forms
36
- // `zh` / `en` are accepted for backwards compatibility with older
37
- // config files and normalised at read time by the i18n layer.
38
- 'cli.lang': {
39
- validator: enumValidator(['zh-CN', 'en-US', 'zh', 'en']),
40
- default: 'en-US',
41
- },
42
- 'cli.color': {
43
- validator: enumValidator(['always', 'never', 'auto']),
44
- default: 'auto',
45
- },
46
- };
47
- /** repo-only: settings that only make sense per-repo. */
48
- const repoOnlySchema = {
49
- 'repo.name': {
50
- validator: nonEmptyStringValidator(),
51
- },
52
- // ── Password protection for `publish build` ─────────────
53
- // On/off toggle. When true, `publish build` reads the other three fields
54
- // and encrypts every material/*.md before writing to dist.
55
- 'publish.password.enabled': {
56
- validator: boolValidator(),
57
- default: false,
58
- },
59
- // Argon2id PHC-style hash of the plaintext password, used to verify the
60
- // password entered at build time. NOT the AES key — that's derived
61
- // separately via `publish.password.aesSalt`. Empty string when protection
62
- // is off; shape enforced by `publish password set` writing a well-formed
63
- // PHC string.
64
- 'publish.password.hash': {
65
- validator: (v) => typeof v === 'string'
66
- ? null
67
- : { code: 'err.schema.nonEmptyString', fallback: 'must be a string' },
68
- default: '',
69
- },
70
- // Base64 salt used with argon2id (password → AES-256 key). Rotated only
71
- // when the user calls `publish password set` — stable across rebuilds so
72
- // the same password unlocks previously published sites.
73
- 'publish.password.aesSalt': {
74
- validator: (v) => typeof v === 'string'
75
- ? null
76
- : { code: 'err.schema.nonEmptyString', fallback: 'must be a string' },
77
- default: '',
78
- },
79
- // What to encrypt. `all` covers material/ + any future encrypted assets;
80
- // `material-only` limits encryption to material/*.md (same behavior in
81
- // v0 since qtype has no other content streams).
82
- 'publish.password.scope': {
83
- validator: enumValidator(['all', 'material-only', 'selected']),
84
- default: 'all',
85
- },
86
- // When scope='selected', the list of file names (relative to material/)
87
- // to encrypt. Ignored otherwise.
88
- 'publish.password.selected': {
89
- validator: (v) => Array.isArray(v) && v.every((x) => typeof x === 'string')
90
- ? null
91
- : { code: 'err.schema.nonEmptyString', fallback: 'must be an array of strings' },
92
- default: [],
93
- },
94
- };
95
- /** both: user sets a global default, individual repos override. */
96
- const bothSchema = {
97
- // Repo language. Drives:
98
- // • which template locale (examples/zh vs examples/en) new repos are
99
- // scaffolded from
100
- // • the web UI language — the reader is locked to repo.lang so the UI
101
- // matches the material's own language
102
- //
103
- // Values are BCP 47 codes (`zh-CN` / `en-US`); the short forms `zh` /
104
- // `en` are accepted for backwards compatibility with older `.qtype/
105
- // config.json` files and normalised at read time.
106
- 'repo.lang': {
107
- validator: enumValidator(['zh-CN', 'en-US', 'zh', 'en']),
108
- default: 'en-US',
109
- },
110
- 'typing.theme': {
111
- validator: enumValidator(['mocha', 'latte']),
112
- default: 'mocha',
113
- },
114
- 'typing.targetWpm': {
115
- validator: positiveIntValidator(),
116
- },
117
- 'typing.tabBehavior': {
118
- validator: enumValidator(['reset', 'input', 'disabled']),
119
- default: 'reset',
120
- },
121
- 'typing.hint': {
122
- validator: enumValidator(['none', 'ghost']),
123
- default: 'none',
124
- },
125
- 'typing.showKeyboard': {
126
- validator: boolValidator(),
127
- default: true,
128
- },
129
- 'typing.showHands': {
130
- validator: boolValidator(),
131
- default: true,
132
- },
133
- // Mermaid pre-render mode. `prerender` (default) uses puppeteer at build
134
- // time to bake SVGs into dist/material/mermaid.json; `client` ships raw
135
- // ```mermaid fences and lets the browser render them; `off` treats mermaid
136
- // as plain code blocks.
137
- 'publish.mermaid': {
138
- validator: enumValidator(['prerender', 'client', 'off']),
139
- default: 'prerender',
140
- },
141
- };
29
+ import { KvStore, JsonStorage } from '../store/index.js';
30
+ import { userOnlySchema, repoOnlySchema, bothSchema } from '../core/schemas/index.js';
142
31
  // ═══════════════════════════════════════════════════════════════
143
32
  // Path resolution
144
33
  // ═══════════════════════════════════════════════════════════════
@@ -1,20 +1,21 @@
1
1
  import { t } from '../../i18n/index.js';
2
- import { messages } from './i18n.js';
3
- import { checkAction } from './action.js';
2
+ import { messages as lintMessages } from '../content/lint/i18n.js';
3
+ import { contentLintAction } from '../content/lint/action.js';
4
4
  export const checkCmd = {
5
5
  name: 'check',
6
- messages,
7
- summary: () => t('check.summary'),
8
- describe: () => t('check.description'),
6
+ messages: lintMessages,
7
+ summary: () => t('content.lint.summary'),
8
+ describe: () => t('content.lint.description'),
9
9
  options: [
10
- { flags: '--repo <path>', describe: () => t('check.opt.repo') },
10
+ { flags: '--repo <path>', describe: () => t('content.lint.arg.path') },
11
+ { flags: '--json', describe: () => t('content.lint.opt.json') },
11
12
  ],
12
13
  examples: () => [
13
- { command: 'qtype check', describe: () => t('check.example.basic') },
14
- { command: 'qtype check --repo ./my-material', describe: () => t('check.example.repo') },
14
+ { command: 'qtype check', describe: () => t('content.lint.example.basic') },
15
+ { command: 'qtype check --json', describe: () => t('content.lint.example.json') },
15
16
  ],
16
- seeAlso: ['publish build', 'content lint'],
17
+ seeAlso: ['content lint', 'content validate', 'publish build'],
17
18
  action: async (opts) => {
18
- await checkAction({ repo: opts.repo });
19
+ await contentLintAction({ path: opts.repo, json: opts.json });
19
20
  },
20
21
  };
@@ -1,6 +1,6 @@
1
1
  import { KvError } from '../../../store/index.js';
2
2
  import { userKv, repoKv } from '../../bootstrap.js';
3
- import { info, success, dim, error } from '../../utils/output.js';
3
+ import { info, success, dim, error, fatal } from '../../utils/output.js';
4
4
  import { t } from '../../i18n/index.js';
5
5
  /** --global / --local are mutually exclusive; returns the explicitly-specified scope (neither given → undefined). */
6
6
  function pickScope(opts) {
@@ -61,8 +61,7 @@ export async function configGetAction(key, opts) {
61
61
  if (!has) {
62
62
  const def = kv.getSchema(key)?.default;
63
63
  if (def === undefined) {
64
- error(t('config.get.msg.notSetScoped', { key, scope }));
65
- process.exit(1);
64
+ fatal(t('config.get.msg.notSetScoped', { key, scope }));
66
65
  }
67
66
  console.log(formatValue(def));
68
67
  return;
@@ -85,8 +84,7 @@ export async function configGetAction(key, opts) {
85
84
  // Fall back to default: repo schema first, then user
86
85
  const def = repoKv.getSchema(key)?.default ?? userKv.getSchema(key)?.default;
87
86
  if (def === undefined) {
88
- error(t('config.get.msg.notSet', { key }));
89
- process.exit(1);
87
+ fatal(t('config.get.msg.notSet', { key }));
90
88
  }
91
89
  console.log(formatValue(def));
92
90
  }
@@ -95,8 +93,7 @@ async function showOrigin(key, scope) {
95
93
  const inUser = await userKv.has(key);
96
94
  if (scope === 'user') {
97
95
  if (!inUser) {
98
- error(t('config.get.msg.notSetScoped', { key, scope: 'user' }));
99
- process.exit(1);
96
+ fatal(t('config.get.msg.notSetScoped', { key, scope: 'user' }));
100
97
  }
101
98
  console.log(formatValue(await userKv.get(key)));
102
99
  console.error(` ${dim('from:')} ${userKv.origin} (user)`);
@@ -104,8 +101,7 @@ async function showOrigin(key, scope) {
104
101
  }
105
102
  if (scope === 'repo') {
106
103
  if (!inRepo) {
107
- error(t('config.get.msg.notSetScoped', { key, scope: 'repo' }));
108
- process.exit(1);
104
+ fatal(t('config.get.msg.notSetScoped', { key, scope: 'repo' }));
109
105
  }
110
106
  console.log(formatValue(await repoKv.get(key)));
111
107
  console.error(` ${dim('from:')} ${repoKv.origin} (repo)`);