@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.
- package/CHANGELOG.md +1 -3
- package/CHANGELOG.zh.md +1 -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 +39 -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-BLryk3W4.css +1 -0
- package/dist/view/assets/index-ByMJp0a5.js +3 -0
- package/dist/view/assets/index-CRh4USfC.js +189 -0
- package/dist/view/assets/index-JNx7Ohoj.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 +10 -2
- 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
|
@@ -6,67 +6,61 @@
|
|
|
6
6
|
* - Each file parses to at least one Q/A card (error)
|
|
7
7
|
* - No card contains characters unmapped in the keystroke plan (error)
|
|
8
8
|
*/
|
|
9
|
-
import fs from 'node:fs';
|
|
10
|
-
import path from 'node:path';
|
|
11
9
|
import { info, success, error, warn } from '../../../utils/output.js';
|
|
12
10
|
import { ensureQtypeRepo } from '../../../utils/repoGuard.js';
|
|
13
11
|
import { t } from '../../../i18n/index.js';
|
|
14
12
|
import { parseMaterial } from '../../../../core/parser/parseMaterial.js';
|
|
13
|
+
import { walkMaterial, readMaterial, MATERIAL_DIR } from '../../../utils/material.js';
|
|
15
14
|
const TITLE_RE = /^title:\s*(.+)$/m;
|
|
16
15
|
const HEADING_RE = /^#\s+.+$/m;
|
|
17
16
|
export async function contentLintAction(opts = {}) {
|
|
18
17
|
const root = ensureQtypeRepo(opts.path ?? process.cwd());
|
|
19
|
-
const materialDir = path.join(root, 'material');
|
|
20
18
|
if (!opts.json)
|
|
21
19
|
info(t('content.lint.msg.scanning', { path: root }));
|
|
22
20
|
const issues = [];
|
|
23
21
|
let cardTotal = 0;
|
|
24
22
|
let materialCount = 0;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
23
|
+
for (const file of walkMaterial(root)) {
|
|
24
|
+
materialCount++;
|
|
25
|
+
const rel = `${MATERIAL_DIR}/${file.rel}`;
|
|
26
|
+
const src = readMaterial(file);
|
|
27
|
+
// File-level title (soft): warn if neither frontmatter title nor
|
|
28
|
+
// a top-level heading is present — the browser view has nothing to
|
|
29
|
+
// show as the material's name.
|
|
30
|
+
const head = src.slice(0, 2048);
|
|
31
|
+
if (!TITLE_RE.test(head) && !HEADING_RE.test(head)) {
|
|
32
|
+
issues.push({
|
|
33
|
+
code: 'material.emptyTitle',
|
|
34
|
+
path: rel,
|
|
35
|
+
message: t('content.lint.issue.emptyTitle', { path: rel }),
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
const material = parseMaterial(src, rel);
|
|
39
|
+
cardTotal += material.cards.length;
|
|
40
|
+
if (material.cards.length === 0) {
|
|
41
|
+
issues.push({
|
|
42
|
+
code: 'material.noCards',
|
|
43
|
+
path: rel,
|
|
44
|
+
message: t('content.lint.issue.noCards', { path: rel }),
|
|
45
|
+
});
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
material.cards.forEach((c, i) => {
|
|
49
|
+
if (c.unknownChars.length > 0) {
|
|
50
|
+
const chars = [...new Set(c.unknownChars)];
|
|
46
51
|
issues.push({
|
|
47
|
-
code: 'material.
|
|
52
|
+
code: 'material.unknownChars',
|
|
48
53
|
path: rel,
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
material.cards.forEach((c, i) => {
|
|
54
|
-
if (c.unknownChars.length > 0) {
|
|
55
|
-
const chars = [...new Set(c.unknownChars)];
|
|
56
|
-
issues.push({
|
|
57
|
-
code: 'material.unknownChars',
|
|
54
|
+
card: i + 1,
|
|
55
|
+
chars,
|
|
56
|
+
message: t('content.lint.issue.unknownChars', {
|
|
58
57
|
path: rel,
|
|
59
58
|
card: i + 1,
|
|
60
|
-
chars,
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}),
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
}
|
|
59
|
+
chars: chars.join(' '),
|
|
60
|
+
}),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
});
|
|
70
64
|
}
|
|
71
65
|
// `emptyTitle` is treated as a warning (informational). Anything else is an error.
|
|
72
66
|
const errorIssues = issues.filter((i) => i.code !== 'material.emptyTitle');
|
|
@@ -1,56 +1,66 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `qtype content search <query>` action — grep-style search over material/*.md.
|
|
3
3
|
*
|
|
4
|
-
* Simple case-insensitive substring match on card questions + answers
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* graph" scope of qtype.
|
|
4
|
+
* Simple case-insensitive substring match on card questions + answers,
|
|
5
|
+
* with optional tag / type / limit filters and a --json output mode for
|
|
6
|
+
* scripting. Not ranked — just returns first-N hits, insertion order.
|
|
8
7
|
*/
|
|
9
|
-
import fs from 'node:fs';
|
|
10
|
-
import path from 'node:path';
|
|
11
8
|
import { info, dim } from '../../../utils/output.js';
|
|
12
9
|
import { ensureQtypeRepo } from '../../../utils/repoGuard.js';
|
|
13
10
|
import { t } from '../../../i18n/index.js';
|
|
14
11
|
import { parseMaterial } from '../../../../core/parser/parseMaterial.js';
|
|
12
|
+
import { walkMaterial, readMaterial, MATERIAL_DIR } from '../../../utils/material.js';
|
|
15
13
|
export async function contentSearchAction(query, opts = {}) {
|
|
16
14
|
const root = ensureQtypeRepo(opts.path ?? process.cwd());
|
|
17
|
-
const materialDir = path.join(root, 'material');
|
|
18
15
|
const limit = opts.limit ?? 20;
|
|
16
|
+
const tagFilter = opts.tag?.map((t) => t.toLowerCase()) ?? [];
|
|
17
|
+
const typeFilter = opts.type?.map((t) => t.toLowerCase()) ?? [];
|
|
19
18
|
const q = query.toLowerCase();
|
|
20
19
|
const hits = [];
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
outer: for (const file of walkMaterial(root)) {
|
|
21
|
+
const rel = `${MATERIAL_DIR}/${file.rel}`;
|
|
22
|
+
const src = readMaterial(file);
|
|
23
|
+
const material = parseMaterial(src, rel);
|
|
24
|
+
const materialType = material.meta.type?.toLowerCase() ?? 'article';
|
|
25
|
+
if (typeFilter.length > 0 && !typeFilter.includes(materialType))
|
|
26
|
+
continue;
|
|
27
|
+
const materialTags = (material.meta.tags ?? []).map((t) => t.toLowerCase());
|
|
28
|
+
if (tagFilter.length > 0 &&
|
|
29
|
+
!tagFilter.every((wanted) => materialTags.includes(wanted))) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
for (let i = 0; i < material.cards.length; i++) {
|
|
33
|
+
const card = material.cards[i];
|
|
34
|
+
const question = card.question ?? card.title;
|
|
35
|
+
const qLc = question.toLowerCase();
|
|
36
|
+
const aLc = card.answer.toLowerCase();
|
|
37
|
+
if (!qLc.includes(q) && !aLc.includes(q))
|
|
24
38
|
continue;
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
file: rel,
|
|
42
|
-
card: i + 1,
|
|
43
|
-
question: card.question,
|
|
44
|
-
answer: card.answer,
|
|
45
|
-
snippet,
|
|
46
|
-
});
|
|
47
|
-
if (hits.length >= limit)
|
|
48
|
-
return;
|
|
39
|
+
const snippetSrc = qLc.includes(q) ? question : card.answer;
|
|
40
|
+
const idx = snippetSrc.toLowerCase().indexOf(q);
|
|
41
|
+
const start = Math.max(0, idx - 20);
|
|
42
|
+
const end = Math.min(snippetSrc.length, idx + q.length + 40);
|
|
43
|
+
const snippet = (start > 0 ? '…' : '') +
|
|
44
|
+
snippetSrc.slice(start, end).replace(/\n+/g, ' ') +
|
|
45
|
+
(end < snippetSrc.length ? '…' : '');
|
|
46
|
+
hits.push({
|
|
47
|
+
file: rel,
|
|
48
|
+
card: i + 1,
|
|
49
|
+
title: card.title,
|
|
50
|
+
question,
|
|
51
|
+
answer: card.answer,
|
|
52
|
+
snippet,
|
|
53
|
+
type: material.meta.type,
|
|
54
|
+
tags: material.meta.tags ?? [],
|
|
49
55
|
});
|
|
50
56
|
if (hits.length >= limit)
|
|
51
57
|
break outer;
|
|
52
58
|
}
|
|
53
59
|
}
|
|
60
|
+
if (opts.json) {
|
|
61
|
+
console.log(JSON.stringify({ query, count: hits.length, hits }, null, 2));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
54
64
|
if (hits.length === 0) {
|
|
55
65
|
info(t('content.search.msg.noResults'));
|
|
56
66
|
return;
|
|
@@ -5,6 +5,9 @@ export const messages = {
|
|
|
5
5
|
'content.search.arg.query': '搜索关键词',
|
|
6
6
|
'content.search.arg.path': '仓库路径(默认当前目录)',
|
|
7
7
|
'content.search.opt.limit': '最多返回结果数(默认 20)',
|
|
8
|
+
'content.search.opt.tag': '按 tag 过滤 material(可多次;需全部匹配)',
|
|
9
|
+
'content.search.opt.type': '按 material 类型过滤(可多次指定)',
|
|
10
|
+
'content.search.opt.json': '以 JSON 输出结果,便于脚本处理',
|
|
8
11
|
'content.search.msg.noResults': '没有找到匹配的结果',
|
|
9
12
|
'content.search.msg.results': '找到 {count} 个结果',
|
|
10
13
|
},
|
|
@@ -14,6 +17,9 @@ export const messages = {
|
|
|
14
17
|
'content.search.arg.query': 'Search query',
|
|
15
18
|
'content.search.arg.path': 'Repo path (default: current directory)',
|
|
16
19
|
'content.search.opt.limit': 'Max results (default: 20)',
|
|
20
|
+
'content.search.opt.tag': 'Filter by tag (repeatable; all must match)',
|
|
21
|
+
'content.search.opt.type': 'Filter by material type (repeatable)',
|
|
22
|
+
'content.search.opt.json': 'Emit results as JSON for scripting',
|
|
17
23
|
'content.search.msg.noResults': 'No results found',
|
|
18
24
|
'content.search.msg.results': 'Found {count} results',
|
|
19
25
|
},
|
|
@@ -17,8 +17,26 @@ export const contentSearchCmd = {
|
|
|
17
17
|
parser: (v) => parseInt(v, 10),
|
|
18
18
|
defaultValue: 20,
|
|
19
19
|
},
|
|
20
|
+
{
|
|
21
|
+
flags: '--tag <tag...>',
|
|
22
|
+
describe: () => t('content.search.opt.tag'),
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
flags: '--type <type...>',
|
|
26
|
+
describe: () => t('content.search.opt.type'),
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
flags: '--json',
|
|
30
|
+
describe: () => t('content.search.opt.json'),
|
|
31
|
+
},
|
|
20
32
|
],
|
|
21
33
|
action: async (query, pathArg, opts) => {
|
|
22
|
-
await contentSearchAction(query, {
|
|
34
|
+
await contentSearchAction(query, {
|
|
35
|
+
path: pathArg,
|
|
36
|
+
limit: opts.limit,
|
|
37
|
+
tag: opts.tag,
|
|
38
|
+
type: opts.type,
|
|
39
|
+
json: opts.json,
|
|
40
|
+
});
|
|
23
41
|
},
|
|
24
42
|
};
|
|
@@ -11,49 +11,47 @@
|
|
|
11
11
|
* - Tag consistency: warns when the same tag is used with different
|
|
12
12
|
* casing (`react` vs `React`)
|
|
13
13
|
*/
|
|
14
|
-
import fs from 'node:fs';
|
|
15
|
-
import path from 'node:path';
|
|
16
14
|
import { info, success, error, warn } from '../../../utils/output.js';
|
|
17
15
|
import { ensureQtypeRepo } from '../../../utils/repoGuard.js';
|
|
18
16
|
import { t } from '../../../i18n/index.js';
|
|
19
17
|
import { parseMaterial } from '../../../../core/parser/parseMaterial.js';
|
|
18
|
+
import { walkMaterial, readMaterial, MATERIAL_DIR } from '../../../utils/material.js';
|
|
20
19
|
export async function contentValidateAction(opts = {}) {
|
|
21
20
|
const root = ensureQtypeRepo(opts.path ?? process.cwd());
|
|
22
|
-
const materialDir = path.join(root, 'material');
|
|
23
21
|
if (!opts.json)
|
|
24
22
|
info(t('content.validate.msg.validating', { path: root }));
|
|
25
23
|
const errors = [];
|
|
26
24
|
const warnings = [];
|
|
27
25
|
let cardTotal = 0;
|
|
28
|
-
const
|
|
26
|
+
const materials = walkMaterial(root);
|
|
29
27
|
// ── Filename uniqueness (case-insensitive) ──────────────
|
|
30
|
-
|
|
28
|
+
// Compares by the full relative path — a file at en/a.md and one
|
|
29
|
+
// at zh/a.md are NOT a collision (different folders), whereas
|
|
30
|
+
// a.md and A.md at the same level are.
|
|
31
|
+
{
|
|
31
32
|
const seen = new Map();
|
|
32
|
-
for (const
|
|
33
|
-
|
|
34
|
-
continue;
|
|
35
|
-
files.push(name);
|
|
36
|
-
const key = name.toLowerCase();
|
|
33
|
+
for (const file of materials) {
|
|
34
|
+
const key = file.rel.toLowerCase();
|
|
37
35
|
const prior = seen.get(key);
|
|
38
|
-
if (prior && prior !==
|
|
36
|
+
if (prior && prior !== file.rel) {
|
|
39
37
|
errors.push({
|
|
40
38
|
code: 'duplicate.file',
|
|
41
|
-
path:
|
|
42
|
-
message: t('content.validate.err.duplicateFile', { file:
|
|
39
|
+
path: file.rel,
|
|
40
|
+
message: t('content.validate.err.duplicateFile', { file: file.rel }),
|
|
43
41
|
extra: { collidesWith: prior },
|
|
44
42
|
});
|
|
45
43
|
}
|
|
46
44
|
else {
|
|
47
|
-
seen.set(key,
|
|
45
|
+
seen.set(key, file.rel);
|
|
48
46
|
}
|
|
49
47
|
}
|
|
50
48
|
}
|
|
51
49
|
// ── Parse every material, then run cross-file checks ────
|
|
52
50
|
const questionIndex = new Map(); // normalized Q → "path#card"
|
|
53
51
|
const tagVariants = new Map(); // lower → { original casings }
|
|
54
|
-
for (const
|
|
55
|
-
const rel =
|
|
56
|
-
const src =
|
|
52
|
+
for (const file of materials) {
|
|
53
|
+
const rel = `${MATERIAL_DIR}/${file.rel}`;
|
|
54
|
+
const src = readMaterial(file);
|
|
57
55
|
const material = parseMaterial(src, rel);
|
|
58
56
|
cardTotal += material.cards.length;
|
|
59
57
|
material.cards.forEach((card, i) => {
|
|
@@ -72,8 +70,9 @@ export async function contentValidateAction(opts = {}) {
|
|
|
72
70
|
extra: { chars },
|
|
73
71
|
});
|
|
74
72
|
}
|
|
75
|
-
// Duplicate card
|
|
76
|
-
|
|
73
|
+
// Duplicate card titles across the corpus (title stands in as the
|
|
74
|
+
// card identity — the H1 text uniquely names each card).
|
|
75
|
+
const key = card.title.trim().toLowerCase();
|
|
77
76
|
if (key.length > 0) {
|
|
78
77
|
const prior = questionIndex.get(key);
|
|
79
78
|
const here = `${rel}#${cardIdx}`;
|
|
@@ -93,14 +92,16 @@ export async function contentValidateAction(opts = {}) {
|
|
|
93
92
|
questionIndex.set(key, here);
|
|
94
93
|
}
|
|
95
94
|
}
|
|
96
|
-
//
|
|
97
|
-
|
|
98
|
-
const lower = tag.toLowerCase();
|
|
99
|
-
const set = tagVariants.get(lower) ?? new Set();
|
|
100
|
-
set.add(tag);
|
|
101
|
-
tagVariants.set(lower, set);
|
|
102
|
-
}
|
|
95
|
+
// Card-level tag consistency was removed with the frontmatter
|
|
96
|
+
// simplification — only material-level tags exist now, handled below.
|
|
103
97
|
});
|
|
98
|
+
// Material-level tag casing consistency.
|
|
99
|
+
for (const tag of material.meta.tags) {
|
|
100
|
+
const lower = tag.toLowerCase();
|
|
101
|
+
const set = tagVariants.get(lower) ?? new Set();
|
|
102
|
+
set.add(tag);
|
|
103
|
+
tagVariants.set(lower, set);
|
|
104
|
+
}
|
|
104
105
|
}
|
|
105
106
|
for (const [lower, variants] of tagVariants) {
|
|
106
107
|
if (variants.size > 1) {
|
|
@@ -116,7 +117,7 @@ export async function contentValidateAction(opts = {}) {
|
|
|
116
117
|
}
|
|
117
118
|
const result = {
|
|
118
119
|
ok: errors.length === 0,
|
|
119
|
-
stats: { materials:
|
|
120
|
+
stats: { materials: materials.length, cards: cardTotal, warnings: warnings.length },
|
|
120
121
|
errors,
|
|
121
122
|
warnings,
|
|
122
123
|
};
|
|
@@ -4,7 +4,6 @@ import { publishCmd } from './publish/index.js';
|
|
|
4
4
|
import { checkCmd } from './check/index.js';
|
|
5
5
|
import { contentCmd } from './content/index.js';
|
|
6
6
|
import { configCmd } from './config/index.js';
|
|
7
|
-
import { pluginCmd } from './plugin/index.js';
|
|
8
7
|
import { packCmd } from './pack/index.js';
|
|
9
8
|
export const allCommands = [
|
|
10
9
|
initCmd,
|
|
@@ -13,7 +12,6 @@ export const allCommands = [
|
|
|
13
12
|
checkCmd,
|
|
14
13
|
contentCmd,
|
|
15
14
|
packCmd,
|
|
16
|
-
pluginCmd,
|
|
17
15
|
configCmd,
|
|
18
16
|
];
|
|
19
|
-
export { initCmd, repoCmd, publishCmd, checkCmd, contentCmd, packCmd,
|
|
17
|
+
export { initCmd, repoCmd, publishCmd, checkCmd, contentCmd, packCmd, configCmd, };
|
|
@@ -9,6 +9,7 @@ import fs from 'node:fs';
|
|
|
9
9
|
import path from 'node:path';
|
|
10
10
|
import { t } from '../../i18n/index.js';
|
|
11
11
|
import { error, info, success } from '../../utils/output.js';
|
|
12
|
+
import { MATERIAL_DIR } from '../../utils/material.js';
|
|
12
13
|
import { messages } from './i18n.js';
|
|
13
14
|
const IGNORE = new Set(['dist', 'node_modules', '.git']);
|
|
14
15
|
export const packCmd = {
|
|
@@ -21,7 +22,7 @@ export const packCmd = {
|
|
|
21
22
|
],
|
|
22
23
|
action: async (opts) => {
|
|
23
24
|
const cwd = process.cwd();
|
|
24
|
-
if (!fs.existsSync(path.join(cwd,
|
|
25
|
+
if (!fs.existsSync(path.join(cwd, MATERIAL_DIR))) {
|
|
25
26
|
error(t('pack.err.noMaterial'));
|
|
26
27
|
process.exitCode = 1;
|
|
27
28
|
return;
|
|
@@ -20,11 +20,13 @@ import { createRepoKv } from '../../../bootstrap.js';
|
|
|
20
20
|
import { deriveAesKey, encryptString, DEFAULT_ARGON2_PARAMS, } from '../../../../core/encrypt.js';
|
|
21
21
|
import { verifyPassword } from '../password/action.js';
|
|
22
22
|
import { prerenderMermaid, emptyMermaidPass } from './mermaidPass.js';
|
|
23
|
+
import { parseMaterialFrontmatter } from '../../../../core/parser/frontmatter.js';
|
|
24
|
+
import { walkMaterial, readMaterial, MATERIAL_DIR } from '../../../utils/material.js';
|
|
23
25
|
export async function publishBuildAction(opts = {}) {
|
|
24
26
|
const cwd = opts.path ? path.resolve(opts.path) : process.cwd();
|
|
25
27
|
const outDir = path.resolve(cwd, opts.out ?? 'dist');
|
|
26
28
|
const viewBundle = resolveBundle('dist/view');
|
|
27
|
-
const materialDir = path.join(cwd,
|
|
29
|
+
const materialDir = path.join(cwd, MATERIAL_DIR);
|
|
28
30
|
info(t('publish.build.msg.parsing', { path: cwd }));
|
|
29
31
|
if (!fs.existsSync(viewBundle) ||
|
|
30
32
|
!fs.existsSync(path.join(viewBundle, 'index.html'))) {
|
|
@@ -41,12 +43,15 @@ export async function publishBuildAction(opts = {}) {
|
|
|
41
43
|
const mermaidMode = await resolveMermaidMode(cwd);
|
|
42
44
|
const repoLang = await resolveRepoLang(cwd);
|
|
43
45
|
const materials = [];
|
|
44
|
-
for (const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
|
|
46
|
+
for (const file of walkMaterial(cwd)) {
|
|
47
|
+
const body = readMaterial(file);
|
|
48
|
+
const filename = file.rel.split('/').pop() ?? file.rel;
|
|
49
|
+
const title = readTitle(body) ?? filename.replace(/\.md$/, '');
|
|
50
|
+
const fm = parseMaterialFrontmatter(body).meta;
|
|
51
|
+
const fallbackType = file.rel.includes('/') ? file.rel.split('/')[0] : '';
|
|
52
|
+
const type = fm.type ?? fallbackType;
|
|
53
|
+
const cardCount = countH1(body);
|
|
54
|
+
materials.push({ name: file.rel, body, title, type, tags: fm.tags, cardCount });
|
|
50
55
|
}
|
|
51
56
|
// Mermaid pre-render — scan all bodies, produce svg + css maps.
|
|
52
57
|
const mermaid = mermaidMode === 'prerender'
|
|
@@ -63,19 +68,29 @@ export async function publishBuildAction(opts = {}) {
|
|
|
63
68
|
// view bundle. Since dev-seed content lives at repo root's examples/
|
|
64
69
|
// (served only by the vite dev middleware, not the bundle), this is a
|
|
65
70
|
// no-op today — kept in case a future build tool ships anything here.
|
|
66
|
-
const targetMaterial = path.join(outDir,
|
|
71
|
+
const targetMaterial = path.join(outDir, MATERIAL_DIR);
|
|
67
72
|
fs.rmSync(targetMaterial, { recursive: true, force: true });
|
|
68
73
|
fs.mkdirSync(targetMaterial, { recursive: true });
|
|
69
74
|
const entries = [];
|
|
70
75
|
for (const m of materials) {
|
|
76
|
+
const outPath = encryptCtx
|
|
77
|
+
? path.join(targetMaterial, `${m.name}.enc.json`)
|
|
78
|
+
: path.join(targetMaterial, m.name);
|
|
79
|
+
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
|
71
80
|
if (encryptCtx) {
|
|
72
81
|
const blob = await encryptString(encryptCtx.key, m.body);
|
|
73
|
-
fs.writeFileSync(
|
|
82
|
+
fs.writeFileSync(outPath, JSON.stringify(blob));
|
|
74
83
|
}
|
|
75
84
|
else {
|
|
76
|
-
fs.writeFileSync(
|
|
85
|
+
fs.writeFileSync(outPath, m.body);
|
|
77
86
|
}
|
|
78
|
-
entries.push({
|
|
87
|
+
entries.push({
|
|
88
|
+
file: m.name,
|
|
89
|
+
title: m.title,
|
|
90
|
+
type: m.type,
|
|
91
|
+
tags: m.tags,
|
|
92
|
+
cardCount: m.cardCount,
|
|
93
|
+
});
|
|
79
94
|
}
|
|
80
95
|
// Mermaid SVG map + CSS: the view fetches these alongside index.json and
|
|
81
96
|
// looks up each fence by its trimmed source when rendering material bodies.
|
|
@@ -196,6 +211,19 @@ function readTitle(source) {
|
|
|
196
211
|
const m = head.match(/^title:\s*(.+)$/m);
|
|
197
212
|
return m ? m[1].trim().replace(/^["']|["']$/g, '') : null;
|
|
198
213
|
}
|
|
214
|
+
/**
|
|
215
|
+
* Cheap card-count estimator — counts top-level `#` H1 lines in the
|
|
216
|
+
* body. Enough for the library's "N cards" chip without full markdown
|
|
217
|
+
* parse. Matches the parser's `splitByH1` semantics.
|
|
218
|
+
*/
|
|
219
|
+
function countH1(source) {
|
|
220
|
+
let n = 0;
|
|
221
|
+
for (const line of source.split('\n')) {
|
|
222
|
+
if (/^#\s+/.test(line))
|
|
223
|
+
n++;
|
|
224
|
+
}
|
|
225
|
+
return n;
|
|
226
|
+
}
|
|
199
227
|
function countCards(materialDir) {
|
|
200
228
|
let n = 0;
|
|
201
229
|
for (const name of fs.readdirSync(materialDir)) {
|
|
@@ -62,11 +62,30 @@ export async function repoNewAction(opts) {
|
|
|
62
62
|
fs.writeFileSync(path.join(repoDir, fileName), readTemplate(templatesDir, fileName, vars));
|
|
63
63
|
console.log(` + ${fileName}`);
|
|
64
64
|
}
|
|
65
|
-
// 4. Seed material
|
|
66
|
-
// but stays trivially prunable if the user wipes the seed.
|
|
65
|
+
// 4. Seed one starter per material type + .gitkeep so the directory
|
|
66
|
+
// exists but stays trivially prunable if the user wipes the seed.
|
|
67
|
+
// Each starter is a valid `qtype check`-clean deck the user can
|
|
68
|
+
// copy, rename, and edit. Their image assets ship alongside so the
|
|
69
|
+
// demos render out of the box.
|
|
67
70
|
fs.mkdirSync(path.join(repoDir, 'material'), { recursive: true });
|
|
68
|
-
|
|
69
|
-
|
|
71
|
+
const starters = [
|
|
72
|
+
['word', 'apple.svg'],
|
|
73
|
+
['sentence', 'pangram.svg'],
|
|
74
|
+
['article', 'sunrise.svg'],
|
|
75
|
+
['snippet', 'moon.svg'],
|
|
76
|
+
['qa', 'login-flow.svg'],
|
|
77
|
+
['code', 'tree.svg'],
|
|
78
|
+
];
|
|
79
|
+
for (const [type, asset] of starters) {
|
|
80
|
+
const mdRel = path.join('material', `${type}.md`);
|
|
81
|
+
fs.writeFileSync(path.join(repoDir, mdRel), readTemplate(templatesDir, mdRel, vars));
|
|
82
|
+
console.log(` + ${mdRel}`);
|
|
83
|
+
const assetRel = path.join('material', 'assets', type, asset);
|
|
84
|
+
const assetDir = path.dirname(path.join(repoDir, assetRel));
|
|
85
|
+
fs.mkdirSync(assetDir, { recursive: true });
|
|
86
|
+
fs.writeFileSync(path.join(repoDir, assetRel), readTemplate(templatesDir, assetRel, vars));
|
|
87
|
+
console.log(` + ${assetRel}`);
|
|
88
|
+
}
|
|
70
89
|
fs.writeFileSync(path.join(repoDir, 'material', '.gitkeep'), '');
|
|
71
90
|
console.log(` + material/.gitkeep`);
|
|
72
91
|
// 5. Write .qtype/config.json with the repo language.
|
|
@@ -16,7 +16,7 @@ export const messages = {
|
|
|
16
16
|
'repo.new.msg.done': '仓库已建好:{path}',
|
|
17
17
|
'repo.new.msg.nextSteps': '下一步:',
|
|
18
18
|
'repo.new.msg.next.cd': 'cd {name}',
|
|
19
|
-
'repo.new.msg.next.edit': '
|
|
19
|
+
'repo.new.msg.next.edit': 'ls material/ # 6 种类型的入门模板,改哪个都行',
|
|
20
20
|
'repo.new.msg.next.commit': 'git add -A && git commit # 首次提交',
|
|
21
21
|
'repo.new.msg.next.gitHint': 'git init # 如需版本控制,也可以 qtype repo new --git',
|
|
22
22
|
'repo.new.tpl.defaultDescription': '资料库',
|
|
@@ -38,7 +38,7 @@ export const messages = {
|
|
|
38
38
|
'repo.new.msg.done': 'Repo created: {path}',
|
|
39
39
|
'repo.new.msg.nextSteps': 'Next steps:',
|
|
40
40
|
'repo.new.msg.next.cd': 'cd {name}',
|
|
41
|
-
'repo.new.msg.next.edit': '
|
|
41
|
+
'repo.new.msg.next.edit': 'ls material/ # 6 starter templates, edit any of them',
|
|
42
42
|
'repo.new.msg.next.commit': 'git add -A && git commit # initial commit',
|
|
43
43
|
'repo.new.msg.next.gitHint': 'git init # or re-run with qtype repo new --git',
|
|
44
44
|
'repo.new.tpl.defaultDescription': 'material library',
|
|
@@ -16,12 +16,32 @@ function buildSyncPlan() {
|
|
|
16
16
|
{ kind: 'file', target: 'TODO.md', source: 'TODO.md', managed: false },
|
|
17
17
|
{ kind: 'file', target: 'index.md', source: 'index.md', managed: false },
|
|
18
18
|
{ kind: 'file', target: 'log.md', source: 'log.md', managed: false },
|
|
19
|
-
|
|
19
|
+
// Starter cards — one per material type. Users copy the one that
|
|
20
|
+
// matches what they're writing, rename it, edit content, and are
|
|
21
|
+
// off to the races. Every file is a valid `qtype check`-clean deck
|
|
22
|
+
// out of the box (the smoke test asserts this).
|
|
23
|
+
...['word', 'sentence', 'article', 'snippet', 'qa', 'code'].map((type) => ({
|
|
20
24
|
kind: 'file',
|
|
21
|
-
target:
|
|
22
|
-
source: path.join('material',
|
|
25
|
+
target: `material/${type}.md`,
|
|
26
|
+
source: path.join('material', `${type}.md`),
|
|
23
27
|
managed: false,
|
|
24
|
-
},
|
|
28
|
+
})),
|
|
29
|
+
// Starter card image assets — SVGs referenced by the starters.
|
|
30
|
+
// `currentColor`-stroked so they inherit the site's text color and
|
|
31
|
+
// adapt to dark/light theme without extra work.
|
|
32
|
+
...[
|
|
33
|
+
['word', 'apple.svg'],
|
|
34
|
+
['sentence', 'pangram.svg'],
|
|
35
|
+
['article', 'sunrise.svg'],
|
|
36
|
+
['snippet', 'moon.svg'],
|
|
37
|
+
['qa', 'login-flow.svg'],
|
|
38
|
+
['code', 'tree.svg'],
|
|
39
|
+
].map(([type, name]) => ({
|
|
40
|
+
kind: 'file',
|
|
41
|
+
target: `material/assets/${type}/${name}`,
|
|
42
|
+
source: path.join('material', 'assets', type, name),
|
|
43
|
+
managed: false,
|
|
44
|
+
})),
|
|
25
45
|
// ── Skeleton (create-only, never touched) ───────
|
|
26
46
|
{ kind: 'dir', target: '.qtype', managed: false },
|
|
27
47
|
{ kind: 'dir', target: 'material', managed: false },
|
|
@@ -9,7 +9,8 @@ import { fileURLToPath } from 'node:url';
|
|
|
9
9
|
* ├── en/
|
|
10
10
|
* │ ├── QTYPE.md README.md TODO.md index.md log.md
|
|
11
11
|
* │ └── material/
|
|
12
|
-
* │
|
|
12
|
+
* │ ├── word.md article.md qa.md
|
|
13
|
+
* │ └── sentence.md snippet.md code.md
|
|
13
14
|
* └── zh/
|
|
14
15
|
* └── (same files)
|
|
15
16
|
*
|