@qnroa/qtype 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +43 -0
- package/CHANGELOG.zh.md +35 -0
- package/dist/cli/commands/publish/build/action.js +49 -0
- package/dist/view/assets/{index-CRh4USfC.js → index-1XXyxiQO.js} +28 -28
- package/dist/view/assets/{index-ByMJp0a5.js → index-DIBSxFYl.js} +2 -2
- package/dist/view/assets/{index-JNx7Ohoj.js → index-DgHvnMV9.js} +1 -1
- package/dist/view/index.html +2 -2
- package/docs/en/settings.md +2 -2
- package/docs/zh/settings.md +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -13,6 +13,49 @@ can land in any minor bump (`0.x.0`).
|
|
|
13
13
|
|
|
14
14
|
## [Unreleased]
|
|
15
15
|
|
|
16
|
+
## [0.0.4] - 2026-08-22
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- **Site name in browser tab + header** — `qtype publish build` now
|
|
21
|
+
writes the site's name into `dist/index.html` (both the `<title>`
|
|
22
|
+
and a `data-qtype-repo-name` attribute on `<html>`). The view reads
|
|
23
|
+
it via `readBootConfig()` so the Header brand label reflects the
|
|
24
|
+
actual deck instead of a hardcoded "qtype". Precedence:
|
|
25
|
+
`repo.name` in `.qtype/config.json` wins, otherwise the repo
|
|
26
|
+
directory basename.
|
|
27
|
+
- **HUD is present from t=0** — the pause / hint / keyboard toggles
|
|
28
|
+
are now reachable the moment the typing page mounts, not after the
|
|
29
|
+
first keystroke. `useHudBridge` derives an initial `TypingState` +
|
|
30
|
+
`TypingMetrics` from the engine on mount; arena's `onStateChange`
|
|
31
|
+
push still overrides once real events fire.
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
|
|
35
|
+
- **Default sound settings** — `qtype.keySoundEnabled` and
|
|
36
|
+
`qtype.errorSoundEnabled` now default to `true` for new users. Both
|
|
37
|
+
were `false` before; existing users' saved preferences are
|
|
38
|
+
untouched.
|
|
39
|
+
- **Default AFK threshold** raised from 15 s to 60 s so a short pause
|
|
40
|
+
to think doesn't cost the flow. Also clarified the settings
|
|
41
|
+
description (mouse hover without movement counts as AFK; tab hidden
|
|
42
|
+
/ window blur pauses immediately).
|
|
43
|
+
|
|
44
|
+
### Fixed
|
|
45
|
+
|
|
46
|
+
- **`typing.$file.tsx` no longer synthesises HUD placeholder state**
|
|
47
|
+
in-line — the fix lives in `useHudBridge` where it belongs. Removes
|
|
48
|
+
the risk of the placeholder drifting from arena's real derivation.
|
|
49
|
+
- **`deriveAesKey` returns an extractable AES key** so the view can
|
|
50
|
+
cache the derived bytes in sessionStorage — a same-tab reload no
|
|
51
|
+
longer re-prompts for the password. Regression test added under
|
|
52
|
+
`__tests__/core/encrypt.test.ts`.
|
|
53
|
+
- **Docs link cleanup** — all internal `.md` links in the npm-shipped
|
|
54
|
+
files now go through `https://www.npmjs.com/package/@qnroa/qtype?activeTab=code&filepath=...`
|
|
55
|
+
URLs so users on npm can click through even when the source repo
|
|
56
|
+
is private. Removed dead links to `CONTRIBUTING.md` /
|
|
57
|
+
`architecture.md` / `TODO.md` since those aren't shipped.
|
|
58
|
+
|
|
16
59
|
## [0.0.1] - 2026-08-16
|
|
17
60
|
|
|
18
61
|
Initial development release. Everything below is the baseline shipped
|
package/CHANGELOG.zh.md
CHANGED
|
@@ -12,6 +12,41 @@ English: [CHANGELOG.md](https://www.npmjs.com/package/@qnroa/qtype?activeTab=cod
|
|
|
12
12
|
|
|
13
13
|
## [Unreleased]
|
|
14
14
|
|
|
15
|
+
## [0.0.4] - 2026-08-22
|
|
16
|
+
|
|
17
|
+
### 新增
|
|
18
|
+
|
|
19
|
+
- **浏览器 tab 标题 + 左上角显示站点名** —— `qtype publish build`
|
|
20
|
+
会把站点名写进 `dist/index.html`(同时改 `<title>` 和 `<html>` 上的
|
|
21
|
+
`data-qtype-repo-name` 属性)。view 通过 `readBootConfig()` 读它,
|
|
22
|
+
Header 的品牌位显示当前 deck 名而不是硬编码的 "qtype"。取值优先级:
|
|
23
|
+
`.qtype/config.json` 里的 `repo.name` > 仓库目录名。
|
|
24
|
+
- **HUD 一进打字页就显示** —— 暂停 / 提示 / 键盘切换按钮从打字页
|
|
25
|
+
挂载起就可用,不再需要等用户敲第一下。`useHudBridge` 在 mount 时
|
|
26
|
+
就从 engine 派生初始 `TypingState` + `TypingMetrics`;arena 的
|
|
27
|
+
`onStateChange` 推送开始后无缝覆盖。
|
|
28
|
+
|
|
29
|
+
### 变更
|
|
30
|
+
|
|
31
|
+
- **默认打开按键音和错误音** —— `qtype.keySoundEnabled` 和
|
|
32
|
+
`qtype.errorSoundEnabled` 对新用户默认改为 `true`(之前都是
|
|
33
|
+
`false`)。已有用户的设置不受影响。
|
|
34
|
+
- **AFK 默认阈值** 从 15 秒调到 60 秒,给思考一点空间。同时把设置
|
|
35
|
+
说明写清楚:鼠标悬停不动也算 AFK;切 tab 或窗口失焦会立即暂停。
|
|
36
|
+
|
|
37
|
+
### 修复
|
|
38
|
+
|
|
39
|
+
- **`typing.$file.tsx` 不再手写 HUD 初始状态** —— 修复搬到该在的
|
|
40
|
+
`useHudBridge`。避免手写值和 arena 真实派生逻辑漂移。
|
|
41
|
+
- **`deriveAesKey` 返回可导出的 AES 密钥**,view 端才能把派生结果
|
|
42
|
+
存进 sessionStorage —— 同 tab 刷新不再重问密码。回归测试见
|
|
43
|
+
`__tests__/core/encrypt.test.ts`。
|
|
44
|
+
- **文档链接整改** —— npm 发布的文件里所有内部 `.md` 链接都改成
|
|
45
|
+
`https://www.npmjs.com/package/@qnroa/qtype?activeTab=code&filepath=...`
|
|
46
|
+
形式的 npm 链接,私仓状态下 npm 用户点了也能看到内容。删掉了指向
|
|
47
|
+
`CONTRIBUTING.md` / `architecture.md` / `TODO.md` 这些不发布文件
|
|
48
|
+
的死链。
|
|
49
|
+
|
|
15
50
|
## [0.0.1] - 2026-08-16
|
|
16
51
|
|
|
17
52
|
首个开发版。以下是这个版本的基线内容。
|
|
@@ -42,6 +42,10 @@ export async function publishBuildAction(opts = {}) {
|
|
|
42
42
|
const encryptCtx = await resolveEncryptCtx(cwd);
|
|
43
43
|
const mermaidMode = await resolveMermaidMode(cwd);
|
|
44
44
|
const repoLang = await resolveRepoLang(cwd);
|
|
45
|
+
// Wiki display name: repo.name from config wins over the directory
|
|
46
|
+
// basename, so users can `qtype config set repo.name "..."` to
|
|
47
|
+
// override how the site labels itself in the browser tab.
|
|
48
|
+
const name = await resolveRepoName(cwd);
|
|
45
49
|
const materials = [];
|
|
46
50
|
for (const file of walkMaterial(cwd)) {
|
|
47
51
|
const body = readMaterial(file);
|
|
@@ -64,6 +68,11 @@ export async function publishBuildAction(opts = {}) {
|
|
|
64
68
|
fs.rmSync(outDir, { recursive: true, force: true });
|
|
65
69
|
fs.mkdirSync(outDir, { recursive: true });
|
|
66
70
|
copyDir(viewBundle, outDir);
|
|
71
|
+
// Rewrite dist/index.html to carry the site's name — both in the
|
|
72
|
+
// <title> (browser tab) and as a `data-qtype-repo-name` attribute on
|
|
73
|
+
// <html> (read by the view's `readBootConfig()` for the header
|
|
74
|
+
// brand label and anywhere else the repo name is shown).
|
|
75
|
+
rewriteIndexHtml(path.join(outDir, 'index.html'), name);
|
|
67
76
|
// Defensive: clear any stale material/ that might have snuck into the
|
|
68
77
|
// view bundle. Since dev-seed content lives at repo root's examples/
|
|
69
78
|
// (served only by the vite dev middleware, not the bundle), this is a
|
|
@@ -107,6 +116,14 @@ export async function publishBuildAction(opts = {}) {
|
|
|
107
116
|
fs.writeFileSync(path.join(targetMaterial, 'mermaid.css'), mermaid.css);
|
|
108
117
|
}
|
|
109
118
|
}
|
|
119
|
+
// Copy `material/assets/**` verbatim (images referenced by cards
|
|
120
|
+
// via `` need to actually resolve at the
|
|
121
|
+
// deployed URL). Assets are NOT encrypted — they're image bytes,
|
|
122
|
+
// and the encryption pass only wraps `.md` content anyway.
|
|
123
|
+
const srcAssets = path.join(cwd, MATERIAL_DIR, 'assets');
|
|
124
|
+
if (fs.existsSync(srcAssets)) {
|
|
125
|
+
copyDir(srcAssets, path.join(targetMaterial, 'assets'));
|
|
126
|
+
}
|
|
110
127
|
// Manifest: lists materials + optional encryption envelope + mermaid mode.
|
|
111
128
|
const manifest = { materials: entries };
|
|
112
129
|
if (encryptCtx) {
|
|
@@ -186,6 +203,38 @@ async function resolveRepoLang(cwd) {
|
|
|
186
203
|
return 'zh-CN';
|
|
187
204
|
return 'en-US';
|
|
188
205
|
}
|
|
206
|
+
/**
|
|
207
|
+
* Repo display name: `repo.name` from `.qtype/config.json` wins over
|
|
208
|
+
* the directory basename, so users can override without renaming the
|
|
209
|
+
* folder. Same rule as qrism's `loadPublishConfig`.
|
|
210
|
+
*/
|
|
211
|
+
async function resolveRepoName(cwd) {
|
|
212
|
+
const kv = createRepoKv(cwd);
|
|
213
|
+
if (await kv.available()) {
|
|
214
|
+
const v = await kv.get('repo.name');
|
|
215
|
+
if (typeof v === 'string' && v.trim())
|
|
216
|
+
return v.trim();
|
|
217
|
+
}
|
|
218
|
+
return path.basename(cwd);
|
|
219
|
+
}
|
|
220
|
+
function rewriteIndexHtml(indexPath, name) {
|
|
221
|
+
if (!fs.existsSync(indexPath))
|
|
222
|
+
return;
|
|
223
|
+
const html = fs.readFileSync(indexPath, 'utf8');
|
|
224
|
+
const escaped = name
|
|
225
|
+
.replace(/&/g, '&')
|
|
226
|
+
.replace(/</g, '<')
|
|
227
|
+
.replace(/>/g, '>')
|
|
228
|
+
.replace(/"/g, '"');
|
|
229
|
+
const withTitle = html.replace(/<title>[^<]*<\/title>/, `<title>${escaped}</title>`);
|
|
230
|
+
// Add or replace data-qtype-repo-name on the root <html> tag.
|
|
231
|
+
// Two branches so a re-run cleanly overwrites an existing attribute.
|
|
232
|
+
const rewritten = /<html\b[^>]*\bdata-qtype-repo-name=/.test(withTitle)
|
|
233
|
+
? withTitle.replace(/(<html\b[^>]*\bdata-qtype-repo-name=)"[^"]*"/, `$1"${escaped}"`)
|
|
234
|
+
: withTitle.replace(/<html\b/, `<html data-qtype-repo-name="${escaped}"`);
|
|
235
|
+
if (rewritten !== html)
|
|
236
|
+
fs.writeFileSync(indexPath, rewritten);
|
|
237
|
+
}
|
|
189
238
|
async function obtainPassword() {
|
|
190
239
|
const envPwd = process.env.QTYPE_PUBLISH_PASSWORD;
|
|
191
240
|
if (envPwd)
|