Skip to content

Commit 4e76117

Browse files
committed
refactor(project structure): restructure project to feature-driven architecture
- Move release sorter core logic to features/release-sorter/core.ts - Add Vue composable for release sorter state management - Relocate ScrollToTopButton to features/scroll-to-top/ - Move shared components and styles to shared/ directory - Update import paths across the codebase - Replace deprecated biome lint/format commands with biome check - Switch from pnpm to bun in README and scripts - Remove unused Vite and Vue SVG assets - Update AGENTS.md and README.md to document new architecture
1 parent a24315c commit 4e76117

15 files changed

Lines changed: 182 additions & 90 deletions

File tree

AGENTS.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,32 @@ No test framework. Run `bun run lint` and `bun run build` (includes type-check)
1616
- **Package manager**: bun (lockfile: `bun.lock`). There is also a `pnpm-lock.yaml` — ignore it.
1717
- **Vue is external**: bundled via CDN (`vue.global.prod.js`), not in the output JS. Do not add Vue to the build bundle.
1818
- **Userscript grants**: `GM_addStyle`, `GM_getValue`, `GM_setValue` — declared in `vite.config.ts`. These are the only GM APIs available.
19-
- **Release sorter is DOM-based**: `initReleaseSorter()` in `src/utils/release-sorter.ts` runs before Vue mounts and operates on raw DOM. Vue components call `sortAndHighlight()` to trigger re-sorting after preference changes.
19+
- **Release sorter is DOM-based**: `initReleaseSorter()` in `src/features/release-sorter/core.ts` runs before Vue mounts and operates on raw DOM. Vue components call `sortAndHighlight()` to trigger re-sorting after preference changes.
2020
- **GitHub Turbo navigation**: The script listens to `turbo:load` events and uses a `MutationObserver` on `document.body` to re-run sorting on SPA navigation. Both use 100ms debounce.
2121
- **Biome overrides for `.vue` files**: `noUnusedVariables` and `noUnusedImports` are disabled for Vue SFCs (Biome can't parse Vue templates properly).
2222

23+
## Project Structure
24+
25+
```
26+
src/
27+
├── features/
28+
│ ├── scroll-to-top/
29+
│ │ └── ScrollToTopButton.vue
30+
│ └── release-sorter/
31+
│ ├── ReleaseSorterSettings.vue
32+
│ ├── core.ts # DOM manipulation, sorting logic
33+
│ ├── composable.ts # Vue-facing exports, types, constants
34+
│ └── index.ts # Public API barrel export
35+
├── shared/
36+
│ ├── components/
37+
│ │ └── SettingsPanel.vue
38+
│ └── styles/
39+
│ └── global.css
40+
├── App.vue
41+
├── main.ts
42+
└── vite-env.d.ts
43+
```
44+
2345
## Code style
2446

2547
- Tabs for indent, double quotes (enforced by Biome)

README.md

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,30 +17,39 @@
1717

1818
```bash
1919
# 安装依赖
20-
pnpm install
20+
bun install
2121

2222
# 启动开发服务器(自动注入用户脚本到浏览器)
23-
pnpm dev
23+
bun dev
2424

25-
# 构建生产版本
26-
pnpm build
25+
# 构建生产版本(含类型检查)
26+
bun run build
2727

28-
# 代码检查 & 格式化
29-
pnpm lint
30-
pnpm format
28+
# 代码检查
29+
bun run lint
30+
31+
# 代码格式化
32+
bun run format
3133
```
3234

3335
## 项目结构
3436

3537
```
3638
src/
37-
├── main.ts # 入口,初始化 release sorter 并挂载 Vue
38-
├── App.vue # 根组件
39-
├── style.css # 全局样式 + 高亮样式
40-
├── components/
41-
│ ├── ScrollToTopButton.vue # 返回顶部按钮
42-
│ ├── SettingsPanel.vue # 通用设置面板(齿轮按钮 + 弹出面板容器)
43-
│ └── ReleaseSorterSettings.vue # Release 排序设置内容
44-
└── utils/
45-
└── release-sorter.ts # 排序/高亮核心逻辑与存储
39+
├── main.ts # 入口,初始化 release sorter 并挂载 Vue
40+
├── App.vue # 根组件
41+
├── vite-env.d.ts # Vite 类型声明
42+
├── features/
43+
│ ├── scroll-to-top/
44+
│ │ └── ScrollToTopButton.vue # 返回顶部按钮(含滚动进度环)
45+
│ └── release-sorter/
46+
│ ├── ReleaseSorterSettings.vue # Release 排序设置 UI
47+
│ ├── core.ts # DOM 操作、排序逻辑、存储
48+
│ ├── composable.ts # Vue composable 及类型导出
49+
│ └── index.ts # 公共 API 导出
50+
└── shared/
51+
├── components/
52+
│ └── SettingsPanel.vue # 通用设置面板(齿轮按钮 + 弹出容器)
53+
└── styles/
54+
└── global.css # 全局样式 + 高亮样式
4655
```

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
"dev": "vite",
1515
"build": "vue-tsc -b && vite build",
1616
"preview": "vite preview",
17-
"lint": "biome lint .",
18-
"format": "biome format --write ."
17+
"lint": "biome check .",
18+
"format": "biome check --write ."
1919
},
2020
"dependencies": {
2121
"vue": "^3.5.38"

src/App.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script setup lang="ts">
2-
import ScrollToTopButton from "./components/ScrollToTopButton.vue";
3-
import SettingsPanel from "./components/SettingsPanel.vue";
4-
import ReleaseSorterSettings from "./components/ReleaseSorterSettings.vue";
2+
import ReleaseSorterSettings from "./features/release-sorter/ReleaseSorterSettings.vue";
3+
import ScrollToTopButton from "./features/scroll-to-top/ScrollToTopButton.vue";
4+
import SettingsPanel from "./shared/components/SettingsPanel.vue";
55
</script>
66

77
<template>

src/assets/vite.svg

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/assets/vue.svg

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/components/ReleaseSorterSettings.vue renamed to src/features/release-sorter/ReleaseSorterSettings.vue

Lines changed: 20 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,27 @@
11
<script setup lang="ts">
2-
import { ref, computed } from "vue";
32
import {
4-
OS_OPTIONS,
5-
OS_LABELS,
6-
ARCH_OPTIONS,
73
ARCH_LABELS,
8-
PKG_OPTIONS,
9-
PKG_LABELS,
10-
type OsOption,
11-
type ArchOption,
12-
type PkgOption,
13-
getSelectedOs,
14-
setSelectedOs,
15-
getSelectedArch,
16-
setSelectedArch,
17-
getSelectedPkg,
18-
setSelectedPkg,
19-
detectPlatform,
4+
ARCH_OPTIONS,
205
detectArch,
21-
sortAndHighlight,
22-
getKeywordsPreview,
23-
} from "../utils/release-sorter";
24-
25-
const selectedOs = ref<OsOption>(getSelectedOs());
26-
const selectedArch = ref<ArchOption>(getSelectedArch());
27-
const selectedPkg = ref<PkgOption>(getSelectedPkg());
28-
29-
const resolvedPlatform = computed(() => {
30-
return selectedOs.value === "auto" ? detectPlatform() : selectedOs.value;
31-
});
32-
33-
const resolvedArch = computed(() => {
34-
return selectedArch.value === "auto" ? detectArch() : selectedArch.value;
35-
});
36-
37-
const keywords = computed(() => {
38-
return getKeywordsPreview(
39-
selectedOs.value,
40-
selectedArch.value,
41-
selectedPkg.value,
42-
);
43-
});
44-
45-
function selectOs(os: OsOption) {
46-
selectedOs.value = os;
47-
setSelectedOs(os);
48-
sortAndHighlight();
49-
}
50-
51-
function selectArch(arch: ArchOption) {
52-
selectedArch.value = arch;
53-
setSelectedArch(arch);
54-
sortAndHighlight();
55-
}
56-
57-
function selectPkg(pkg: PkgOption) {
58-
selectedPkg.value = pkg;
59-
setSelectedPkg(pkg);
60-
sortAndHighlight();
61-
}
6+
detectPlatform,
7+
OS_LABELS,
8+
OS_OPTIONS,
9+
PKG_LABELS,
10+
PKG_OPTIONS,
11+
useReleaseSorter,
12+
} from "./composable";
13+
14+
const {
15+
selectedOs,
16+
selectedArch,
17+
selectedPkg,
18+
resolvedPlatform,
19+
resolvedArch,
20+
keywords,
21+
selectOs,
22+
selectArch,
23+
selectPkg,
24+
} = useReleaseSorter();
6225
</script>
6326

