@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.
- package/CHANGELOG.md +44 -3
- package/CHANGELOG.zh.md +36 -2
- package/README.md +88 -56
- package/README.zh.md +82 -62
- package/dist/cli/bootstrap.js +4 -115
- package/dist/cli/commands/check/index.js +11 -10
- package/dist/cli/commands/config/action.js +5 -9
- package/dist/cli/commands/content/lint/action.js +38 -44
- package/dist/cli/commands/content/search/action.js +44 -34
- package/dist/cli/commands/content/search/i18n.js +6 -0
- package/dist/cli/commands/content/search/index.js +19 -1
- package/dist/cli/commands/content/validate/action.js +28 -27
- package/dist/cli/commands/index.js +1 -3
- package/dist/cli/commands/pack/index.js +2 -1
- package/dist/cli/commands/publish/build/action.js +88 -11
- package/dist/cli/commands/repo/new/action.js +23 -4
- package/dist/cli/commands/repo/new/i18n.js +2 -2
- package/dist/cli/commands/repo/sync/action.js +24 -4
- package/dist/cli/commands/repo/templates.js +2 -1
- package/dist/cli/utils/material.js +49 -0
- package/dist/core/encrypt.js +24 -1
- package/dist/core/index.js +1 -0
- package/dist/core/parser/frontmatter.js +39 -34
- package/dist/core/parser/parseMaterial.js +174 -24
- package/dist/core/romanization/index.js +152 -0
- package/dist/core/schemas/both.js +52 -0
- package/dist/core/schemas/index.js +3 -0
- package/dist/core/schemas/repo.js +55 -0
- package/dist/core/schemas/user.js +19 -0
- package/dist/core/typing/engine/TypingInput.js +243 -0
- package/dist/core/typing/engine/chars.js +30 -0
- package/dist/core/typing/engine/feedback.js +15 -0
- package/dist/core/typing/engine/index.js +5 -0
- package/dist/core/typing/engine/settings.js +8 -0
- package/dist/core/typing/engine/stats.js +45 -0
- package/dist/core/typing/index.js +9 -1
- package/dist/view/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/dist/view/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/dist/view/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/dist/view/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/dist/view/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/dist/view/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/dist/view/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/dist/view/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/dist/view/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/dist/view/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/dist/view/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/dist/view/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/dist/view/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/dist/view/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/dist/view/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/dist/view/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/dist/view/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/dist/view/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/dist/view/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/dist/view/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/dist/view/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/dist/view/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/dist/view/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/dist/view/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/dist/view/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/dist/view/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/dist/view/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/dist/view/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/dist/view/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/dist/view/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/dist/view/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/dist/view/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/dist/view/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/dist/view/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/dist/view/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/dist/view/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/dist/view/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/dist/view/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/dist/view/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/dist/view/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/dist/view/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/dist/view/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/dist/view/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/dist/view/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/dist/view/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/dist/view/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/dist/view/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/dist/view/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/dist/view/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/dist/view/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/dist/view/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/dist/view/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/dist/view/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/dist/view/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/dist/view/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/dist/view/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/dist/view/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/dist/view/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/dist/view/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/dist/view/assets/abap-BdImnpbu.js +1 -0
- package/dist/view/assets/actionscript-3-B3316cI-.js +1 -0
- package/dist/view/assets/ada-bCR0ucgS.js +1 -0
- package/dist/view/assets/ahk-CsyLZFj1.js +1 -0
- package/dist/view/assets/ahk2-8Zs4aa1G.js +1 -0
- package/dist/view/assets/andromeeda-C4gqWexZ.js +1 -0
- package/dist/view/assets/angular-html-DA-rfuFy.js +1 -0
- package/dist/view/assets/angular-ts-BrjP3tb8.js +1 -0
- package/dist/view/assets/apache-Pmp26Uib.js +1 -0
- package/dist/view/assets/apex-DhZFqWV2.js +1 -0
- package/dist/view/assets/apl-CORt7UWP.js +1 -0
- package/dist/view/assets/applescript-Co6uUVPk.js +1 -0
- package/dist/view/assets/ara-BRHolxvo.js +1 -0
- package/dist/view/assets/asciidoc-CSVQ5wI8.js +1 -0
- package/dist/view/assets/asm-D_Q5rh1f.js +1 -0
- package/dist/view/assets/astro-HNnZUWAn.js +1 -0
- package/dist/view/assets/aurora-x-D-2ljcwZ.js +1 -0
- package/dist/view/assets/awk-DMzUqQB5.js +1 -0
- package/dist/view/assets/ayu-dark-DYE7WIF3.js +1 -0
- package/dist/view/assets/ayu-light-BA47KaF1.js +1 -0
- package/dist/view/assets/ayu-mirage-32ctXXKs.js +1 -0
- package/dist/view/assets/ballerina-BFfxhgS-.js +1 -0
- package/dist/view/assets/bat-CickPsom.js +1 -0
- package/dist/view/assets/beancount-k_qm7-4y.js +1 -0
- package/dist/view/assets/berry-uYugtg8r.js +1 -0
- package/dist/view/assets/bibtex-CHM0blh-.js +1 -0
- package/dist/view/assets/bicep-Bmn6On1c.js +1 -0
- package/dist/view/assets/bird2-Bx8U0n9b.js +1 -0
- package/dist/view/assets/blade-2xfisSek.js +1 -0
- package/dist/view/assets/bsl-DlhNcFeZ.js +1 -0
- package/dist/view/assets/c-BIGW1oBm.js +1 -0
- package/dist/view/assets/c3-Dp5svz6Z.js +1 -0
- package/dist/view/assets/cadence-Bv_4Rxtq.js +1 -0
- package/dist/view/assets/cairo-KRGpt6FW.js +1 -0
- package/dist/view/assets/catppuccin-frappe-CZL1YF0i.js +1 -0
- package/dist/view/assets/catppuccin-latte-DH-8KZSZ.js +1 -0
- package/dist/view/assets/catppuccin-macchiato-B7yYVSCf.js +1 -0
- package/dist/view/assets/catppuccin-mocha-Ct7hS0mc.js +1 -0
- package/dist/view/assets/chapel-DTp_pixX.js +1 -0
- package/dist/view/assets/clarity-Dn5IMItf.js +1 -0
- package/dist/view/assets/clojure-P80f7IUj.js +1 -0
- package/dist/view/assets/cmake-D1j8_8rp.js +1 -0
- package/dist/view/assets/cobol-nBiQ_Alo.js +1 -0
- package/dist/view/assets/codeowners-Bp6g37R7.js +1 -0
- package/dist/view/assets/codeql-DsOJ9woJ.js +1 -0
- package/dist/view/assets/coffee-Ch7k5sss.js +1 -0
- package/dist/view/assets/common-lisp-Cg-RD9OK.js +1 -0
- package/dist/view/assets/coq-C7JzOVbR.js +1 -0
- package/dist/view/assets/cpp-BMRokrvK.js +1 -0
- package/dist/view/assets/crystal-DGywbUpC.js +1 -0
- package/dist/view/assets/csharp-DSvCPggb.js +1 -0
- package/dist/view/assets/css-CLj8gQPS.js +1 -0
- package/dist/view/assets/csv-fuZLfV_i.js +1 -0
- package/dist/view/assets/cue-D82EKSYY.js +1 -0
- package/dist/view/assets/cypher-COkxafJQ.js +1 -0
- package/dist/view/assets/d-85-TOEBH.js +1 -0
- package/dist/view/assets/dark-plus-C3mMm8J8.js +1 -0
- package/dist/view/assets/dart-bE4Kk8sk.js +1 -0
- package/dist/view/assets/dax-CEL-wOlO.js +1 -0
- package/dist/view/assets/desktop-BmXAJ9_W.js +1 -0
- package/dist/view/assets/diff-D97Zzqfu.js +1 -0
- package/dist/view/assets/docker-BcOcwvcX.js +1 -0
- package/dist/view/assets/dotenv-Da5cRb03.js +1 -0
- package/dist/view/assets/dracula-BzJJZx-M.js +1 -0
- package/dist/view/assets/dracula-soft-BXkSAIEj.js +1 -0
- package/dist/view/assets/dream-maker-BtqSS_iP.js +1 -0
- package/dist/view/assets/edge-FbVlp4U3.js +1 -0
- package/dist/view/assets/elixir-CkH2-t6x.js +1 -0
- package/dist/view/assets/elm-DbKCFpqz.js +1 -0
- package/dist/view/assets/emacs-lisp-C_m_b--Z.js +1 -0
- package/dist/view/assets/erb-DXfck5VN.js +1 -0
- package/dist/view/assets/erlang-DsQrWhSR.js +1 -0
- package/dist/view/assets/everforest-dark-BgDCqdQA.js +1 -0
- package/dist/view/assets/everforest-light-C8M2exoo.js +1 -0
- package/dist/view/assets/fennel-BYunw83y.js +1 -0
- package/dist/view/assets/fish-BvzEVeQv.js +1 -0
- package/dist/view/assets/fluent-C4IJs8-o.js +1 -0
- package/dist/view/assets/fortran-fixed-form-CkoXwp7k.js +1 -0
- package/dist/view/assets/fortran-free-form-BxgE0vQu.js +1 -0
- package/dist/view/assets/fsharp-CXgrBDvD.js +1 -0
- package/dist/view/assets/gdresource-TyuKm33G.js +1 -0
- package/dist/view/assets/gdscript-DqcFQ5yU.js +1 -0
- package/dist/view/assets/gdshader-DkwncUOv.js +1 -0
- package/dist/view/assets/genie-D0YGMca9.js +1 -0
- package/dist/view/assets/gherkin-DyxjwDmM.js +1 -0
- package/dist/view/assets/git-commit-F4YmCXRG.js +1 -0
- package/dist/view/assets/git-rebase-r7XF79zn.js +1 -0
- package/dist/view/assets/github-dark-DHJKELXO.js +1 -0
- package/dist/view/assets/github-dark-default-Cuk6v7N8.js +1 -0
- package/dist/view/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
- package/dist/view/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
- package/dist/view/assets/github-light-DAi9KRSo.js +1 -0
- package/dist/view/assets/github-light-default-D7oLnXFd.js +1 -0
- package/dist/view/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
- package/dist/view/assets/gleam-BspZqrRM.js +1 -0
- package/dist/view/assets/glimmer-js-ByusRIyA.js +1 -0
- package/dist/view/assets/glimmer-ts-BfAWNZQY.js +1 -0
- package/dist/view/assets/glsl-DplSGwfg.js +1 -0
- package/dist/view/assets/gn-n2N0HUVH.js +1 -0
- package/dist/view/assets/gnuplot-DdkO51Og.js +1 -0
- package/dist/view/assets/go-C27-OAKa.js +1 -0
- package/dist/view/assets/graphql-ChdNCCLP.js +1 -0
- package/dist/view/assets/groovy-gcz8RCvz.js +1 -0
- package/dist/view/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
- package/dist/view/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
- package/dist/view/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
- package/dist/view/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
- package/dist/view/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
- package/dist/view/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
- package/dist/view/assets/hack-BWmVpMyf.js +1 -0
- package/dist/view/assets/haml-D5jkg6IW.js +1 -0
- package/dist/view/assets/handlebars-BpdQsYii.js +1 -0
- package/dist/view/assets/haskell-Df6bDoY_.js +1 -0
- package/dist/view/assets/haxe-CfZj7gIn.js +1 -0
- package/dist/view/assets/hcl-BWvSN4gD.js +1 -0
- package/dist/view/assets/hjson-D5-asLiD.js +1 -0
- package/dist/view/assets/hlsl-D3lLCCz7.js +1 -0
- package/dist/view/assets/horizon-BUw7H-hv.js +1 -0
- package/dist/view/assets/horizon-bright-CUuTKBJd.js +1 -0
- package/dist/view/assets/houston-DnULxvSX.js +1 -0
- package/dist/view/assets/html-derivative-DlHx6ybY.js +1 -0
- package/dist/view/assets/html-pp8916En.js +1 -0
- package/dist/view/assets/http-jrhK8wxY.js +1 -0
- package/dist/view/assets/hurl-irOxFIW8.js +1 -0
- package/dist/view/assets/hxml-2-FPmUDs.js +1 -0
- package/dist/view/assets/hy-DFXneXwc.js +1 -0
- package/dist/view/assets/imba-DGztddWO.js +1 -0
- package/dist/view/assets/index-1XXyxiQO.js +189 -0
- package/dist/view/assets/index-BLryk3W4.css +1 -0
- package/dist/view/assets/index-DIBSxFYl.js +3 -0
- package/dist/view/assets/index-DgHvnMV9.js +153 -0
- package/dist/view/assets/ini-BEwlwnbL.js +1 -0
- package/dist/view/assets/java-CylS5w8V.js +1 -0
- package/dist/view/assets/javascript-wDzz0qaB.js +1 -0
- package/dist/view/assets/jinja-f2NsQr07.js +1 -0
- package/dist/view/assets/jison-wvAkD_A8.js +1 -0
- package/dist/view/assets/json-Cp-IABpG.js +1 -0
- package/dist/view/assets/json5-C9tS-k6U.js +1 -0
- package/dist/view/assets/jsonc-Des-eS-w.js +1 -0
- package/dist/view/assets/jsonl-DcaNXYhu.js +1 -0
- package/dist/view/assets/jsonnet-DFQXde-d.js +1 -0
- package/dist/view/assets/jssm-C2t-YnRu.js +1 -0
- package/dist/view/assets/jsx-g9-lgVsj.js +1 -0
- package/dist/view/assets/julia-5Bft2YPA.js +1 -0
- package/dist/view/assets/just-Cwhn7H3k.js +1 -0
- package/dist/view/assets/kanagawa-dragon-BuwD2xS4.js +1 -0
- package/dist/view/assets/kanagawa-lotus-C3DzagqV.js +1 -0
- package/dist/view/assets/kanagawa-wave-DFGoQZhC.js +1 -0
- package/dist/view/assets/katex-BYZTj3zW.css +1 -0
- package/dist/view/assets/katex-C5jXJg4s.js +257 -0
- package/dist/view/assets/kdl-DV7GczEv.js +1 -0
- package/dist/view/assets/kotlin-BdnUsdx6.js +1 -0
- package/dist/view/assets/kusto-wEQ09or8.js +1 -0
- package/dist/view/assets/laserwave-DUszq2jm.js +1 -0
- package/dist/view/assets/latex-D5pSuvFb.js +1 -0
- package/dist/view/assets/lean-BZvkOJ9d.js +1 -0
- package/dist/view/assets/less-B1dDrJ26.js +1 -0
- package/dist/view/assets/light-plus-B7mTdjB0.js +1 -0
- package/dist/view/assets/liquid-C0sCDyMI.js +1 -0
- package/dist/view/assets/llvm-BZoOZj88.js +1 -0
- package/dist/view/assets/log-2UxHyX5q.js +1 -0
- package/dist/view/assets/logo-BtOb2qkB.js +1 -0
- package/dist/view/assets/lua-BaeVxFsk.js +1 -0
- package/dist/view/assets/luau-BnpPk5vE.js +1 -0
- package/dist/view/assets/make-CHLpvVh8.js +1 -0
- package/dist/view/assets/markdown-Cvjx9yec.js +1 -0
- package/dist/view/assets/marko-DjSrsDqO.js +1 -0
- package/dist/view/assets/material-theme-D5KoaKCx.js +1 -0
- package/dist/view/assets/material-theme-darker-BfHTSMKl.js +1 -0
- package/dist/view/assets/material-theme-lighter-B0m2ddpp.js +1 -0
- package/dist/view/assets/material-theme-ocean-CyktbL80.js +1 -0
- package/dist/view/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
- package/dist/view/assets/matlab-D7o27uSR.js +1 -0
- package/dist/view/assets/mdc-D1_yUvq7.js +1 -0
- package/dist/view/assets/mdx-Cmh6b_Ma.js +1 -0
- package/dist/view/assets/mermaid-CQcHuHx7.js +1 -0
- package/dist/view/assets/min-dark-CafNBF8u.js +1 -0
- package/dist/view/assets/min-light-CTRr51gU.js +1 -0
- package/dist/view/assets/mipsasm-CKIfxQSi.js +1 -0
- package/dist/view/assets/mojo-DJz3ZmWd.js +1 -0
- package/dist/view/assets/monokai-BRVnQi9A.js +1 -0
- package/dist/view/assets/moonbit-CHtswR0a.js +1 -0
- package/dist/view/assets/move-el3G9tDJ.js +1 -0
- package/dist/view/assets/narrat-DRg8JJMk.js +1 -0
- package/dist/view/assets/nextflow-C-mBbutL.js +1 -0
- package/dist/view/assets/nextflow-groovy-vE_lwT2v.js +1 -0
- package/dist/view/assets/nginx-BpAMiNFr.js +1 -0
- package/dist/view/assets/night-owl-C39BiMTA.js +1 -0
- package/dist/view/assets/night-owl-light-CMTm3GFP.js +1 -0
- package/dist/view/assets/nim-BIad80T-.js +1 -0
- package/dist/view/assets/nix-CwoSXNpI.js +1 -0
- package/dist/view/assets/nord-Ddv68eIx.js +1 -0
- package/dist/view/assets/nsis-BlV79W_Q.js +1 -0
- package/dist/view/assets/nushell-D3jzshHO.js +1 -0
- package/dist/view/assets/objective-c-DXmwc3jG.js +1 -0
- package/dist/view/assets/objective-cpp-CLxacb5B.js +1 -0
- package/dist/view/assets/ocaml-C0hk2d4L.js +1 -0
- package/dist/view/assets/odin-BBf5iR-q.js +1 -0
- package/dist/view/assets/one-dark-pro-DVMEJ2y_.js +1 -0
- package/dist/view/assets/one-light-C3Wv6jpd.js +1 -0
- package/dist/view/assets/openscad-C4EeE6gA.js +1 -0
- package/dist/view/assets/org-DM6o9KBp.js +1 -0
- package/dist/view/assets/pascal-D93ZcfNL.js +1 -0
- package/dist/view/assets/perl-B9cMNwum.js +1 -0
- package/dist/view/assets/php-Csjmro_R.js +1 -0
- package/dist/view/assets/pkl-u5AG7uiY.js +1 -0
- package/dist/view/assets/plastic-3e1v2bzS.js +1 -0
- package/dist/view/assets/plsql-ChMvpjG-.js +1 -0
- package/dist/view/assets/po-BTJTHyun.js +1 -0
- package/dist/view/assets/poimandres-CS3Unz2-.js +1 -0
- package/dist/view/assets/polar-C0HS_06l.js +1 -0
- package/dist/view/assets/postcss-CXtECtnM.js +1 -0
- package/dist/view/assets/powerquery-CEu0bR-o.js +1 -0
- package/dist/view/assets/powershell-BmBUJMz7.js +1 -0
- package/dist/view/assets/prisma-Vru482bI.js +1 -0
- package/dist/view/assets/prolog-CbFg5uaA.js +1 -0
- package/dist/view/assets/proto-C7zT0LnQ.js +1 -0
- package/dist/view/assets/pug-DKIMFp6K.js +1 -0
- package/dist/view/assets/puppet-BMWR74SV.js +1 -0
- package/dist/view/assets/purescript-CklMAg4u.js +1 -0
- package/dist/view/assets/python-B6aJPvgy.js +1 -0
- package/dist/view/assets/qml-3beO22l8.js +1 -0
- package/dist/view/assets/qmldir-C8lEn-DE.js +1 -0
- package/dist/view/assets/qss-IeuSbFQv.js +1 -0
- package/dist/view/assets/r-Cf5RLm7j.js +1 -0
- package/dist/view/assets/racket-BqYA7rlc.js +1 -0
- package/dist/view/assets/raku-DXvB9xmW.js +1 -0
- package/dist/view/assets/razor-BjBPvh-w.js +1 -0
- package/dist/view/assets/rbs-CpoqiR4B.js +1 -0
- package/dist/view/assets/red-hvxz__6c.js +1 -0
- package/dist/view/assets/reg-C-SQnVFl.js +1 -0
- package/dist/view/assets/regexp-CDVJQ6XC.js +1 -0
- package/dist/view/assets/rel-C3B-1QV4.js +1 -0
- package/dist/view/assets/riscv-BM1_JUlF.js +1 -0
- package/dist/view/assets/ron-D8l8udqQ.js +1 -0
- package/dist/view/assets/rose-pine-5qJOZa0Y.js +1 -0
- package/dist/view/assets/rose-pine-dawn-zx0QlTCp.js +1 -0
- package/dist/view/assets/rose-pine-moon-C9pEdX9L.js +1 -0
- package/dist/view/assets/rosmsg-BJDFO7_C.js +1 -0
- package/dist/view/assets/rst-bs7f0vWN.js +1 -0
- package/dist/view/assets/ruby-C0TQ7zu5.js +1 -0
- package/dist/view/assets/rust-B1yitclQ.js +1 -0
- package/dist/view/assets/sas-DEy46yEz.js +1 -0
- package/dist/view/assets/sass-Cj5Yp3dK.js +1 -0
- package/dist/view/assets/scala-CqE71os6.js +1 -0
- package/dist/view/assets/scheme-C98Dy4si.js +1 -0
- package/dist/view/assets/scss-D5BDwBP9.js +1 -0
- package/dist/view/assets/sdbl-DVxCFoDh.js +1 -0
- package/dist/view/assets/shaderlab-Dg9Lc6iA.js +1 -0
- package/dist/view/assets/shellscript-Yzrsuije.js +1 -0
- package/dist/view/assets/shellsession-BADoaaVG.js +1 -0
- package/dist/view/assets/slack-dark-BthQWCQV.js +1 -0
- package/dist/view/assets/slack-ochin-DqwNpetd.js +1 -0
- package/dist/view/assets/smalltalk-BOQMe2GC.js +1 -0
- package/dist/view/assets/smithy-cds9vsN8.js +1 -0
- package/dist/view/assets/snazzy-light-Bw305WKR.js +1 -0
- package/dist/view/assets/solarized-dark-CpvCGNkr.js +1 -0
- package/dist/view/assets/solarized-light-Dlz6yCKv.js +1 -0
- package/dist/view/assets/solidity-DijEV5ha.js +1 -0
- package/dist/view/assets/soy-8wufbnw4.js +1 -0
- package/dist/view/assets/sparql-rVzFXLq3.js +1 -0
- package/dist/view/assets/splunk-BtCnVYZw.js +1 -0
- package/dist/view/assets/sql-CRqJ_cUM.js +1 -0
- package/dist/view/assets/ssh-config-_ykCGR6B.js +1 -0
- package/dist/view/assets/stata-DI20mbqo.js +1 -0
- package/dist/view/assets/stylus-BEDo0Tqx.js +1 -0
- package/dist/view/assets/surrealql-Cjom0U5J.js +1 -0
- package/dist/view/assets/svelte-Cy7k_4gC.js +1 -0
- package/dist/view/assets/swift-C2oV4EkX.js +1 -0
- package/dist/view/assets/synthwave-84-CbfX1IO0.js +1 -0
- package/dist/view/assets/system-verilog-0hqHdDBg.js +1 -0
- package/dist/view/assets/systemd-4A_iFExJ.js +1 -0
- package/dist/view/assets/talonscript-CkByrt1z.js +1 -0
- package/dist/view/assets/tasl-QIJgUcNo.js +1 -0
- package/dist/view/assets/tcl-dwOrl1Do.js +1 -0
- package/dist/view/assets/templ-DhtptRzy.js +1 -0
- package/dist/view/assets/terraform-BETggiCN.js +1 -0
- package/dist/view/assets/tex-D96PA37w.js +1 -0
- package/dist/view/assets/tokyo-night-hegEt444.js +1 -0
- package/dist/view/assets/toml-vGWfd6FD.js +1 -0
- package/dist/view/assets/ts-tags-D351s5mN.js +1 -0
- package/dist/view/assets/tsv-B_m7g4N7.js +1 -0
- package/dist/view/assets/tsx-COt5Ahok.js +1 -0
- package/dist/view/assets/turtle-BsS91CYL.js +1 -0
- package/dist/view/assets/twig-27uCiNez.js +1 -0
- package/dist/view/assets/typescript-BPQ3VLAy.js +1 -0
- package/dist/view/assets/typespec-CAFt9gP4.js +1 -0
- package/dist/view/assets/typst-BUadGCkm.js +1 -0
- package/dist/view/assets/v-BGw2Nkan.js +1 -0
- package/dist/view/assets/vala-CsfeWuGM.js +1 -0
- package/dist/view/assets/vb-Cu-pLBUe.js +1 -0
- package/dist/view/assets/verilog-nZwndyjY.js +1 -0
- package/dist/view/assets/vesper-DRje8inN.js +1 -0
- package/dist/view/assets/vhdl-CeAyd5Ju.js +1 -0
- package/dist/view/assets/viml-CJc9bBzg.js +1 -0
- package/dist/view/assets/vitesse-black-Bkuqu6BP.js +1 -0
- package/dist/view/assets/vitesse-dark-D0r3Knsf.js +1 -0
- package/dist/view/assets/vitesse-light-CVO1_9PV.js +1 -0
- package/dist/view/assets/vue-BqiEGhQt.js +1 -0
- package/dist/view/assets/vue-html-AaS7Mt5G.js +1 -0
- package/dist/view/assets/vue-vine-BoDAl6tE.js +1 -0
- package/dist/view/assets/vyper-CDx5xZoG.js +1 -0
- package/dist/view/assets/wasm-CG6Dc4jp.js +1 -0
- package/dist/view/assets/wasm-MzD3tlZU.js +1 -0
- package/dist/view/assets/wenyan-BV7otONQ.js +1 -0
- package/dist/view/assets/wgsl-Dx-B1_4e.js +1 -0
- package/dist/view/assets/wikitext-BhOHFoWU.js +1 -0
- package/dist/view/assets/wit-5i3qLPDT.js +1 -0
- package/dist/view/assets/wolfram-lXgVvXCa.js +1 -0
- package/dist/view/assets/xml-sdJ4AIDG.js +1 -0
- package/dist/view/assets/xsl-CtQFsRM5.js +1 -0
- package/dist/view/assets/yaml-Buea-lGh.js +1 -0
- package/dist/view/assets/zenscript-DVFEvuxE.js +1 -0
- package/dist/view/assets/zig-VOosw3JB.js +1 -0
- package/dist/view/index.html +11 -3
- package/docs/README.md +21 -15
- package/docs/README.zh.md +15 -9
- package/docs/en/authoring.md +85 -80
- package/docs/en/cli-reference.md +110 -72
- package/docs/en/deployment.md +183 -0
- package/docs/en/getting-started.md +112 -0
- package/docs/en/material-types.md +241 -0
- package/docs/en/settings.md +133 -0
- package/docs/en/shortcuts.md +89 -0
- package/docs/en/typing-rules.md +208 -0
- package/docs/zh/authoring.md +74 -69
- package/docs/zh/cli-reference.md +92 -58
- package/docs/zh/deployment.md +179 -0
- package/docs/zh/getting-started.md +104 -0
- package/docs/zh/material-types.md +769 -0
- package/docs/zh/settings.md +120 -0
- package/docs/zh/shortcuts.md +83 -0
- package/docs/zh/typing-rules.md +191 -0
- package/package.json +13 -11
- package/templates/en/QTYPE.md +155 -495
- package/templates/en/README.md +11 -11
- package/templates/en/TODO.md +1 -1
- package/templates/en/material/article.md +20 -0
- package/templates/en/material/assets/article/sunrise.svg +12 -0
- package/templates/en/material/assets/code/tree.svg +19 -0
- package/templates/en/material/assets/qa/login-flow.svg +17 -0
- package/templates/en/material/assets/sentence/pangram.svg +6 -0
- package/templates/en/material/assets/snippet/moon.svg +8 -0
- package/templates/en/material/assets/word/apple.svg +5 -0
- package/templates/en/material/code.md +55 -0
- package/templates/en/material/qa.md +33 -0
- package/templates/en/material/sentence.md +18 -0
- package/templates/en/material/snippet.md +17 -0
- package/templates/en/material/word.md +20 -0
- package/templates/zh/QTYPE.md +191 -473
- package/templates/zh/README.md +6 -6
- package/templates/zh/index.md +1 -1
- package/templates/zh/material/article.md +18 -0
- package/templates/zh/material/assets/article/sunrise.svg +12 -0
- package/templates/zh/material/assets/code/tree.svg +19 -0
- package/templates/zh/material/assets/qa/login-flow.svg +17 -0
- package/templates/zh/material/assets/sentence/pangram.svg +6 -0
- package/templates/zh/material/assets/snippet/moon.svg +8 -0
- package/templates/zh/material/assets/word/apple.svg +5 -0
- package/templates/zh/material/code.md +55 -0
- package/templates/zh/material/qa.md +33 -0
- package/templates/zh/material/sentence.md +19 -0
- package/templates/zh/material/snippet.md +20 -0
- package/templates/zh/material/word.md +20 -0
- package/dist/cli/commands/check/action.js +0 -56
- package/dist/cli/commands/check/i18n.js +0 -26
- package/dist/cli/commands/plugin/i18n.js +0 -28
- package/dist/cli/commands/plugin/index.js +0 -41
- package/dist/core/typing/engine.js +0 -112
- package/dist/view/assets/index-DBABH3dG.js +0 -111
- package/dist/view/assets/index-RTLZ-zOC.css +0 -1
- package/templates/en/material/example.md +0 -47
- 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,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,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
|
-
|
|
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';
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|