Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ jobs:
- run: npm ci --no-audit --no-fund
- run: npm test
- run: npm run typecheck
- run: npm run lint
# --check builds twice and fails when the two outputs differ
- run: node scripts/build.mjs --check
- name: Package contents
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ jobs:
- run: npm ci --no-audit --no-fund
- run: npm test
- run: npm run typecheck
- run: npm run lint
- run: node scripts/build.mjs --check

- name: Check the tag against package.json
Expand Down
4 changes: 3 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ data is outside this repository.

## Development

Node.js 22 or later. There are no runtime dependencies; the development dependencies are esbuild and TypeScript.
Node.js 22 or later. There are no runtime dependencies; the development dependencies are esbuild, TypeScript and
ESLint.

```sh
npm ci
npm test # unit tests: node --test tests/unit/*.test.mjs
npm run typecheck # type-checks tests/types/ against types/
npm run lint # ESLint's recommended rules (eslint.config.js)
npm run build # dist/: ESM, minified ESM and IIFE bundles with source maps
node scripts/build.mjs --check # builds twice and fails if the outputs differ
```
Expand Down
1 change: 1 addition & 0 deletions README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ npm ci
npm test # unit tests (node --test, synthetic inputs only)
npm run build # browser bundles in dist/
npm run typecheck # checks the type declarations in types/
npm run lint # ESLint (eslint.config.js)
npm run validate-data -- <site dir> [chart id ...] # validates a site against the data format
node scripts/story-author.mjs help # the commands of the story script tool (experimental)
OURNOTES_DATA=<site dir> npm run test:data # opt-in: runs the player in Node on real chart data
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ npm ci
npm test # 单元测试(node --test,仅使用合成输入)
npm run build # 构建 dist/ 下的浏览器包
npm run typecheck # 检查 types/ 下的类型声明
npm run lint # ESLint 检查(eslint.config.js)
npm run validate-data -- <站点目录> [谱面 id ...] # 按数据格式校验一个站点
node scripts/story-author.mjs help # 自写剧情工具(实验功能)的命令
OURNOTES_DATA=<站点目录> npm run test:data # 可选:用真实谱面数据在 Node 中运行播放器
Expand Down
25 changes: 25 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// ESLint: the recommended rules over the sources, scripts, examples and tests (`npm run lint`). The player runs in
// browsers, the scripts and tests in Node.js; the Live2D and Spine runtimes are page scripts that define globals.
// The ports keep the statement order of the code they reproduce, including assignments a later branch overwrites, so
// no-useless-assignment is off.
import js from "@eslint/js";
import globals from "globals";

export default [
{ ignores: ["dist/"] },
js.configs.recommended,
{
languageOptions: {
ecmaVersion: 2024,
sourceType: "module",
globals: {
...globals.browser, ...globals.node,
Live2DCubismCore: "readonly", Live2DCubismMotionSyncCore: "readonly", spine: "readonly",
},
},
rules: {
"no-unused-vars": ["error", { args: "none", caughtErrors: "none", ignoreRestSiblings: true }],
"no-useless-assignment": "off",
},
},
];
3 changes: 1 addition & 2 deletions examples/songs/songs.js
Original file line number Diff line number Diff line change
Expand Up @@ -283,8 +283,7 @@ const main = async () => {
// the route heading: the view's title and what it answers
const renderHead = () => {
const t = u();
const [title, lead] = S.view === "guide" ? [g().title, g().lead]
: [t.heads[S.view], t.lead[S.view](songs.songs.length, rows.length)];
const title = S.view === "guide" ? g().title : t.heads[S.view];
put(head, h("div", { class: "page-head" }, h("div", { class: "page-head-title" }, icon("star", "ic star"), h("h1", {}, title), betaBadge("page-beta-note"),
h("span", { class: "track", "aria-hidden": "true" })), S.view === "guide" && g().reminder ? h("aside", {id:"page-beta-note",class:"guide-reminder",role:"note"}, h("strong",{},g().reminder.title),g().reminder.priority ? h("p",{},h("strong",{},g().reminder.priority)) : null, h("p",{},modelText(g().reminder.text))) : h("span", {id:"page-beta-note",class:"sr-only"},t.betaNote)));
};
Expand Down
10 changes: 0 additions & 10 deletions examples/songs/text.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@ export const UI = {
jackets: "封面", jacketsHint: "显示或隐藏封面", theme: "切换浅色 / 深色", swap: "交换横纵轴",
band: "乐队", difficulty: "难度",
heads: { rank: "谱面排行", charts: "谱面图表" },
lead: {
rank: (songs, charts) => `${songs} 首歌、${charts} 张谱面的效率、活动评级、速度、等级与时长。得分数值按所选游玩场景计算:默认撃奏ライブ、三个区间都是第 1 名(最佳情况),也可切到不开激走的自由 Live,见说明。点任意一行看谱面详情;作词作曲、音频和谱面预览见 moenotes。`,
charts: (songs, charts) => `${songs} 首歌、${charts} 张谱面的分布。散点图的两轴可任选,点任意一点看谱面详情。`,
},
empty: "没有符合条件的谱面", emptyHint: "当前的乐队、难度、搜索词或“只看前沿”把所有谱面都排除了。", clear: "清除筛选",
diff: { easy: "Easy", normal: "Normal", hard: "Hard", expert: "Expert" },
rankBy: {
Expand Down Expand Up @@ -132,10 +128,6 @@ export const UI = {
jackets: "Jackets", jacketsHint: "Show or hide the jackets", theme: "Light / dark", swap: "Swap the axes",
band: "Band", difficulty: "Difficulty",
heads: { rank: "Chart rankings", charts: "Chart figures" },
lead: {
rank: (songs, charts) => `Efficiency, event ranks, speed, level and length of ${songs} songs and ${charts} charts. Score figures follow the play scenario chosen: by default Gekisou Live at rank 1 in all three ranges (the best case), or Free Live without Gekisou; see the guide. Open any row for the chart's details; credits, audio and chart previews are on moenotes.`,
charts: (songs, charts) => `How ${songs} songs and ${charts} charts spread. Pick any two figures for the scatter; open any point for the chart's details.`,
},
empty: "No chart matches", emptyHint: "The band, difficulties, search or “frontier only” leave out every chart.", clear: "Clear filters",
diff: { easy: "Easy", normal: "Normal", hard: "Hard", expert: "Expert" },
rankBy: {
Expand Down Expand Up @@ -354,7 +346,6 @@ export const REPLAY_UI = {
export const GUIDE = {
"zh": {
"title": "定义、推导与成立条件",
"lead": "排行与适性展示理论最佳打法的统计参考;谱面详情还可逐音符计算给定输入的一局。新 chart-stats/3 使用标称概率期望与计算包围区间,旧 music-data/1 的采样统计仍可读取。两者使用同一套 ournotes-deck Rust 模型,原生逐帧核对的案例见下表。",
"sections": [
{
"title": "得分模型",
Expand Down Expand Up @@ -639,7 +630,6 @@ export const GUIDE = {
},
"en": {
"title": "Definitions, derivations and conditions",
"lead": "Rankings and aptitude show statistical references for theoretical best play. Chart details can also calculate one play from per-note inputs. New chart-stats/3 data uses nominal expectations and numerical enclosures; legacy music-data/1 samples remain readable. Both use the same ournotes-deck Rust model; the native frame checks are listed below.",
"sections": [
{
"title": "Score model",
Expand Down
Loading
Loading