@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.
- package/CHANGELOG.md +97 -0
- package/CHANGELOG.zh.md +82 -0
- package/LICENSE +201 -0
- package/README.md +113 -0
- package/README.zh.md +114 -0
- package/dist/cli/bootstrap.js +190 -0
- package/dist/cli/commands/check/action.js +56 -0
- package/dist/cli/commands/check/i18n.js +26 -0
- package/dist/cli/commands/check/index.js +20 -0
- package/dist/cli/commands/config/action.js +236 -0
- package/dist/cli/commands/config/i18n.js +64 -0
- package/dist/cli/commands/config/index.js +72 -0
- package/dist/cli/commands/content/index.js +22 -0
- package/dist/cli/commands/content/lint/action.js +102 -0
- package/dist/cli/commands/content/lint/i18n.js +32 -0
- package/dist/cli/commands/content/lint/index.js +19 -0
- package/dist/cli/commands/content/search/action.js +64 -0
- package/dist/cli/commands/content/search/i18n.js +20 -0
- package/dist/cli/commands/content/search/index.js +24 -0
- package/dist/cli/commands/content/validate/action.js +145 -0
- package/dist/cli/commands/content/validate/i18n.js +32 -0
- package/dist/cli/commands/content/validate/index.js +19 -0
- package/dist/cli/commands/index.js +19 -0
- package/dist/cli/commands/init/index.js +53 -0
- package/dist/cli/commands/pack/i18n.js +20 -0
- package/dist/cli/commands/pack/index.js +48 -0
- package/dist/cli/commands/plugin/i18n.js +28 -0
- package/dist/cli/commands/plugin/index.js +41 -0
- package/dist/cli/commands/publish/build/action.js +208 -0
- package/dist/cli/commands/publish/build/i18n.js +50 -0
- package/dist/cli/commands/publish/build/index.js +24 -0
- package/dist/cli/commands/publish/build/mermaidPass.js +93 -0
- package/dist/cli/commands/publish/build/mermaidPrerender.js +199 -0
- package/dist/cli/commands/publish/index.js +21 -0
- package/dist/cli/commands/publish/init/action.js +74 -0
- package/dist/cli/commands/publish/init/i18n.js +22 -0
- package/dist/cli/commands/publish/init/index.js +26 -0
- package/dist/cli/commands/publish/password/action.js +158 -0
- package/dist/cli/commands/publish/password/i18n.js +44 -0
- package/dist/cli/commands/publish/password/index.js +35 -0
- package/dist/cli/commands/publish/serve/action.js +117 -0
- package/dist/cli/commands/publish/serve/i18n.js +32 -0
- package/dist/cli/commands/publish/serve/index.js +37 -0
- package/dist/cli/commands/repo/index.js +19 -0
- package/dist/cli/commands/repo/new/action.js +98 -0
- package/dist/cli/commands/repo/new/i18n.js +46 -0
- package/dist/cli/commands/repo/new/index.js +57 -0
- package/dist/cli/commands/repo/sync/action.js +202 -0
- package/dist/cli/commands/repo/sync/i18n.js +44 -0
- package/dist/cli/commands/repo/sync/index.js +47 -0
- package/dist/cli/commands/repo/templates.js +87 -0
- package/dist/cli/i18n/index.js +141 -0
- package/dist/cli/i18n/shared.js +142 -0
- package/dist/cli/index.js +79 -0
- package/dist/cli/registry/command.js +1 -0
- package/dist/cli/registry/commander-i18n.js +76 -0
- package/dist/cli/registry/help.js +142 -0
- package/dist/cli/registry/mount.js +71 -0
- package/dist/cli/utils/output.js +92 -0
- package/dist/cli/utils/paths.js +13 -0
- package/dist/cli/utils/prompt.js +72 -0
- package/dist/cli/utils/repoGuard.js +41 -0
- package/dist/core/encrypt.js +113 -0
- package/dist/core/index.js +8 -0
- package/dist/core/keystroke/asciiMap.js +39 -0
- package/dist/core/keystroke/chineseMap.js +32 -0
- package/dist/core/keystroke/fullwidthMap.js +41 -0
- package/dist/core/keystroke/index.js +22 -0
- package/dist/core/keystroke/textNormalize.js +68 -0
- package/dist/core/keystroke/types.js +1 -0
- package/dist/core/parser/frontmatter.js +58 -0
- package/dist/core/parser/index.js +2 -0
- package/dist/core/parser/parseMaterial.js +42 -0
- package/dist/core/types.js +1 -0
- package/dist/core/typing/engine.js +112 -0
- package/dist/core/typing/index.js +2 -0
- package/dist/core/typing/metrics.js +20 -0
- package/dist/store/index.js +44 -0
- package/dist/store/kv.js +276 -0
- package/dist/store/storage/browser.js +61 -0
- package/dist/store/storage/json.js +113 -0
- package/dist/store/storage/memory.js +24 -0
- package/dist/store/types.js +47 -0
- package/dist/store/validators.js +71 -0
- package/dist/view/assets/index-DBABH3dG.js +111 -0
- package/dist/view/assets/index-RTLZ-zOC.css +1 -0
- package/dist/view/index.html +14 -0
- package/dist/view/qtype-logo.svg +49 -0
- package/dist/view/qtype-mark.svg +21 -0
- package/docs/README.md +23 -0
- package/docs/README.zh.md +20 -0
- package/docs/en/authoring.md +138 -0
- package/docs/en/cli-reference.md +171 -0
- package/docs/zh/authoring.md +131 -0
- package/docs/zh/cli-reference.md +162 -0
- package/package.json +85 -0
- package/templates/en/QTYPE.md +618 -0
- package/templates/en/README.md +64 -0
- package/templates/en/TODO.md +7 -0
- package/templates/en/index.md +7 -0
- package/templates/en/log.md +3 -0
- package/templates/en/material/.gitkeep +0 -0
- package/templates/en/material/example.md +47 -0
- package/templates/zh/QTYPE.md +618 -0
- package/templates/zh/README.md +64 -0
- package/templates/zh/TODO.md +7 -0
- package/templates/zh/index.md +7 -0
- package/templates/zh/log.md +3 -0
- package/templates/zh/material/.gitkeep +0 -0
- 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
|