@qnroa/qtype 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/CHANGELOG.zh.md +82 -0
  3. package/LICENSE +201 -0
  4. package/README.md +113 -0
  5. package/README.zh.md +114 -0
  6. package/dist/cli/bootstrap.js +190 -0
  7. package/dist/cli/commands/check/action.js +56 -0
  8. package/dist/cli/commands/check/i18n.js +26 -0
  9. package/dist/cli/commands/check/index.js +20 -0
  10. package/dist/cli/commands/config/action.js +236 -0
  11. package/dist/cli/commands/config/i18n.js +64 -0
  12. package/dist/cli/commands/config/index.js +72 -0
  13. package/dist/cli/commands/content/index.js +22 -0
  14. package/dist/cli/commands/content/lint/action.js +102 -0
  15. package/dist/cli/commands/content/lint/i18n.js +32 -0
  16. package/dist/cli/commands/content/lint/index.js +19 -0
  17. package/dist/cli/commands/content/search/action.js +64 -0
  18. package/dist/cli/commands/content/search/i18n.js +20 -0
  19. package/dist/cli/commands/content/search/index.js +24 -0
  20. package/dist/cli/commands/content/validate/action.js +145 -0
  21. package/dist/cli/commands/content/validate/i18n.js +32 -0
  22. package/dist/cli/commands/content/validate/index.js +19 -0
  23. package/dist/cli/commands/index.js +19 -0
  24. package/dist/cli/commands/init/index.js +53 -0
  25. package/dist/cli/commands/pack/i18n.js +20 -0
  26. package/dist/cli/commands/pack/index.js +48 -0
  27. package/dist/cli/commands/plugin/i18n.js +28 -0
  28. package/dist/cli/commands/plugin/index.js +41 -0
  29. package/dist/cli/commands/publish/build/action.js +208 -0
  30. package/dist/cli/commands/publish/build/i18n.js +50 -0
  31. package/dist/cli/commands/publish/build/index.js +24 -0
  32. package/dist/cli/commands/publish/build/mermaidPass.js +93 -0
  33. package/dist/cli/commands/publish/build/mermaidPrerender.js +199 -0
  34. package/dist/cli/commands/publish/index.js +21 -0
  35. package/dist/cli/commands/publish/init/action.js +74 -0
  36. package/dist/cli/commands/publish/init/i18n.js +22 -0
  37. package/dist/cli/commands/publish/init/index.js +26 -0
  38. package/dist/cli/commands/publish/password/action.js +158 -0
  39. package/dist/cli/commands/publish/password/i18n.js +44 -0
  40. package/dist/cli/commands/publish/password/index.js +35 -0
  41. package/dist/cli/commands/publish/serve/action.js +117 -0
  42. package/dist/cli/commands/publish/serve/i18n.js +32 -0
  43. package/dist/cli/commands/publish/serve/index.js +37 -0
  44. package/dist/cli/commands/repo/index.js +19 -0
  45. package/dist/cli/commands/repo/new/action.js +98 -0
  46. package/dist/cli/commands/repo/new/i18n.js +46 -0
  47. package/dist/cli/commands/repo/new/index.js +57 -0
  48. package/dist/cli/commands/repo/sync/action.js +202 -0
  49. package/dist/cli/commands/repo/sync/i18n.js +44 -0
  50. package/dist/cli/commands/repo/sync/index.js +47 -0
  51. package/dist/cli/commands/repo/templates.js +87 -0
  52. package/dist/cli/i18n/index.js +141 -0
  53. package/dist/cli/i18n/shared.js +142 -0
  54. package/dist/cli/index.js +79 -0
  55. package/dist/cli/registry/command.js +1 -0
  56. package/dist/cli/registry/commander-i18n.js +76 -0
  57. package/dist/cli/registry/help.js +142 -0
  58. package/dist/cli/registry/mount.js +71 -0
  59. package/dist/cli/utils/output.js +92 -0
  60. package/dist/cli/utils/paths.js +13 -0
  61. package/dist/cli/utils/prompt.js +72 -0
  62. package/dist/cli/utils/repoGuard.js +41 -0
  63. package/dist/core/encrypt.js +113 -0
  64. package/dist/core/index.js +8 -0
  65. package/dist/core/keystroke/asciiMap.js +39 -0
  66. package/dist/core/keystroke/chineseMap.js +32 -0
  67. package/dist/core/keystroke/fullwidthMap.js +41 -0
  68. package/dist/core/keystroke/index.js +22 -0
  69. package/dist/core/keystroke/textNormalize.js +68 -0
  70. package/dist/core/keystroke/types.js +1 -0
  71. package/dist/core/parser/frontmatter.js +58 -0
  72. package/dist/core/parser/index.js +2 -0
  73. package/dist/core/parser/parseMaterial.js +42 -0
  74. package/dist/core/types.js +1 -0
  75. package/dist/core/typing/engine.js +112 -0
  76. package/dist/core/typing/index.js +2 -0
  77. package/dist/core/typing/metrics.js +20 -0
  78. package/dist/store/index.js +44 -0
  79. package/dist/store/kv.js +276 -0
  80. package/dist/store/storage/browser.js +61 -0
  81. package/dist/store/storage/json.js +113 -0
  82. package/dist/store/storage/memory.js +24 -0
  83. package/dist/store/types.js +47 -0
  84. package/dist/store/validators.js +71 -0
  85. package/dist/view/assets/index-DBABH3dG.js +111 -0
  86. package/dist/view/assets/index-RTLZ-zOC.css +1 -0
  87. package/dist/view/index.html +14 -0
  88. package/dist/view/qtype-logo.svg +49 -0
  89. package/dist/view/qtype-mark.svg +21 -0
  90. package/docs/README.md +23 -0
  91. package/docs/README.zh.md +20 -0
  92. package/docs/en/authoring.md +138 -0
  93. package/docs/en/cli-reference.md +171 -0
  94. package/docs/zh/authoring.md +131 -0
  95. package/docs/zh/cli-reference.md +162 -0
  96. package/package.json +85 -0
  97. package/templates/en/QTYPE.md +618 -0
  98. package/templates/en/README.md +64 -0
  99. package/templates/en/TODO.md +7 -0
  100. package/templates/en/index.md +7 -0
  101. package/templates/en/log.md +3 -0
  102. package/templates/en/material/.gitkeep +0 -0
  103. package/templates/en/material/example.md +47 -0
  104. package/templates/zh/QTYPE.md +618 -0
  105. package/templates/zh/README.md +64 -0
  106. package/templates/zh/TODO.md +7 -0
  107. package/templates/zh/index.md +7 -0
  108. package/templates/zh/log.md +3 -0
  109. package/templates/zh/material/.gitkeep +0 -0
  110. package/templates/zh/material/example.md +47 -0