6427
<template>
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { computed, ref } from "vue";
2+
import {
3+
type ArchOption,
4+
detectArch,
5+
detectPlatform,
6+
getKeywordsPreview,
7+
getSelectedArch,
8+
getSelectedOs,
9+
getSelectedPkg,
10+
type OsOption,
11+
type PkgOption,
12+
setSelectedArch,
13+
setSelectedOs,
14+
setSelectedPkg,
15+
sortAndHighlight,
16+
} from "./core";
17+
18+
export {
19+
ARCH_LABELS,
20+
ARCH_OPTIONS,
21+
type ArchOption,
22+
detectArch,
23+
detectPlatform,
24+
getKeywordsPreview,
25+
getSelectedArch,
26+
getSelectedOs,
27+
getSelectedPkg,
28+
isReleasePage,
29+
OS_LABELS,
30+
OS_OPTIONS,
31+
type OsOption,
32+
PKG_LABELS,
33+
PKG_OPTIONS,
34+
type PkgOption,
35+
setSelectedArch,
36+
setSelectedOs,
37+
setSelectedPkg,
38+
sortAndHighlight,
39+
} from "./core";
40+
41+
export function useReleaseSorter() {
42+
const selectedOs = ref<OsOption>(getSelectedOs());
43+
const selectedArch = ref<ArchOption>(getSelectedArch());
44+
const selectedPkg = ref<PkgOption>(getSelectedPkg());
45+
46+
const resolvedPlatform = computed(() => {
47+
return selectedOs.value === "auto" ? detectPlatform() : selectedOs.value;
48+
});
49+
50+
const resolvedArch = computed(() => {
51+
return selectedArch.value === "auto" ? detectArch() : selectedArch.value;
52+
});
53+
54+
const keywords = computed(() => {
55+
return getKeywordsPreview(
56+
selectedOs.value,
57+
selectedArch.value,
58+
selectedPkg.value,
59+
);
60+
});
61+
62+
function selectOs(os: OsOption) {
63+
selectedOs.value = os;
64+
setSelectedOs(os);
65+
sortAndHighlight();
66+
}
67+
68+
function selectArch(arch: ArchOption) {
69+
selectedArch.value = arch;
70+
setSelectedArch(arch);
71+
sortAndHighlight();
72+
}
73+
74+
function selectPkg(pkg: PkgOption) {
75+
selectedPkg.value = pkg;
76+
setSelectedPkg(pkg);
77+
sortAndHighlight();
78+
}
79+
80+
return {
81+
selectedOs,
82+
selectedArch,
83+
selectedPkg,
84+
resolvedPlatform,
85+
resolvedArch,
86+
keywords,
87+
selectOs,
88+
selectArch,
89+
selectPkg,
90+
};
91+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export { useReleaseSorter } from "./composable";
2+
export type { ArchOption, OsOption, PkgOption } from "./core";
3+
export { initReleaseSorter, isReleasePage, sortAndHighlight } from "./core";

0 commit comments

Comments
 (0)