@@ -0,0 +1 @@
1
+ .qt-qi{position:relative;width:100%}.qt-qi__input{width:100%;height:var(--search-h);padding:0 var(--space-3);padding-right:2.4rem;border-radius:var(--radius);background:var(--qt-base);color:var(--qt-text);border:1px solid var(--qt-border);outline:none;font-family:inherit;font-size:var(--fs-ui);transition:border-color .12s ease}.qt-qi__input::placeholder{color:var(--qt-text-muted)}.qt-qi__input:focus{border-color:var(--qt-accent)}.qt-qi__kbd{position:absolute;right:8px;top:50%;transform:translateY(-50%);padding:.1em .5em;font-size:.72em;border-radius:3px;color:var(--qt-text-muted);background:var(--qt-mantle);border:1px solid var(--qt-border);font-family:var(--font-mono);pointer-events:none}.qt-qi__panel{position:absolute;top:calc(100% + 4px);left:0;right:0;max-height:60vh;overflow-y:auto;background:var(--qt-mantle);border:1px solid var(--qt-border);border-radius:var(--radius);box-shadow:0 12px 32px #00000052;padding:var(--space-1);z-index:40}.qt-qi__row{width:100%;display:flex;align-items:center;gap:10px;padding:8px 12px;min-height:var(--control-h);background:transparent;border:none;border-left:2px solid transparent;border-radius:4px;color:var(--qt-text);text-align:left;font-family:inherit;font-size:var(--fs-ui);cursor:pointer;transition:background .12s ease}.qt-qi__row.is-focused{background:var(--qt-surface-hover)}.qt-qi__row.is-active{border-left-color:var(--qt-accent);font-weight:600}.qt-qi__row.is-disabled{opacity:.5;cursor:not-allowed}.qt-qi__body{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.qt-qi__label{color:var(--qt-text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.qt-qi__detail{color:var(--qt-text-muted);font-size:var(--fs-caption);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.qt-qi__desc{color:var(--qt-text-muted);font-family:var(--font-mono);font-size:var(--fs-caption);flex-shrink:0}.qt-header{height:var(--header-h);display:grid;align-items:center;padding:0 max(var(--gutter),env(safe-area-inset-left));background:var(--qt-mantle);border-bottom:1px solid var(--qt-border);color:var(--qt-text);position:sticky;top:0;z-index:30}.qt-header--ml{grid-template-columns:minmax(0,1fr) minmax(240px,2fr) minmax(0,1fr);gap:12px}.qt-header--s{grid-template-columns:auto minmax(0,1fr) auto;gap:8px}.qt-brand{display:flex;align-items:center;gap:var(--space-2);background:transparent;border:none;padding:0;cursor:pointer;color:inherit;font-family:inherit;transition:color .12s ease}.qt-brand:hover .qt-brand__name{color:var(--qt-accent)}.qt-brand:focus-visible{outline:none}.qt-brand:focus-visible .qt-brand__name{color:var(--qt-accent)}.qt-brand__logo{display:block;width:28px;height:28px;flex-shrink:0}.qt-brand__name{font-size:var(--fs-title);font-weight:600;color:var(--qt-text);letter-spacing:.01em}.qt-header--s .qt-brand__name{display:none}.qt-header-search{width:100%}.qt-header-right{display:flex;align-items:center;gap:var(--space-2);justify-self:end}.qt-header-count{color:var(--qt-text-muted);font-family:var(--font-mono);font-size:var(--fs-caption)}.qt-header-stats{display:flex;align-items:center;gap:10px;justify-self:end}.qt-header-count-pct{color:var(--qt-green)}.qt-header-pct{color:var(--qt-green);font-family:var(--font-mono);font-size:var(--fs-caption);font-weight:600;min-width:2.8em;text-align:right}.qt-header-bar{width:90px;height:5px;background:var(--qt-border);border-radius:3px;overflow:hidden;flex-shrink:0}.qt-header-bar__fill{height:100%;width:var(--qt-progress, 0%);background:var(--qt-green);transition:width .22s ease}.qt-header{position:sticky;top:0;z-index:30}.qt-footer{position:relative;height:26px;display:flex;align-items:center;gap:var(--space-3);padding:0 max(var(--gutter),env(safe-area-inset-left));background:var(--qt-mantle);border-top:1px solid var(--qt-border);color:var(--qt-text-muted);font-size:var(--fs-caption);font-family:var(--font-ui)}.qt-footer__left{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.qt-footer__center{position:absolute;left:50%;transform:translate(-50%);display:flex;align-items:center;color:var(--qt-text-muted);font-family:var(--font-mono)}.qt-footer__right{display:flex;align-items:center;gap:4px;flex-shrink:0}.qt-footer__stat{width:22px;height:22px;padding:0;background:transparent;border:none;color:var(--qt-text-muted);font-size:.85rem;border-radius:4px;cursor:pointer;transition:background .12s ease,color .12s ease;display:inline-flex;align-items:center;justify-content:center}.qt-footer__icon{display:block;vertical-align:middle}.qt-footer__stat:hover:not(:disabled){background:color-mix(in srgb,var(--qt-text) 8%,transparent);color:var(--qt-text)}.qt-footer__stat:disabled{opacity:.4;cursor:not-allowed}.qt-footer__stat.is-off{opacity:.55}.qt-toolbar{display:flex;align-items:flex-start;gap:var(--space-3);padding:0;margin-bottom:var(--space-3);position:relative;z-index:15}.qt-toolbar--selection{align-items:center;min-height:var(--control-h);gap:6px}.qt-selbar__count{flex:0 0 auto;color:var(--qt-text-muted);font-family:var(--font-mono);font-size:var(--fs-caption);padding:0 var(--space-2)}.qt-selbar__count b{color:var(--qt-accent);font-weight:600}.qt-selbar__spacer{flex:1}.qt-selbar__btn{background:transparent;border:1px solid var(--qt-border);color:var(--qt-text-muted);padding:0 12px;height:var(--control-h);border-radius:var(--radius);font-family:inherit;font-size:var(--fs-ui);cursor:pointer;transition:color .12s ease,border-color .12s ease,background .12s ease}.qt-selbar__btn:hover:not(:disabled){color:var(--qt-text);border-color:color-mix(in srgb,var(--qt-accent) 40%,var(--qt-border))}.qt-selbar__btn--danger:hover:not(:disabled){color:var(--qt-red);border-color:color-mix(in srgb,var(--qt-red) 50%,var(--qt-border));background:color-mix(in srgb,var(--qt-red) 10%,transparent)}.qt-selbar__btn:disabled{opacity:.4;cursor:not-allowed}.qt-right-stack{flex-shrink:0;display:flex;align-items:center;gap:6px;height:var(--control-h)}.qt-select-btn{display:inline-flex;align-items:center;justify-content:center;width:var(--icon-btn);height:var(--icon-btn);padding:0;background:transparent;border:1px solid var(--qt-border);border-radius:var(--radius);color:var(--qt-text-muted);cursor:pointer;transition:color .12s ease,border-color .12s ease,background .12s ease}.qt-select-btn:hover{color:var(--qt-accent);border-color:var(--qt-accent);background:var(--qt-surface-hover)}.qt-select-btn.is-active{color:var(--qt-accent);border-color:var(--qt-accent);background:color-mix(in srgb,var(--qt-accent) 12%,transparent)}.qt-chip-hidden-badge{display:inline-flex;align-items:center;height:var(--control-h);padding:0 8px;border-radius:9999px;background:color-mix(in srgb,var(--qt-accent) 18%,transparent);color:var(--qt-accent);font-family:var(--font-mono);font-size:var(--fs-caption);line-height:1;border:1px solid transparent;flex-shrink:0;-webkit-user-select:none;user-select:none}.qt-tag-clickcatcher{position:fixed;top:0;right:0;bottom:0;left:0;background:transparent;z-index:10}.qt-chips{flex:1;min-width:0;position:relative;transition:background .16s ease-out,box-shadow .16s ease-out,padding .16s ease-out,border-color .16s ease-out;border-radius:var(--radius);border:1px solid transparent;overflow:hidden}.qt-chips.is-expanded{z-index:20;overflow:visible}.qt-chips__row{display:flex;align-items:center;gap:6px;flex-wrap:nowrap;transition:flex-wrap 0s}.qt-chips__row.is-expanded{flex-wrap:wrap}.qt-chips__measure{position:absolute;visibility:hidden;pointer-events:none;top:0;left:0;width:max-content;max-width:none;display:flex;gap:6px;white-space:nowrap}.qt-chip{background:transparent;border:1px solid var(--qt-border);color:var(--qt-text-muted);padding:0 12px;height:var(--control-h);border-radius:9999px;font-family:inherit;font-size:var(--fs-ui);line-height:1;white-space:nowrap;cursor:pointer;transition:background .12s ease,color .12s ease,border-color .12s ease;display:inline-flex;align-items:center;gap:4px}.qt-chip__icon{flex-shrink:0;display:block}.qt-chip:hover{color:var(--qt-text);border-color:color-mix(in srgb,var(--qt-accent) 40%,var(--qt-border))}.qt-chip.is-active{background:color-mix(in srgb,var(--qt-accent) 18%,transparent);color:var(--qt-accent);border-color:var(--qt-accent)}.qt-chip--more{color:var(--qt-text-muted)}.qt-chip--more.is-open{color:var(--qt-text);border-color:var(--qt-accent);background:var(--qt-surface-hover)}.qt-chip--more.has-selected{color:var(--qt-accent);border-color:var(--qt-accent)}.qt-chip-more__badge{display:inline-flex;align-items:center;justify-content:center;min-width:16px;height:16px;padding:0 5px;border-radius:8px;background:var(--qt-accent);color:#fff;font-size:10px;font-family:var(--font-mono)}.qt-chip--reset{color:var(--qt-text-muted);padding:0 10px;gap:6px}.qt-chip--reset:hover{color:var(--qt-red);border-color:color-mix(in srgb,var(--qt-red) 50%,var(--qt-border));background:color-mix(in srgb,var(--qt-red) 10%,transparent)}.qt-viewtoggle{display:inline-flex;border:1px solid var(--qt-border);border-radius:var(--radius);overflow:hidden;flex-shrink:0;height:var(--control-h);align-self:center}.qt-viewtoggle button{display:inline-flex;align-items:center;justify-content:center;width:var(--icon-btn);height:100%;border:none;background:transparent;color:var(--qt-text-muted);padding:0;transition:background .12s ease,color .12s ease}.qt-viewtoggle button:hover{background:var(--qt-surface-hover);color:var(--qt-text)}.qt-viewtoggle button.is-active{background:var(--qt-surface-hover);color:var(--qt-accent)}.qt-md{color:var(--qt-text);line-height:1.75;font-size:var(--fs-body)}.qt-md h1,.qt-md h2,.qt-md h3,.qt-md h4{margin:0 0 var(--space-3);color:var(--qt-text);font-weight:600}.qt-md h1{font-size:var(--fs-display)}.qt-md h2{font-size:1.05rem}.qt-md h3{font-size:var(--fs-body)}.qt-md p{margin:0 0 var(--space-3)}.qt-md p:last-child{margin-bottom:0}.qt-md code{background:var(--qt-base);padding:0 3px;border-radius:3px}.qt-md pre{margin:var(--space-3) 0}.qt-md pre code{background:transparent;padding:0}.qt-md strong{color:var(--qt-text);font-weight:600}.qt-md-mermaid{margin:var(--space-3) 0;overflow-x:auto}.qt-md-mermaid__dark,.qt-md-mermaid__light{display:none}html[data-theme=mocha] .qt-md-mermaid__dark,html[data-theme=dark] .qt-md-mermaid__dark,:root:not([data-theme=latte]):not([data-theme=light]) .qt-md-mermaid__dark{display:block}html[data-theme=latte] .qt-md-mermaid__light,html[data-theme=light] .qt-md-mermaid__light{display:block}.qt-md-mermaid svg{max-width:100%;height:auto}.qt-deckcard{display:flex;flex-direction:column;padding:0;background:var(--qt-mantle);border:1px solid var(--qt-border);border-radius:12px;color:var(--qt-text);text-align:left;cursor:pointer;transition:border-color .12s ease,transform .12s ease,opacity .12s ease,background .12s ease;min-width:0;min-height:16rem;overflow:hidden;position:relative;word-break:break-word}.qt-deckcard.is-selected{border-color:var(--qt-accent);background:color-mix(in srgb,var(--qt-accent) 8%,var(--qt-mantle))}.qt-deckcard__checkbox{position:absolute;top:10px;left:10px;width:18px;height:18px;border-radius:4px;border:1.5px solid var(--qt-border);background:var(--qt-mantle);color:#fff;font-size:11px;font-weight:700;line-height:1;padding:0;cursor:pointer;display:none;align-items:center;justify-content:center;flex-shrink:0;z-index:3;transition:border-color .12s ease,background .12s ease}.qt-deckcard.is-selection-mode .qt-deckcard__checkbox{display:inline-flex}.qt-deckcard__checkbox:hover{border-color:var(--qt-accent)}.qt-deckcard__checkbox.is-checked{background:var(--qt-accent);border-color:var(--qt-accent)}.qt-deckcard__stamp{position:absolute;right:12px;bottom:8px;padding:3px 10px;border:2px solid var(--qt-green);border-radius:4px;color:var(--qt-green);font-family:var(--font-mono);font-size:10px;font-weight:700;letter-spacing:.15em;transform:rotate(-8deg);background:color-mix(in srgb,var(--qt-green) 8%,transparent);pointer-events:none;-webkit-user-select:none;user-select:none;line-height:1.2;z-index:3}.qt-deckcard:hover{border-color:var(--qt-accent);transform:translateY(-1px)}.qt-deckcard:focus-visible{outline:none;border-color:var(--qt-accent)}.qt-deckcard__done-icon{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;border-radius:50%;background:color-mix(in srgb,var(--qt-green) 22%,transparent);color:var(--qt-green);font-size:11px;font-weight:700;line-height:1}.qt-deckcard__cover{position:relative;flex:1;display:flex;flex-direction:column;background:linear-gradient(135deg,color-mix(in srgb,var(--qt-accent) 24%,var(--qt-mantle)) 0%,color-mix(in srgb,var(--qt-accent) 8%,var(--qt-mantle)) 50%,var(--qt-mantle) 100%);border-bottom:1px solid var(--qt-border);overflow:hidden}.qt-deckcard__topbar{display:flex;justify-content:space-between;align-items:flex-start;padding:var(--space-3) var(--space-3);padding-bottom:0;padding-left:36px;z-index:1}.qt-deckcard__tags{display:flex;gap:6px;flex-wrap:wrap}.qt-deckcard__tag{background:var(--qt-base);color:var(--qt-text-muted);padding:3px 10px;border-radius:9999px;font-family:var(--font-mono);font-size:var(--fs-caption);line-height:1;border:1px solid var(--qt-border);max-width:12ch;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.qt-deckcard__tag--more{cursor:help;color:var(--qt-text-muted);opacity:.8}.qt-deckcard__topright{display:flex;align-items:center;gap:4px}.qt-deckcard__check{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;border-radius:50%;background:color-mix(in srgb,var(--qt-green) 22%,transparent);color:var(--qt-green);font-size:11px;font-weight:700}.qt-deckcard__menuwrap{position:relative}.qt-deckcard__menu-btn{background:transparent;border:none;color:var(--qt-text-muted);width:24px;height:24px;border-radius:4px;font-size:16px;line-height:1;padding:0;cursor:pointer;transition:background .12s ease,color .12s ease}.qt-deckcard__menu-btn:hover{background:var(--qt-surface-hover);color:var(--qt-text)}.qt-deckcard__menu{position:absolute;top:calc(100% + 4px);right:0;min-width:140px;background:var(--qt-mantle);border:1px solid var(--qt-border);border-radius:var(--radius);box-shadow:0 12px 32px #00000052;z-index:20;padding:4px}.qt-deckcard__menu-opt{width:100%;background:transparent;border:none;color:var(--qt-text);padding:6px 10px;border-radius:3px;text-align:left;font-family:inherit;font-size:var(--fs-ui);cursor:pointer;transition:background .12s ease,color .12s ease}.qt-deckcard__menu-opt:hover:not(:disabled){background:var(--qt-surface-hover)}.qt-deckcard__menu-opt:disabled{color:var(--qt-text-muted);cursor:not-allowed;opacity:.5}.qt-deckcard__center{position:relative;flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;padding:var(--space-4) var(--space-4);text-align:center}.qt-deckcard__title{font-size:1.5rem;font-weight:600;color:var(--qt-text);line-height:1.3;letter-spacing:.01em;min-width:0;max-width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.qt-deckcard__count{color:var(--qt-text-muted);font-family:var(--font-mono);font-size:var(--fs-caption)}.qt-deckcard__cover-foot{display:flex;justify-content:space-between;align-items:flex-end;padding:var(--space-3) var(--space-3);padding-top:0;min-height:24px}.qt-deckcard__cover-foot .qt-deckcard__count{padding-left:4px}.qt-deckcard__peak{color:var(--qt-accent);font-family:var(--font-mono);font-size:var(--fs-caption);padding-right:4px}.qt-deckcard__bottom{padding:var(--space-3) var(--space-3);background:var(--qt-mantle);display:flex;flex-direction:column;gap:6px;flex-shrink:0}.qt-deckcard__preview{color:var(--qt-text-muted);font-size:var(--fs-caption);line-height:1.4;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;text-overflow:ellipsis;height:2.8em;word-break:break-word}.qt-deckcard__meta{display:flex;align-items:center;gap:var(--space-2)}.qt-deckcard__status{padding:2px 8px;border-radius:9999px;font-size:var(--fs-caption);font-family:var(--font-mono);color:var(--qt-text-muted);background:var(--qt-base);border:1px solid var(--qt-border)}.qt-deckcard__status.is-done{color:var(--qt-green);background:color-mix(in srgb,var(--qt-green) 12%,transparent);border-color:color-mix(in srgb,var(--qt-green) 40%,var(--qt-border))}.qt-deckcard.is-done{cursor:not-allowed}.qt-deckcard.is-done:hover{transform:none;border-color:var(--qt-border)}.qt-deckcard.is-done .qt-deckcard__topbar,.qt-deckcard.is-done .qt-deckcard__center,.qt-deckcard.is-done .qt-deckcard__cover-foot,.qt-deckcard.is-done .qt-deckcard__bottom{filter:grayscale(1);opacity:.55}.qt-deckcard.is-done .qt-deckcard__cover{background:var(--qt-mantle)}.qt-deckrow{display:flex;align-items:center;gap:14px;padding:10px 8px;background:transparent;border:1px solid transparent;border-radius:8px;color:var(--qt-text);text-align:left;font-family:inherit;font-size:var(--fs-body);transition:background .12s ease,border-color .12s ease,opacity .12s ease;width:100%;min-width:0;min-height:92px;cursor:pointer;outline:none;overflow:hidden;word-break:break-word}.qt-deckrow>*{min-width:0}.qt-deckrow:hover,.qt-deckrow:focus-visible{background:var(--qt-surface-hover);border-color:var(--qt-border)}.qt-deckrow.is-selected{background:color-mix(in srgb,var(--qt-accent) 10%,transparent);border-color:var(--qt-accent)}.qt-deckrow__checkbox{width:16px;height:16px;border-radius:3px;border:1.5px solid var(--qt-border);background:transparent;color:#fff;font-size:10px;font-weight:700;line-height:1;padding:0;cursor:pointer;display:none;align-items:center;justify-content:center;flex-shrink:0;transition:border-color .12s ease,background .12s ease}.qt-deckrow.is-selection-mode .qt-deckrow__checkbox{display:inline-flex}.qt-deckrow__checkbox:hover{border-color:var(--qt-accent)}.qt-deckrow__checkbox.is-checked{background:var(--qt-accent);border-color:var(--qt-accent)}.qt-deckrow__thumb{flex-shrink:0;width:64px;height:64px;border-radius:10px;background:linear-gradient(135deg,color-mix(in srgb,var(--qt-accent) 22%,var(--qt-mantle)),var(--qt-mantle));border:1px solid var(--qt-border);display:flex;align-items:center;justify-content:center;color:var(--qt-text);font-family:var(--font-mono);font-size:15px;font-weight:600;letter-spacing:.02em;-webkit-user-select:none;user-select:none}.qt-deckrow__body{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}.qt-deckrow__line1{display:flex;align-items:center;gap:10px;min-width:0}.qt-deckrow__title{font-weight:600;font-size:var(--fs-body);color:var(--qt-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;flex:0 1 auto}.qt-deckrow__tags{position:relative;display:flex;gap:4px;overflow:hidden;flex-wrap:nowrap;min-width:0;flex:1 1 auto}.qt-deckrow__tags-measure{position:absolute;visibility:hidden;pointer-events:none;top:0;left:0;display:flex;gap:4px;white-space:nowrap}.qt-deckrow__tag{background:color-mix(in srgb,var(--qt-accent) 14%,transparent);color:var(--qt-accent);padding:1px 8px;border-radius:3px;font-size:var(--fs-caption);font-family:var(--font-mono);line-height:1.4;white-space:nowrap;flex-shrink:0}.qt-deckrow__tag--more{background:transparent;color:var(--qt-text-muted);border:1px solid var(--qt-border);cursor:help}.qt-deckrow__preview{color:var(--qt-text-muted);font-size:var(--fs-caption);line-height:1.4;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.qt-deckrow__line2{display:flex;align-items:center;gap:6px;color:var(--qt-text-muted);font-family:var(--font-mono);font-size:var(--fs-caption);overflow:hidden;white-space:nowrap;min-width:0}.qt-deckrow__line2>span:first-child{overflow:hidden;text-overflow:ellipsis;min-width:0}.qt-deckrow__sep{opacity:.5;flex-shrink:0}.qt-deckrow__stamp{padding:3px 10px;border:2px solid var(--qt-green);border-radius:4px;color:var(--qt-green);font-family:var(--font-mono);font-size:10px;font-weight:700;letter-spacing:.15em;transform:rotate(-6deg);opacity:.85;background:color-mix(in srgb,var(--qt-green) 6%,transparent);pointer-events:none;-webkit-user-select:none;user-select:none;line-height:1.2;flex-shrink:0;align-self:center}.qt-deckrow__menuwrap{position:relative;flex-shrink:0}.qt-deckrow__menu-btn{background:transparent;border:none;color:var(--qt-text-muted);width:26px;height:26px;border-radius:4px;font-size:16px;line-height:1;padding:0;cursor:pointer;transition:background .12s ease,color .12s ease}.qt-deckrow__menu-btn:hover{background:var(--qt-surface-hover);color:var(--qt-text)}.qt-deckrow__menu{position:absolute;top:calc(100% + 4px);right:0;min-width:140px;background:var(--qt-mantle);border:1px solid var(--qt-border);border-radius:var(--radius);box-shadow:0 12px 32px #00000052;z-index:20;padding:4px}.qt-deckrow__menu-opt{width:100%;background:transparent;border:none;color:var(--qt-text);padding:6px 10px;border-radius:3px;text-align:left;font-family:inherit;font-size:var(--fs-ui);cursor:pointer;transition:background .12s ease}.qt-deckrow__menu-opt:hover:not(:disabled){background:var(--qt-surface-hover)}.qt-deckrow__menu-opt:disabled{color:var(--qt-text-muted);cursor:not-allowed;opacity:.5}.qt-deckrow.is-done{cursor:not-allowed}.qt-deckrow.is-done:hover{background:transparent;border-color:transparent}.qt-deckrow.is-done .qt-deckrow__thumb,.qt-deckrow.is-done .qt-deckrow__body{filter:grayscale(1);opacity:.5}.qt-deckrow.is-done .qt-deckrow__stamp{filter:none;opacity:.85}.qt-deckrow__peak{color:var(--qt-accent)}.qt-pw{flex:1;min-height:100vh;display:flex;align-items:center;justify-content:center;padding:var(--space-5)}.qt-pw__box{display:flex;flex-direction:column;gap:var(--space-3);width:min(360px,100%);padding:var(--space-5);border:1px solid var(--qt-border);border-radius:var(--radius);background:var(--qt-mantle)}.qt-pw__title{margin:0;font-size:var(--fs-display);color:var(--qt-text);letter-spacing:.05em}.qt-pw__desc{margin:0;color:var(--qt-text-muted);font-size:var(--fs-ui)}.qt-pw__input{height:var(--control-h);padding:0 var(--space-3);border:1px solid var(--qt-border);border-radius:var(--radius);background:var(--qt-base);color:var(--qt-text);font-family:var(--font-mono);font-size:var(--fs-body)}.qt-pw__input:focus{outline:none;border-color:var(--qt-accent)}.qt-pw__error{color:var(--qt-red);font-size:var(--fs-caption)}.qt-setting-row{padding:var(--space-4) 0}.qt-setting-row.has-divider{border-top:1px solid var(--qt-border)}.qt-setting-row.has-divider:first-child{border-top:none;padding-top:0}.qt-setting-row__title{font-size:var(--fs-body);color:var(--qt-text);font-weight:500;line-height:1.4;letter-spacing:.01em}.qt-setting-row__desc{color:var(--qt-text-muted);font-size:var(--fs-caption);margin-top:6px;line-height:1.6}.qt-setting-row__control{margin-top:12px;display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap}.qt-setting-btn{padding:6px 14px;background:var(--qt-mantle);border:1px solid color-mix(in srgb,var(--qt-text) 12%,transparent);border-radius:6px;color:var(--qt-text);font-size:13px;cursor:pointer}.qt-setting-btn:hover{background:color-mix(in srgb,var(--qt-text) 8%,var(--qt-mantle))}.qt-setting-range{flex:1;min-width:100px;max-width:180px;accent-color:var(--qt-accent)}.qt-setting-range:disabled{opacity:.4}.qt-setting-range-value{min-width:32px;text-align:right;font-family:var(--font-mono);font-size:12px;color:var(--qt-text-muted)}.qt-segmented{display:inline-flex;border:1px solid var(--qt-border);border-radius:var(--radius);overflow:hidden;background:var(--qt-base)}.qt-segmented__btn{background:transparent;border:none;color:var(--qt-text-muted);padding:0 var(--space-3);height:28px;font-family:inherit;font-size:var(--fs-ui);cursor:pointer;transition:background .12s ease,color .12s ease}.qt-segmented__btn:hover{color:var(--qt-text);background:var(--qt-surface-hover)}.qt-segmented__btn.is-active{background:var(--qt-accent);color:#fff}.qt-switch{display:inline-flex;align-items:center;gap:8px;background:transparent;border:none;padding:0;cursor:pointer;color:var(--qt-text-muted);font-family:inherit;font-size:var(--fs-ui)}.qt-switch__track{position:relative;width:34px;height:20px;border-radius:10px;background:var(--qt-border);transition:background .16s ease}.qt-switch__thumb{position:absolute;top:2px;left:2px;width:16px;height:16px;border-radius:50%;background:#fff;transition:transform .16s ease}.qt-switch.is-on .qt-switch__track{background:var(--qt-accent)}.qt-switch.is-on .qt-switch__thumb{transform:translate(14px)}.qt-switch.is-on{color:var(--qt-text)}.qt-panel-header{padding-bottom:var(--space-4);border-bottom:1px solid var(--qt-border);margin-bottom:var(--space-5)}.qt-panel-header__title{font-size:1.35rem;font-weight:600;color:var(--qt-text);margin:0;letter-spacing:.02em}.qt-panel-header__sub{margin:6px 0 0;color:var(--qt-text-muted);font-size:var(--fs-caption);line-height:1.5}.qt-caret{position:absolute;left:0;top:0;width:2px;height:1.4em;background:var(--qt-accent);border-radius:1px;pointer-events:none;z-index:3;animation:qt-caret-blink .9s steps(2,start) infinite;will-change:transform,height}.qt-caret.is-paused{animation:none;opacity:0}.qt-caret.is-hidden{display:none!important}.qt-caret--shadow{background:var(--qt-text-muted);opacity:.4;animation:none}@keyframes qt-caret-blink{to{opacity:0}}.qt-char{position:relative;transition:color 80ms ease,background 80ms ease}.qt-char.is-hit{color:var(--qt-green)}.qt-char.is-miss{color:var(--qt-red)}.qt-char.is-recover:not(.is-hit):not(.is-miss){color:var(--qt-text-muted);text-decoration:underline dotted;text-underline-offset:3px}.qt-char__placeholder{color:var(--qt-text-muted);opacity:.55}.qt-char-icon{display:inline-block;width:.9em;height:.9em;vertical-align:-.1em;color:inherit;opacity:.55}.qt-char.is-hit .qt-char-icon{opacity:.7}.qt-char.is-miss .qt-char-icon{opacity:1}.qt-char-icon--enter{margin-right:2px}.qt-char.is-newline{display:inline}.qt-char.is-newline:after{content:"";display:block;width:0;height:0}.qt-char-dot{opacity:.55}.qt-char.is-hit .qt-char-dot{opacity:.7}.qt-char.is-miss .qt-char-dot{opacity:1}.qt-typing-arena{position:relative;padding:0;cursor:text;display:flex;flex-direction:column;flex:1;min-height:0}.qt-typing-arena__text{font-family:var(--font-mono);font-size:var(--qt-typing-font-size, 1.05rem);letter-spacing:var(--qt-typing-letter-spacing, 0);line-height:1.85;white-space:pre-wrap;word-break:var(--qt-typing-word-break, break-word);min-height:0;padding:24px 32px;overflow-y:auto;flex:1;color:var(--qt-text);position:relative;z-index:1;transition:filter .16s ease}.qt-typing-arena__text.is-blind .qt-char:not(.is-hit):not(.is-miss):not(.is-recover):not(.is-cursor){color:transparent}.qt-typing-arena__text.is-blind .qt-char.is-cursor:not(.is-hit):not(.is-miss){color:var(--qt-text-muted)}.qt-typing-arena__text.is-ghost .qt-char:not(.is-hit):not(.is-miss){color:var(--qt-text-muted);opacity:.35}.qt-shadow-marker{display:inline-block;width:0;height:1em;vertical-align:baseline}.qt-char.is-eof{display:inline-block;width:0}.qt-typing-arena__editor{position:absolute;width:1px;z-index:2;padding:0;border:0;margin:0;outline:none;background:transparent;color:transparent;caret-color:transparent;font-family:var(--font-mono);font-size:1.05rem;line-height:1.85;white-space:pre;opacity:0}.qt-typing-arena__preedit{position:absolute;z-index:3;padding:3px 8px;font-family:var(--font-mono);font-size:.85rem;line-height:1.2;color:var(--qt-accent);background:var(--qt-mantle);border:1px solid color-mix(in srgb,var(--qt-accent) 40%,transparent);border-radius:999px;box-shadow:0 4px 12px color-mix(in srgb,var(--qt-accent) 30%,transparent);pointer-events:none;white-space:pre;transform:translateY(calc(-100% - 4px))}.qt-typing-arena.is-paused .qt-typing-arena__text{filter:blur(2px);-webkit-user-select:none;user-select:none}.qt-typing-arena.is-cursor-idle,.qt-typing-arena.is-cursor-idle *{cursor:none}.qt-typing-arena__mask{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;align-items:center;justify-content:center;background:color-mix(in srgb,var(--qt-base) 25%,transparent);z-index:5;cursor:pointer;animation:qt-arena-mask-in .16s ease}.qt-typing-arena__mask-tip{color:var(--qt-text-muted);font-size:var(--fs-body);font-weight:500;display:inline-flex;align-items:center;gap:8px}.qt-typing-arena__mask-icon{color:var(--qt-accent);flex-shrink:0;animation:qt-mask-icon-pulse 1.6s ease-in-out infinite}@keyframes qt-mask-icon-pulse{0%,to{transform:translateY(0) scale(1);opacity:1}50%{transform:translateY(-1px) scale(1.06);opacity:.85}}.qt-typing-arena__mask-kbd{display:inline-flex;align-items:center;min-width:44px;height:22px;padding:0 8px;font-family:var(--font-mono);font-size:12px;font-weight:600;color:var(--qt-text);background:color-mix(in srgb,var(--qt-base) 60%,transparent);border:1px solid color-mix(in srgb,var(--qt-text) 14%,transparent);border-bottom-width:2px;border-radius:5px;justify-content:center}@keyframes qt-arena-mask-in{0%{opacity:0}to{opacity:1}}.qt-typing-arena__violation{position:absolute;top:8px;left:50%;transform:translate(-50%);z-index:20;padding:6px 12px;border-radius:999px;background:color-mix(in srgb,var(--qt-red, #f38ba8) 22%,var(--qt-mantle));color:var(--qt-red, #f38ba8);font-size:var(--fs-caption);font-family:var(--font-mono);box-shadow:0 4px 20px #00000059;pointer-events:none;animation:qt-violation-in .14s ease}@keyframes qt-violation-in{0%{opacity:0;transform:translate(-50%,-6px)}to{opacity:1;transform:translate(-50%)}}.qt-hud{display:inline-flex;flex-direction:column;align-items:center;color:var(--qt-text-muted);font-family:var(--font-mono);font-size:var(--fs-caption);-webkit-user-select:none;user-select:none;pointer-events:none}.qt-hud>*{pointer-events:auto}.qt-hud__bar{display:flex;align-items:center;gap:6px;flex-shrink:0}.qt-hud__popover{position:fixed;top:calc(var(--header-h) + 40px);left:50%;transform:translate(-50%);width:min(720px,calc(100vw - 2 * var(--gutter)));max-height:60vh;overflow-y:auto;padding:var(--space-3) var(--space-4);z-index:40;color:var(--qt-text);font-family:var(--font-body);font-size:var(--fs-body);background:color-mix(in srgb,var(--qt-mantle) 92%,transparent);backdrop-filter:blur(20px) saturate(1.6);-webkit-backdrop-filter:blur(20px) saturate(1.6);border-radius:12px;box-shadow:0 1px color-mix(in srgb,var(--qt-text) 6%,transparent) inset,0 12px 40px #00000047,0 3px 10px #00000024;animation:qt-hud-popover-in .2s cubic-bezier(.22,1,.36,1);transform-origin:top center}.qt-hud__arrow{position:fixed;top:calc(var(--header-h) + 28px);width:20px;height:13px;transform:translate(-50%);background:color-mix(in srgb,var(--qt-mantle) 92%,transparent);clip-path:polygon(50% 0,100% 100%,0 100%);z-index:42;pointer-events:none;filter:drop-shadow(0 -2px 4px rgba(0,0,0,.12))}.qt-question--word{text-align:center;padding:var(--space-2) 0}.qt-question__word{font-size:2.4rem;font-weight:600;color:var(--qt-text);letter-spacing:.02em}.qt-question__phonetic{margin-top:4px;color:var(--qt-text-muted);font-family:var(--font-mono);font-size:1rem}.qt-question__gloss{margin-top:8px;color:var(--qt-text);font-size:1rem;line-height:1.5}.qt-question--sentence .qt-question__translation{margin-top:12px;padding-top:8px;border-top:1px dashed color-mix(in srgb,var(--qt-text) 15%,transparent);color:var(--qt-text-muted);font-size:.95rem;line-height:1.5}@keyframes qt-hud-popover-in{0%{opacity:0;transform:translate(-50%) translateY(-4px) scale(.98)}to{opacity:1;transform:translate(-50%) translateY(0) scale(1)}}.qt-hud-dot{display:inline-block;width:7px;height:7px;border-radius:50%;background:var(--qt-muted);flex-shrink:0}.qt-hud-dot.is-running{background:var(--qt-green);animation:qt-hud-pulse 1.4s ease-in-out infinite}.qt-hud-dot.is-done{background:var(--qt-green)}@keyframes qt-hud-pulse{0%,to{opacity:1}50%{opacity:.35}}.qt-hud-item{color:var(--qt-text-muted);display:inline-flex;align-items:center;gap:4px;padding:0 6px;height:24px;box-sizing:border-box}.qt-hud-item b{color:var(--qt-text);font-weight:600}.qt-hud-unit{color:var(--qt-text-muted);font-size:10px;margin-left:3px;letter-spacing:.03em}.qt-hud-dot{padding:0;border:none;background:var(--qt-muted);box-shadow:none}.qt-hud-icon{color:var(--qt-text-muted);flex-shrink:0}.qt-hud-sep{display:none}.qt-hud-btn{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;padding:0;background:transparent;border:none;color:var(--qt-text-muted);border-radius:4px;cursor:pointer;outline:none;transition:color .12s ease,background .12s ease}.qt-hud-btn:focus-visible{box-shadow:0 0 0 2px color-mix(in srgb,var(--qt-accent) 60%,transparent)}.qt-hud-btn:hover:not(:disabled){color:var(--qt-accent);background:color-mix(in srgb,var(--qt-text) 8%,transparent)}.qt-hud-btn.is-on{color:var(--qt-accent)}.qt-hud-btn.is-off{opacity:.5}.qt-hud-btn:disabled{opacity:.3;cursor:not-allowed}.qt-tk{--qt-tk-scale: 1.3;--qt-tk-native-w: 454px;--qt-tk-native-h: 170px;width:calc(var(--qt-tk-native-w) * var(--qt-tk-scale));height:calc(var(--qt-tk-native-h) * var(--qt-tk-scale));display:flex;justify-content:center;-webkit-user-select:none;user-select:none;pointer-events:none;font-family:var(--font-mono)}.qt-tk__stage{position:relative;width:var(--qt-tk-native-w);display:flex;flex-direction:column;padding:10px;box-sizing:border-box;transform:scale(var(--qt-tk-scale));transform-origin:top center}.qt-tk__row{display:flex;justify-content:space-between}.qt-tk__row.is-space-row{justify-content:flex-start}.qt-tk__key{position:relative;width:28px;height:28px;margin:1px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1px;box-sizing:border-box;border:1px solid color-mix(in srgb,var(--qt-text) 6%,transparent);border-radius:5px 5px 7px 7px;background:color-mix(in srgb,var(--qt-mantle) 55%,transparent);backdrop-filter:blur(12px) saturate(1.4);-webkit-backdrop-filter:blur(12px) saturate(1.4);box-shadow:0 1px color-mix(in srgb,var(--qt-text) 10%,transparent) inset,0 2px 6px #0000002e,0 1px 2px #0000001a;color:var(--qt-text);opacity:.85;font-size:13px;font-weight:500;transition:background .2s ease,color .2s ease,border-color .2s ease,opacity .2s ease,transform .12s ease}.qt-tk__key.is-tab{width:42px;font-size:10px}.qt-tk__key.is-caps,.qt-tk__key.is-enter{width:49px;font-size:10px}.qt-tk__key.is-lshift,.qt-tk__key.is-rshift{width:63px;font-size:10px}.qt-tk__key.is-backspace{width:42px;font-size:10px}.qt-tk__key.is-space{width:140px;font-size:10px}.qt-tk__key.is-bump:after{content:"";position:absolute;bottom:3px;left:50%;transform:translate(-50%);width:8px;height:2px;background:currentColor;opacity:.6;border-radius:1px}.qt-tk__key-sub{font-size:9px;opacity:.7;line-height:1}.qt-tk__key-main{line-height:1}.qt-tk__key.is-next{background:transparent;color:var(--qt-accent);border-color:var(--qt-accent);border-width:2px;opacity:1}.qt-tk__key.is-shift-hint{background:transparent;color:var(--qt-accent);border-color:var(--qt-accent);opacity:.9}.qt-tk__hand{color:var(--qt-text);opacity:.75;pointer-events:none;z-index:2}.qt-tk__hand svg{width:100%;height:100%;display:block}.qt-tk__hand.is-interactive{pointer-events:auto;cursor:grab;touch-action:none}.qt-tk__hand.is-interactive:active{cursor:grabbing}.qt-tk__hand.is-interactive svg{pointer-events:none}.qt-tk__row{position:relative;z-index:1}.qt-cardnav{display:inline-flex;align-items:center;gap:8px;max-width:500px;color:inherit;-webkit-user-select:none;user-select:none;line-height:1}.qt-cardnav__btn{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;padding:0;border:none;border-radius:3px;background:transparent;color:inherit;cursor:pointer;transition:background .12s ease,color .12s ease}.qt-cardnav__btn:hover:not(:disabled){background:color-mix(in srgb,var(--qt-text) 12%,transparent);color:var(--qt-text)}.qt-cardnav__btn:disabled{opacity:.3;cursor:not-allowed}.qt-cardnav__dots{display:inline-flex;align-items:center;gap:4px}.qt-cardnav__dot{width:7px;height:7px;padding:0;border:1px solid currentColor;border-radius:50%;background:transparent;opacity:.4;cursor:pointer;transition:opacity .12s ease,background .12s ease,transform .12s ease,box-shadow .12s ease}.qt-cardnav__dot:hover{opacity:.75}.qt-cardnav__dot.is-done{background:currentColor;border-color:transparent;opacity:.85}.qt-cardnav__dot.is-current{opacity:1;box-shadow:0 0 0 1.5px var(--qt-accent, var(--qt-green));transform:scale(1.2)}.qt-cardnav__dot.is-current.is-done{background:var(--qt-accent, var(--qt-green))}.qt-cardnav__track{position:relative;flex:1 1 auto;min-width:100px;height:3px;border-radius:2px;background:currentColor;opacity:.22}.qt-cardnav__track-fill{position:absolute;left:0;top:0;bottom:0;border-radius:2px;background:currentColor;opacity:.75}.qt-cardnav__track-cursor{position:absolute;top:50%;width:6px;height:6px;margin-left:-3px;margin-top:-3px;border-radius:50%;background:var(--qt-accent, var(--qt-green))}.qt-cardnav__frac{font-family:var(--font-mono, ui-monospace, monospace);font-size:var(--fs-caption);min-width:3.5em;text-align:center;color:inherit;opacity:.8;display:inline-flex;align-items:center;justify-content:center;gap:2px}.qt-cardnav__sep{opacity:.4;margin:0 1px}.qt-cardnav__jump-btn{padding:0 2px;border:none;background:transparent;color:inherit;font:inherit;cursor:pointer;border-radius:3px}.qt-cardnav__jump-btn:hover{background:color-mix(in srgb,var(--qt-text) 12%,transparent);color:var(--qt-text)}.qt-cardnav__jump{width:2.4em;padding:0 2px;border:1px solid var(--qt-mauve, currentColor);border-radius:3px;background:transparent;color:var(--qt-text);font:inherit;text-align:center;outline:none}.qt-cardnav__frac--editing{opacity:1}.qt-carousel{position:relative;width:100%;flex:1;min-height:0;display:flex;justify-content:center;align-items:stretch;overflow:hidden;margin-top:40px;margin-bottom:243px}.qt-carousel__current{position:relative;z-index:5;width:100%;max-width:800px;display:flex;flex-direction:column;min-height:0}.qt-confirm__overlay{position:fixed;top:0;right:0;bottom:0;left:0;background:#0000008c;z-index:200;animation:qt-confirm-fade .12s ease}.qt-confirm__content{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);z-index:201;width:min(420px,calc(100vw - 32px));background:var(--qt-base);border:1px solid color-mix(in srgb,var(--qt-text) 12%,transparent);border-radius:12px;box-shadow:0 24px 60px #00000073;padding:20px 22px 16px;animation:qt-confirm-pop .14s ease}@keyframes qt-confirm-fade{0%{opacity:0}to{opacity:1}}@keyframes qt-confirm-pop{0%{opacity:0;transform:translate(-50%,-48%)}to{opacity:1;transform:translate(-50%,-50%)}}.qt-confirm__title{font-size:15px;font-weight:600;color:var(--qt-text);margin:0 0 10px}.qt-confirm__message{color:var(--qt-text);font-size:13.5px;line-height:1.55;margin:0 0 18px;word-break:break-word}.qt-confirm__actions{display:flex;justify-content:flex-end;gap:8px}.qt-confirm__btn{padding:6px 16px;background:var(--qt-mantle);border:1px solid color-mix(in srgb,var(--qt-text) 12%,transparent);border-radius:6px;color:var(--qt-text);font-size:13px;cursor:pointer;font-family:inherit;min-width:64px}.qt-confirm__btn:hover{background:color-mix(in srgb,var(--qt-text) 8%,var(--qt-mantle))}.qt-confirm__btn:focus-visible{outline:2px solid var(--qt-accent);outline-offset:1px}.qt-confirm__btn--primary{background:var(--qt-accent);color:var(--qt-base);border-color:var(--qt-accent)}.qt-confirm__btn--primary:hover{filter:brightness(1.05)}.qt-confirm__btn--danger{background:var(--qt-red, #f38ba8);border-color:var(--qt-red, #f38ba8);color:var(--qt-base)}.qt-confirm__btn--danger:hover{filter:brightness(1.05)}.qt-danger-btn{background:transparent;border:1px solid var(--qt-border);color:var(--qt-text-muted);padding:0 var(--space-3);height:28px;border-radius:var(--radius);font-family:inherit;font-size:var(--fs-ui);cursor:pointer;transition:color .12s ease,border-color .12s ease,background .12s ease}.qt-danger-btn:hover:not(:disabled){color:var(--qt-red);border-color:color-mix(in srgb,var(--qt-red) 50%,var(--qt-border));background:color-mix(in srgb,var(--qt-red) 10%,transparent)}.qt-danger-btn:disabled{opacity:.4;cursor:not-allowed}.qt-kcal__overlay{position:fixed;top:0;right:0;bottom:0;left:0;background:#0000008c;z-index:100;animation:qt-kcal-fade .12s ease}.qt-kcal__content{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);z-index:101;display:flex;flex-direction:column;width:min(880px,calc(100vw - 32px));max-height:calc(100vh - 32px);background:var(--qt-base);border:1px solid color-mix(in srgb,var(--qt-text) 12%,transparent);border-radius:12px;box-shadow:0 24px 60px #00000073;overflow:hidden;animation:qt-kcal-pop .14s ease}@keyframes qt-kcal-fade{0%{opacity:0}to{opacity:1}}@keyframes qt-kcal-pop{0%{opacity:0;transform:translate(-50%,-48%)}to{opacity:1;transform:translate(-50%,-50%)}}.qt-kcal__header{display:flex;align-items:center;gap:16px;padding:12px 16px;border-bottom:1px solid color-mix(in srgb,var(--qt-text) 10%,transparent)}.qt-kcal__title{font-size:15px;font-weight:600;color:var(--qt-text);margin:0}.qt-kcal__hint{flex:1;color:var(--qt-text-muted);font-size:12px}.qt-kcal__close{background:transparent;border:0;color:var(--qt-text-muted);font-size:20px;line-height:1;cursor:pointer;padding:4px 8px}.qt-kcal__close:hover{color:var(--qt-text)}.qt-kcal__body{display:flex;flex-direction:column;gap:16px;padding:20px 24px;min-height:0;overflow:auto}.qt-kcal__nav{display:flex;align-items:center;gap:12px}.qt-kcal__pose-label{flex:1;display:flex;align-items:baseline;justify-content:center;gap:12px}.qt-kcal__pose-side{color:var(--qt-text-muted);font-size:13px}.qt-kcal__pose-name{color:var(--qt-accent);font-size:22px;font-weight:600;font-family:var(--font-mono)}.qt-kcal__pose-idx{color:var(--qt-text-muted);font-size:12px;font-family:var(--font-mono)}.qt-kcal__stage{display:flex;justify-content:center;padding:8px;background:color-mix(in srgb,var(--qt-text) 3%,transparent);border-radius:8px;overflow:hidden;position:relative;z-index:0}.qt-kcal__nav,.qt-kcal__actions,.qt-kcal__coords{position:relative;z-index:1}.qt-kcal__coords{display:flex;align-items:center;gap:16px;padding:8px 12px;background:color-mix(in srgb,var(--qt-text) 5%,transparent);border-radius:6px;font-family:var(--font-mono);font-size:12.5px;color:var(--qt-text)}.qt-kcal__svg-tag{color:var(--qt-accent);font-size:11.5px}.qt-kcal__dirty{margin-left:auto;padding:2px 8px;background:color-mix(in srgb,var(--qt-red, #f38ba8) 22%,transparent);color:var(--qt-red, #f38ba8);border-radius:999px;font-size:11px}.qt-kcal__customized{margin-left:auto;color:var(--qt-accent);font-size:11px}.qt-kcal__actions{display:flex;gap:8px;justify-content:flex-end}.qt-kcal__footer{display:flex;align-items:center;gap:8px;padding:12px 16px;border-top:1px solid color-mix(in srgb,var(--qt-text) 10%,transparent);background:color-mix(in srgb,var(--qt-text) 3%,transparent)}.qt-kcal__footer-spacer{flex:1}.qt-kcal__btn{padding:6px 14px;background:var(--qt-mantle);border:1px solid color-mix(in srgb,var(--qt-text) 12%,transparent);border-radius:6px;color:var(--qt-text);font-size:13px;cursor:pointer;font-family:inherit}.qt-kcal__btn:hover:not(:disabled){background:color-mix(in srgb,var(--qt-text) 8%,var(--qt-mantle))}.qt-kcal__btn:disabled{opacity:.4;cursor:not-allowed}.qt-kcal__btn--primary{background:var(--qt-accent);color:var(--qt-base);border-color:var(--qt-accent)}.qt-kcal__btn--primary:hover:not(:disabled){filter:brightness(1.05)}.qt-kcal__picker-overlay{position:fixed;top:0;right:0;bottom:0;left:0;background:#000000a6;z-index:110;animation:qt-kcal-fade .12s ease}.qt-kcal__picker{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);z-index:111;display:flex;flex-direction:column;width:min(720px,calc(100vw - 32px));max-height:calc(100vh - 48px);background:var(--qt-base);border:1px solid color-mix(in srgb,var(--qt-text) 12%,transparent);border-radius:12px;box-shadow:0 24px 60px #0000008c;overflow:hidden}.qt-kcal__picker-header{display:flex;align-items:center;padding:12px 16px;border-bottom:1px solid color-mix(in srgb,var(--qt-text) 10%,transparent)}.qt-kcal__picker-title{flex:1;font-size:14px;font-weight:600;color:var(--qt-text);margin:0}.qt-kcal__picker-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(96px,1fr));gap:8px;padding:16px;overflow-y:auto}.qt-kcal__picker-cell{display:flex;align-items:center;justify-content:center;height:96px;background:color-mix(in srgb,var(--qt-text) 4%,transparent);border:1px solid color-mix(in srgb,var(--qt-text) 10%,transparent);border-radius:8px;cursor:pointer;color:var(--qt-text);padding:4px}.qt-kcal__picker-cell:hover{background:color-mix(in srgb,var(--qt-accent) 10%,transparent);border-color:color-mix(in srgb,var(--qt-accent) 40%,transparent)}.qt-kcal__picker-cell.is-selected{border-color:var(--qt-accent);background:color-mix(in srgb,var(--qt-accent) 15%,transparent)}.qt-shortcuts{display:flex;flex-direction:column;gap:20px;margin-top:12px}.qt-shortcuts__group-title{font-size:12px;font-weight:600;color:var(--qt-text-muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px}.qt-shortcut-row{display:grid;grid-template-columns:1fr auto auto auto;grid-column-gap:8px;align-items:center;padding:6px 0;border-top:1px solid color-mix(in srgb,var(--qt-text) 6%,transparent)}.qt-shortcut-row:first-of-type{border-top:0}.qt-shortcut-row__label{font-size:13px;color:var(--qt-text)}.qt-shortcut-row__key{min-width:120px;padding:4px 10px;background:color-mix(in srgb,var(--qt-text) 4%,transparent);border:1px solid color-mix(in srgb,var(--qt-text) 12%,transparent);border-radius:6px;color:var(--qt-text);cursor:pointer;font-family:var(--font-mono);font-size:12px;text-align:center}.qt-shortcut-row__key:hover{background:color-mix(in srgb,var(--qt-text) 8%,transparent)}.qt-shortcut-row__key.is-recording{border-color:var(--qt-accent);background:color-mix(in srgb,var(--qt-accent) 12%,transparent)}.qt-shortcut-row__kbd{font-family:var(--font-mono);font-size:12px}.qt-shortcut-row__disabled{color:var(--qt-text-muted);font-style:italic}.qt-shortcut-row__hint{color:var(--qt-accent)}.qt-shortcut-row__btn{width:24px;height:24px;padding:0;background:transparent;border:0;border-radius:4px;color:var(--qt-text-muted);font-size:14px;cursor:pointer;line-height:1}.qt-shortcut-row__btn:hover{background:color-mix(in srgb,var(--qt-text) 8%,transparent);color:var(--qt-text)}.qt-shortcut-row__btn--danger:hover{color:var(--qt-red, #f38ba8)}.qt-shortcut-row__warn{grid-column:1 / -1;padding:4px 0 2px;font-size:11.5px;color:color-mix(in srgb,var(--qt-red, #f38ba8) 80%,var(--qt-text));display:flex;gap:12px;flex-wrap:wrap}.qt-settings__overlay{position:fixed;top:0;right:0;bottom:0;left:0;background:#0006;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);animation:qt-fade-in .12s ease-out;z-index:40}@keyframes qt-fade-in{0%{opacity:0}to{opacity:1}}.qt-settings__content{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:min(760px,92vw);height:min(600px,85vh);background:var(--qt-mantle);border-radius:16px;box-shadow:0 12px 40px #0006;display:flex;flex-direction:column;overflow:hidden;z-index:50;animation:qt-dialog-in .16s ease-out}@keyframes qt-dialog-in{0%{opacity:0;transform:translate(-50%,-50%) scale(.96)}to{opacity:1;transform:translate(-50%,-50%) scale(1)}}.qt-settings__header{display:flex;align-items:flex-end;gap:var(--space-3);padding:8px 8px 0 12px;background:var(--qt-mantle);flex-shrink:0;min-height:40px}.qt-settings__brand{display:flex;align-items:center;gap:6px;color:var(--qt-text-muted);padding-bottom:10px;flex-shrink:0}.qt-settings__brand-text{font-size:var(--fs-caption);font-weight:600;color:var(--qt-text);margin:0;letter-spacing:.08em}.qt-settings__tabbar{display:flex;gap:2px;flex:1;align-items:flex-end;padding-left:var(--space-3)}.qt-settings__tab{position:relative;background:transparent;border:none;color:var(--qt-text-muted);padding:9px 16px;font-family:inherit;font-size:var(--fs-ui);cursor:pointer;border-radius:8px 8px 0 0;transition:background .12s ease,color .12s ease;margin-bottom:-1px;border:1px solid transparent;border-bottom:none}.qt-settings__tab:hover:not(.is-active){color:var(--qt-text);background:var(--qt-surface-hover)}.qt-settings__tab.is-active{color:var(--qt-text);background:var(--qt-base);border-color:var(--qt-border);font-weight:600;z-index:2}.qt-settings__close{background:transparent;border:none;color:var(--qt-text-muted);font-size:20px;width:26px;height:26px;border-radius:4px;cursor:pointer;line-height:1;display:inline-flex;align-items:center;justify-content:center;transition:background .12s ease,color .12s ease;margin-bottom:8px;flex-shrink:0}.qt-settings__close:hover{background:var(--qt-surface-hover);color:var(--qt-text)}.qt-settings__panel{flex:1;overflow-y:auto;padding:var(--space-5);background:var(--qt-base);border-top:1px solid var(--qt-border);position:relative;z-index:1}.qt-toast-stack{position:fixed;left:50%;bottom:32px;transform:translate(-50%);z-index:300;display:flex;flex-direction:column;align-items:center;gap:8px;pointer-events:none}.qt-toast{padding:8px 16px;min-width:120px;max-width:min(90vw,460px);border-radius:8px;font-size:13px;font-family:inherit;color:var(--qt-text);background:var(--qt-mantle);border:1px solid color-mix(in srgb,var(--qt-text) 14%,transparent);box-shadow:0 8px 24px #00000059;text-align:center;pointer-events:auto;opacity:0;transform:translateY(6px);transition:opacity .16s ease,transform .16s ease}.qt-toast.is-in{opacity:1;transform:translateY(0)}.qt-toast.is-out{opacity:0;transform:translateY(-4px)}.qt-toast--success{border-color:color-mix(in srgb,var(--qt-accent) 40%,transparent);color:var(--qt-accent)}.qt-toast--warn{border-color:color-mix(in srgb,var(--qt-yellow, #f9e2af) 60%,transparent);color:var(--qt-yellow, #f9e2af)}.qt-toast--error{border-color:color-mix(in srgb,var(--qt-red, #f38ba8) 60%,transparent);color:var(--qt-red, #f38ba8)}:root{--qt-base: #252528;--qt-mantle: #2b2b2f;--qt-text: #dadada;--qt-text-muted: #b3b3b3;--qt-accent: #7e6ad8;--qt-mauve: #a882ff;--qt-border: #363636;--qt-surface-hover: rgba(255, 255, 255, .067);--qt-green: #44cf6e;--qt-yellow: #e0de71;--qt-peach: #e9973f;--qt-red: #fb464c}:root{--space-1: .25rem;--space-2: .5rem;--space-3: .75rem;--space-4: 1rem;--space-5: 1.125rem;--control-h: 1.75rem;--icon-btn: 1.625rem;--header-h: clamp(40px, 56px - 1.25vw, 44px);--footer-h: clamp(28px, 40px - .85vw, 34px);--search-h: calc(var(--header-h) - .625rem);--gutter: clamp(12px, 3vw, 16px);--radius: 6px}:root{font-size:clamp(13px,9px + 1vw,16px);--fs-caption: .6875rem;--fs-ui: .75rem;--fs-body: .875rem;--fs-title: .875rem;--fs-display: 1.25rem;--font-ui: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;--font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace}*,*:before,*:after{box-sizing:border-box}html,body{margin:0;padding:0;height:100%;overflow:hidden;font-family:var(--font-ui);font-size:var(--fs-body);background:var(--qt-base);color:var(--qt-text);-webkit-font-smoothing:antialiased}#root{height:100%;display:flex;flex-direction:column;overflow:hidden}button{font-family:inherit;font-size:inherit;color:inherit;cursor:pointer}a{color:var(--qt-accent);text-decoration:none}a:hover{text-decoration:underline}kbd{font-family:var(--font-mono)}pre{font-family:var(--font-mono);font-size:var(--fs-body);background:var(--qt-base);border:1px solid var(--qt-border);border-radius:var(--radius);padding:var(--space-3);overflow:auto;line-height:1.5;margin:var(--space-3) 0}code{font-family:var(--font-mono);font-size:.95em;background:var(--qt-base);padding:0 3px;border-radius:3px}pre code{background:transparent;padding:0}.qt-app{display:flex;flex-direction:column;height:100vh;background:var(--qt-base);overflow:hidden}.qt-empty{color:var(--qt-text-muted);text-align:center;padding:var(--space-5);font-size:var(--fs-body)}.qt-home{flex:1;min-width:0;min-height:0;padding:var(--space-3);width:100%;display:flex;flex-direction:column;overflow:hidden}.qt-home__scroll{flex:1;min-height:0;overflow-x:hidden;overflow-y:auto}.qt-home__grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(260px,100%),1fr));gap:var(--space-3);min-width:0}.qt-home__list{display:flex;flex-direction:column}.qt-tag{background:color-mix(in srgb,var(--qt-accent) 14%,transparent);color:var(--qt-accent);padding:0 var(--space-2);border-radius:3px;font-size:var(--fs-caption);font-family:var(--font-mono)}.qt-session{position:relative;flex:1;min-height:0;width:100%;display:flex;flex-direction:column;overflow:hidden;padding:0}.qt-session__hud-anchor{position:absolute;top:.3125rem;left:0;right:0;display:flex;justify-content:center;pointer-events:none;z-index:10}.qt-session__hud-anchor>*{pointer-events:auto}.qt-session__typing{flex:1;min-height:0;width:100%;overflow:hidden;padding:0;display:flex;flex-direction:column}.qt-card-warning{background:color-mix(in srgb,var(--qt-yellow, #f9e2af) 15%,transparent);border:1px solid color-mix(in srgb,var(--qt-yellow, #f9e2af) 50%,var(--qt-border));color:var(--qt-text);padding:var(--space-2) var(--space-3);border-radius:var(--radius);font-size:var(--fs-caption);margin-bottom:var(--space-2)}.qt-card-warning code{background:color-mix(in srgb,var(--qt-text) 10%,transparent);padding:0 4px;border-radius:3px;margin-left:4px;font-family:var(--font-mono)}.qt-session__kbd{position:absolute;bottom:.75rem;left:50%;transform:translate(-50%);display:flex;justify-content:center;pointer-events:none;z-index:10;background:transparent}.qt-session__done{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--space-3);padding:var(--space-5)}.qt-session__done h1{color:var(--qt-green);font-size:var(--fs-display);margin:0;font-weight:600}.qt-session__done p{color:var(--qt-text-muted);margin:0 0 var(--space-3)}.qt-session__stamp{padding:12px 32px;border:3px solid var(--qt-green);border-radius:8px;color:var(--qt-green);font-family:var(--font-mono);font-size:1.5rem;font-weight:700;letter-spacing:.2em;transform:rotate(-6deg);background:color-mix(in srgb,var(--qt-green) 8%,transparent);animation:qt-stamp-in .4s cubic-bezier(.34,1.56,.64,1)}@keyframes qt-stamp-in{0%{opacity:0;transform:rotate(-30deg) scale(1.6)}to{opacity:1;transform:rotate(-6deg) scale(1)}}.qt-primary{background:var(--qt-accent);color:#fff;padding:0 var(--space-4);height:var(--control-h);border:none;border-radius:var(--radius);font-family:inherit;font-weight:500;font-size:var(--fs-ui);transition:filter .12s ease}.qt-primary:hover{filter:brightness(1.1)}
@@ -0,0 +1,14 @@
1
+ <!doctype html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <link rel="icon" type="image/svg+xml" href="/qtype-mark.svg" />
7
+ <title>qtype — 打字复习</title>
8
+ <script type="module" crossorigin src="/assets/index-DBABH3dG.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-RTLZ-zOC.css">
10
+ </head>
11
+ <body>
12
+ <div id="root"></div>
13
+ </body>
14
+ </html>
@@ -0,0 +1,49 @@
1
+ <svg width="1280" height="640" viewBox="0 0 1280 640" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <defs>
3
+ <linearGradient id="panel" x1="0" y1="0" x2="1280" y2="640" gradientUnits="userSpaceOnUse">
4
+ <stop stop-color="#F5F0FF"/>
5
+ <stop offset="1" stop-color="#E7DFFF"/>
6
+ </linearGradient>
7
+ <linearGradient id="cardFront" x1="190" y1="140" x2="430" y2="400" gradientUnits="userSpaceOnUse">
8
+ <stop stop-color="#FCFAFF"/>
9
+ <stop offset="1" stop-color="#E9E1FF"/>
10
+ </linearGradient>
11
+ <linearGradient id="cardBack" x1="140" y1="110" x2="360" y2="350" gradientUnits="userSpaceOnUse">
12
+ <stop stop-color="#A78BFA"/>
13
+ <stop offset="1" stop-color="#7C3AED"/>
14
+ </linearGradient>
15
+ <filter id="softShadow" x="70" y="70" width="420" height="420" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
16
+ <feFlood flood-opacity="0" result="BackgroundImageFix"/>
17
+ <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
18
+ <feOffset dy="18"/>
19
+ <feGaussianBlur stdDeviation="18"/>
20
+ <feComposite in2="hardAlpha" operator="out"/>
21
+ <feColorMatrix type="matrix" values="0 0 0 0 0.09 0 0 0 0 0.06 0 0 0 0 0.18 0 0 0 0.2 0"/>
22
+ <feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1_1"/>
23
+ <feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_1_1" result="shape"/>
24
+ </filter>
25
+ </defs>
26
+
27
+ <rect width="1280" height="640" rx="40" fill="url(#panel)"/>
28
+
29
+ <g filter="url(#softShadow)">
30
+ <path d="M349 251C349 309.542 301.542 357 243 357C184.458 357 137 309.542 137 251C137 192.458 184.458 145 243 145C301.542 145 349 192.458 349 251Z" stroke="#1B1530" stroke-width="24"/>
31
+ <path d="M320 332L359 371" stroke="#1B1530" stroke-width="24" stroke-linecap="round"/>
32
+ <rect x="156" y="141" width="154" height="190" rx="28" transform="rotate(-8 156 141)" fill="url(#cardBack)"/>
33
+ <path d="M190 164H301L332 196V311C332 328.673 317.673 343 300 343H190C172.327 343 158 328.673 158 311V196C158 178.327 172.327 164 190 164Z" fill="url(#cardFront)" stroke="#1B1530" stroke-width="12" stroke-linejoin="round"/>
34
+ <path d="M301 164V196H332" stroke="#1B1530" stroke-width="12" stroke-linejoin="round"/>
35
+ <path d="M207 218H288" stroke="#1B1530" stroke-width="12" stroke-linecap="round"/>
36
+ <path d="M207 252H271" stroke="#1B1530" stroke-width="12" stroke-linecap="round"/>
37
+ <rect x="207" y="286" width="66" height="24" rx="12" fill="#8B5CF6"/>
38
+ </g>
39
+
40
+ <g>
41
+ <text x="515" y="418" fill="#1B1530" font-family="Helvetica Neue, Arial, sans-serif" font-size="228" font-weight="700" letter-spacing="-4">qtype</text>
42
+ <text x="525" y="500" fill="#5D5672" font-family="Menlo, Monaco, Consolas, monospace" font-size="34" font-weight="700" letter-spacing="5">COMPILE TYPE INTERNALIZE</text>
43
+ </g>
44
+
45
+ <g opacity="0.9">
46
+ <path d="M525 480H760" stroke="#8B5CF6" stroke-width="12" stroke-linecap="round"/>
47
+ <path d="M795 480H980" stroke="#1B1530" stroke-width="12" stroke-linecap="round"/>
48
+ </g>
49
+ </svg>
@@ -0,0 +1,21 @@
1
+ <svg width="512" height="512" viewBox="120 120 265 280" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <defs>
3
+ <linearGradient id="cardFront" x1="166" y1="150" x2="337" y2="343" gradientUnits="userSpaceOnUse">
4
+ <stop stop-color="#FCFAFF"/>
5
+ <stop offset="1" stop-color="#E9E1FF"/>
6
+ </linearGradient>
7
+ <linearGradient id="cardBack" x1="136" y1="132" x2="306" y2="319" gradientUnits="userSpaceOnUse">
8
+ <stop stop-color="#A78BFA"/>
9
+ <stop offset="1" stop-color="#7C3AED"/>
10
+ </linearGradient>
11
+ </defs>
12
+
13
+ <path d="M363 250C363 313.513 311.513 365 248 365C184.487 365 133 313.513 133 250C133 186.487 184.487 135 248 135C311.513 135 363 186.487 363 250Z" stroke="#1B1530" stroke-width="24"/>
14
+ <path d="M334 331L375 372" stroke="#1B1530" stroke-width="24" stroke-linecap="round"/>
15
+ <rect x="160" y="137" width="154" height="190" rx="28" transform="rotate(-8 160 137)" fill="url(#cardBack)"/>
16
+ <path d="M194 160H305L336 192V307C336 324.673 321.673 339 304 339H194C176.327 339 162 324.673 162 307V192C162 174.327 176.327 160 194 160Z" fill="url(#cardFront)" stroke="#1B1530" stroke-width="12" stroke-linejoin="round"/>
17
+ <path d="M305 160V192H336" stroke="#1B1530" stroke-width="12" stroke-linejoin="round"/>
18
+ <path d="M211 214H292" stroke="#1B1530" stroke-width="12" stroke-linecap="round"/>
19
+ <path d="M211 248H275" stroke="#1B1530" stroke-width="12" stroke-linecap="round"/>
20
+ <rect x="211" y="282" width="66" height="24" rx="12" fill="#8B5CF6"/>
21
+ </svg>
package/docs/README.md ADDED
@@ -0,0 +1,23 @@
1
+ # qtype Documentation
2
+
3
+ Chinese: [README.zh.md](./README.zh.md)
4
+
5
+ Documentation for authoring, building, and publishing a typing-review
6
+ repo with qtype. See also
7
+ [`README.md`](../README.md) and
8
+ [`CHANGELOG.md`](../CHANGELOG.md) at the package root.
9
+
10
+ ## Guides
11
+
12
+ | Doc | What's in it |
13
+ |---|---|
14
+ | [`en/architecture.md`](./en/architecture.md) ([zh](./zh/architecture.md)) | Top-level layers (`cli` / `core` / `store` / `view`) and how a `publish build` flows through them |
15
+ | [`en/cli-reference.md`](./en/cli-reference.md) ([zh](./zh/cli-reference.md)) | Brief CLI overview — each top-level command with a summary and example |
16
+ | [`en/authoring.md`](./en/authoring.md) ([zh](./zh/authoring.md)) | Quick primer on writing `material/*.md`; the full spec is `templates/{zh,en}/QTYPE.md` |
17
+
18
+ ## Spec
19
+
20
+ The qtype authoring spec is available in both languages:
21
+ [`templates/en/QTYPE.md`](../templates/en/QTYPE.md) /
22
+ [`templates/zh/QTYPE.md`](../templates/zh/QTYPE.md). `qtype init --lang zh|en`
23
+ copies the matching version into every new repo as `QTYPE.md`.
@@ -0,0 +1,20 @@
1
+ # qtype 文档
2
+
3
+ English: [README.md](./README.md)
4
+
5
+ 用 qtype 撰写、构建、发布一个打字复习仓库的相关文档。也可参考项目根目录的
6
+ [`README.zh.md`](../README.zh.md) 和 [`CHANGELOG.zh.md`](../CHANGELOG.zh.md)。
7
+
8
+ ## 指南
9
+
10
+ | 文档 | 内容 |
11
+ |---|---|
12
+ | [`zh/architecture.md`](./zh/architecture.md)([en](./en/architecture.md)) | 顶层分层(`cli` / `core` / `store` / `view`)以及 `publish build` 如何贯穿这些层 |
13
+ | [`zh/cli-reference.md`](./zh/cli-reference.md)([en](./en/cli-reference.md)) | CLI 简介 —— 每个顶层命令附一句摘要和示例 |
14
+ | [`zh/authoring.md`](./zh/authoring.md)([en](./en/authoring.md)) | 撰写 `material/*.md` 的快速入门;完整规范见 `templates/{zh,en}/QTYPE.md` |
15
+
16
+ ## 规范
17
+
18
+ qtype 撰写规范双语:[`templates/en/QTYPE.md`](../templates/en/QTYPE.md) /
19
+ [`templates/zh/QTYPE.md`](../templates/zh/QTYPE.md)。
20
+ `qtype init --lang zh|en` 时拷成新仓库的 `QTYPE.md`。
@@ -0,0 +1,138 @@
1
+ # Authoring material files
2
+
3
+ Chinese: [authoring.md](../zh/authoring.md)
4
+
5
+ Quick primer for writing `material/*.md`. The authoritative spec is
6
+ [`templates/en/QTYPE.md`](../../templates/en/QTYPE.md) (copied into
7
+ every new repo by `qtype init --lang en`). Read that when in doubt.
8
+
9
+ ## Where files live
10
+
11
+ ```
12
+ my-deck/
13
+ ├── QTYPE.md # the spec (auto-copied by init; keep it around)
14
+ ├── index.md # human-facing table of contents (optional)
15
+ ├── material/
16
+ │ ├── example.md
17
+ │ ├── javascript-basics.md
18
+ │ └── english-vocab.md
19
+ └── .qtype/
20
+ └── config.json # per-repo settings
21
+ ```
22
+
23
+ One `.md` file = one deck of cards.
24
+
25
+ ## Card shape
26
+
27
+ Each card has a question and an answer, separated by `## Q:` / `## A:`
28
+ headings. Multiple cards in the same file are separated by
29
+ `\n\n---\n\n` (a bare `---` on its own line, blank-line-padded).
30
+
31
+ ```markdown
32
+ ---
33
+ tags: [js, basics]
34
+ type: normal
35
+ ---
36
+
37
+ ## Q:
38
+
39
+ What are the three core concepts of React?
40
+
41
+ ## A:
42
+
43
+ Components, props, and state.
44
+
45
+ ---
46
+
47
+ ## Q:
48
+
49
+ What does `useState` return?
50
+
51
+ ## A:
52
+
53
+ A tuple of `[value, setter]`.
54
+ ```
55
+
56
+ The parser splits on the `---` separator, then per-card extracts Q,
57
+ A, and metadata. Cards missing either Q or A are dropped silently
58
+ (good for stubs).
59
+
60
+ ## Frontmatter fields
61
+
62
+ At the top of a file, YAML frontmatter applies to every card in the
63
+ file. Per-card overrides go inside a fenced YAML block above the
64
+ card if needed (see the spec).
65
+
66
+ | Field | Type | Notes |
67
+ |---|---|---|
68
+ | `tags` | `string[]` | Free-form; used by list filters |
69
+ | `type` | `normal \| word \| sentence` | Default `normal` |
70
+ | `hint` | `none \| ghost` | Whether to show a faded target underneath |
71
+ | `difficulty` | `1..5` | Optional, informational |
72
+ | `phonetic` | `string` | For `type: word` — IPA / pinyin |
73
+ | `gloss` | `string` | For `type: word` — meaning |
74
+ | `translation` | `string` | For `type: sentence` — target-language equivalent |
75
+
76
+ Example — a `word` card:
77
+
78
+ ```markdown
79
+ ---
80
+ type: word
81
+ phonetic: /ˈklō-zhər/
82
+ gloss: A function that captures its lexical scope
83
+ ---
84
+
85
+ ## Q:
86
+
87
+ closure
88
+
89
+ ## A:
90
+
91
+ closure
92
+ ```
93
+
94
+ ## What gets typed?
95
+
96
+ The **answer** is what the user types. Q is the prompt (never
97
+ typed). Code fences, inline code, and multi-line answers are all
98
+ supported — the typing engine iterates codepoints in the normalised
99
+ answer.
100
+
101
+ Characters the engine can't handle (e.g. surrogate-pair emoji it
102
+ doesn't recognise) show up in `qtype check` output. Fix or remove
103
+ them before publishing.
104
+
105
+ ## Running `qtype check`
106
+
107
+ Before publishing, always run:
108
+
109
+ ```bash
110
+ qtype check
111
+ ```
112
+
113
+ It reports:
114
+ - Missing `material/` directory (fatal)
115
+ - Cards with un-typable characters (fatal, with per-card list)
116
+
117
+ Exit code 0 means safe to publish.
118
+
119
+ ## Publishing
120
+
121
+ ```bash
122
+ qtype publish build
123
+ qtype publish serve # http://localhost:4000
124
+ ```
125
+
126
+ For password-protected decks:
127
+
128
+ ```bash
129
+ qtype publish password set # sets repo config
130
+ qtype publish build # encrypts material on the way out
131
+ ```
132
+
133
+ ## Where the full spec lives
134
+
135
+ Everything above is the 80% case. Rare situations (per-card frontmatter
136
+ overrides, exact whitespace handling, edge cases in the separator
137
+ rule, cross-file references) are in
138
+ [`templates/en/QTYPE.md`](../../templates/en/QTYPE.md).
@@ -0,0 +1,171 @@
1
+ # qtype CLI reference
2
+
3
+ Chinese: [cli-reference.md](../zh/cli-reference.md)
4
+
5
+ Brief overview of every top-level command. Run `qtype <cmd> --help`
6
+ for full options.
7
+
8
+ ## Global options
9
+
10
+ | Option | Description |
11
+ |---|---|
12
+ | `-h, --help` | Show help |
13
+ | `-v, --version` | Show version |
14
+ | `--lang <zh\|en>` | UI language for this invocation |
15
+
16
+ Language precedence: `--lang` > `$QTYPE_LANG` > `cli.lang` in
17
+ `~/.config/qtype/config.json` > system locale.
18
+
19
+ ---
20
+
21
+ ## `qtype init [dir]`
22
+
23
+ Scaffold a material repo in an existing directory. Alias for
24
+ `qtype repo sync` with the same shape.
25
+
26
+ ```bash
27
+ qtype init # in cwd
28
+ qtype init my-deck --lang en # in ./my-deck, English templates
29
+ ```
30
+
31
+ Creates: `QTYPE.md` (authoring spec), `AGENTS.md` / `CLAUDE.md` /
32
+ `GEMINI.md` (symlinks to `QTYPE.md`), `README.md`, `TODO.md`,
33
+ `index.md`, `log.md`, `material/` with an example, `.qtype/`
34
+ skeleton.
35
+
36
+ Idempotent — skips existing user content, overwrites managed spec
37
+ files only with `--force`.
38
+
39
+ ---
40
+
41
+ ## `qtype repo new <name>`
42
+
43
+ Create a fresh material repo directory.
44
+
45
+ ```bash
46
+ qtype repo new my-deck --lang zh
47
+ cd my-deck
48
+ ```
49
+
50
+ ## `qtype repo sync [dir]`
51
+
52
+ Fill in conventions in an existing repo. Same as `qtype init`.
53
+
54
+ ---
55
+
56
+ ## `qtype check [dir]`
57
+
58
+ Validate every card in `material/*.md`. Reports cards containing
59
+ characters the typing engine can't handle (returns non-zero exit).
60
+ Read-only; drop it into CI before `publish build`.
61
+
62
+ ```bash
63
+ qtype check
64
+ ```
65
+
66
+ ---
67
+
68
+ ## `qtype publish build [dir]`
69
+
70
+ Assemble the static site into `dist/`. Runs:
71
+
72
+ 1. Parse every `material/*.md`
73
+ 2. Pre-render Mermaid diagrams (unless `publish.mermaid=off`)
74
+ 3. Encrypt materials (if `publish.password.enabled=true`)
75
+ 4. Emit per-material HTML + `_data/material.json`
76
+ 5. Copy the pre-built view bundle into `dist/_app/`
77
+
78
+ ```bash
79
+ qtype publish build
80
+ ```
81
+
82
+ ## `qtype publish serve [dir]`
83
+
84
+ Local HTTP preview of `dist/`. Defaults to port 4000.
85
+
86
+ ```bash
87
+ qtype publish serve
88
+ qtype publish serve --port 8080
89
+ ```
90
+
91
+ ## `qtype publish init`
92
+
93
+ Write `.gitignore` + a GitHub Pages workflow so pushing to `main`
94
+ triggers a build + deploy.
95
+
96
+ ## `qtype publish password set / clear / status`
97
+
98
+ Password-protection lifecycle. `set` prompts for a password, derives
99
+ an AES key via Argon2id, and stores the resulting salt + hash in
100
+ `.qtype/config.json`. `clear` disables protection.
101
+
102
+ ```bash
103
+ qtype publish password set
104
+ qtype publish password status
105
+ ```
106
+
107
+ ---
108
+
109
+ ## `qtype pack [-o file]`
110
+
111
+ Bundle the current material repo into a `.tar.gz`. Ignores `dist/`,
112
+ `node_modules/`, `.git/`, and any pre-existing tarball.
113
+
114
+ ```bash
115
+ qtype pack -o my-deck.tar.gz
116
+ ```
117
+
118
+ ---
119
+
120
+ ## `qtype content lint | validate | search`
121
+
122
+ - `lint` — style-level pass over material files
123
+ - `validate` — schema-level pass; complements `qtype check`
124
+ - `search <query>` — plain substring search across cards
125
+
126
+ ```bash
127
+ qtype content search "closure"
128
+ ```
129
+
130
+ ---
131
+
132
+ ## `qtype config get / set / unset / list`
133
+
134
+ Git-style two-layer configuration (user layer in
135
+ `~/.config/qtype/config.json`, repo layer in `<repo>/.qtype/config.json`).
136
+
137
+ Read precedence: repo > user > schema default. Writes default to
138
+ repo when the key is a repo-scope key, user when user-only, and
139
+ prefer repo when both.
140
+
141
+ ```bash
142
+ qtype config set cli.lang zh --global
143
+ qtype config set typing.theme mocha
144
+ qtype config get typing.theme --show-origin
145
+ qtype config list
146
+ qtype config unset typing.targetWpm
147
+ ```
148
+
149
+ Known keys include:
150
+
151
+ - `cli.lang`, `cli.color` (user-only)
152
+ - `repo.name`, `publish.password.*` (repo-only)
153
+ - `repo.lang`, `typing.theme`, `typing.targetWpm`, `typing.tabBehavior`,
154
+ `typing.hint`, `typing.showKeyboard`, `typing.showHands`,
155
+ `publish.mermaid` (both scopes)
156
+
157
+ Strict mode: unknown keys are rejected.
158
+
159
+ ---
160
+
161
+ ## `qtype plugin`
162
+
163
+ Command skeleton. Runtime execution not implemented yet — see
164
+ [TODO.md](../../TODO.md).
165
+
166
+ ---
167
+
168
+ ## Exit codes
169
+
170
+ - `0` — success
171
+ - `1` — validation / lint / build failure, or any thrown error