11<script setup lang="ts">
22import { useData } from " vitepress" ;
3- import { computed , ref , watch , onMounted } from " vue" ;
3+ import { computed , ref } from " vue" ;
44import {
55 downloadTranslations ,
66 type DownloadKey ,
@@ -16,94 +16,6 @@ const t = computed(
1616const version = ref (ghdata .version );
1717const copied = ref <string | null >(null );
1818const useMirror = ref (false );
19- const useNightly = ref (false );
20-
21- // Nightly 配置
22- const NIGHTLY_OWNER = " LanRhyme" ;
23- const NIGHTLY_REPO = " MicYou" ;
24- const NIGHTLY_WORKFLOW = " development.yml" ;
25-
26- // 存储 nightly run 和 artifacts 信息
27- const nightlyRunId = ref <number | null >(null );
28- const nightlyArtifacts = ref <Map <string , string >>(new Map ());
29- const nightlyLoading = ref (false );
30- const nightlyError = ref <string | null >(null );
31-
32- // GitHub API 获取最新成功 run
33- async function fetchLatestNightlyRun() {
34- if (! useNightly .value ) return ;
35- nightlyLoading .value = true ;
36- nightlyError .value = null ;
37-
38- try {
39- // 获取最新成功的 workflow run
40- const runsUrl = ` https://api.github.com/repos/${NIGHTLY_OWNER }/${NIGHTLY_REPO }/actions/workflows/${NIGHTLY_WORKFLOW }/runs?status=success&per_page=1 ` ;
41- const runsRes = await fetch (runsUrl );
42- if (runsRes .status === 403 )
43- throw new Error (" GitHub API rate limit exceeded" );
44- if (runsRes .status === 401 )
45- throw new Error (" GitHub API authentication failed" );
46- if (! runsRes .ok )
47- throw new Error (
48- ` GitHub API error: HTTP ${runsRes .status } ${runsRes .statusText } ` ,
49- );
50- const runsData = await runsRes .json ();
51-
52- if (! runsData .workflow_runs ?.length ) {
53- throw new Error (" No successful runs found" );
54- }
55-
56- const runId = runsData .workflow_runs [0 ].id ;
57- nightlyRunId .value = runId ;
58-
59- // 获取该 run 的 artifacts
60- const artifactsUrl = ` https://api.github.com/repos/${NIGHTLY_OWNER }/${NIGHTLY_REPO }/actions/runs/${runId }/artifacts ` ;
61- const artifactsRes = await fetch (artifactsUrl );
62- if (artifactsRes .status === 403 )
63- throw new Error (" GitHub API rate limit exceeded" );
64- if (artifactsRes .status === 401 )
65- throw new Error (" GitHub API authentication failed" );
66- if (! artifactsRes .ok )
67- throw new Error (
68- ` GitHub API error: HTTP ${artifactsRes .status } ${artifactsRes .statusText } ` ,
69- );
70- const artifactsData = await artifactsRes .json ();
71-
72- // 构建 artifact 名称映射 (pattern prefix -> actual name)
73- const map = new Map <string , string >();
74- for (const artifact of artifactsData .artifacts || []) {
75- const name = artifact .name ;
76- map .set (name , name );
77- }
78- nightlyArtifacts .value = map ;
79- } catch (e ) {
80- nightlyError .value = e instanceof Error ? e .message : " Unknown error" ;
81- console .error (" Failed to fetch nightly info:" , e );
82- } finally {
83- nightlyLoading .value = false ;
84- }
85- }
86-
87- // 监听 nightly 模式切换
88- watch (useNightly , (newVal ) => {
89- if (newVal ) fetchLatestNightlyRun ();
90- });
91-
92- // 初始化时如果 nightly 已开启则加载
93- onMounted (() => {
94- if (useNightly .value ) fetchLatestNightlyRun ();
95- });
96-
97- // 正则匹配 artifact 名称
98- function findArtifact(pattern : string ): string | null {
99- const regex = new RegExp (
100- ` ^${pattern .replace (" {version}" , " \\ d+\\ .\\ d+\\ .\\ d+" )}$ ` ,
101- );
102- for (const [name] of nightlyArtifacts .value ) {
103- if (regex .test (name )) return name ;
104- }
105- return null ;
106- }
10719
10820const platforms: {
10921 name: string ;
@@ -113,7 +25,6 @@ const platforms: {
11325 name: DownloadKey ;
11426 pattern? : string ;
11527 copy? : string ;
116- nightlyPattern? : string ;
11728 }[];
11829}[] = [
11930 {
@@ -124,17 +35,14 @@ const platforms: {
12435 {
12536 name: " installer" ,
12637 pattern: " MicYou-Win-{version}-installer.exe" ,
127- nightlyPattern: " MicYou-Win-{version}" ,
12838 },
12939 {
13040 name: " portableJRE" ,
13141 pattern: " MicYou-Win-{version}.zip" ,
132- nightlyPattern: " MicYou-Win-{version}" ,
13342 },
13443 {
13544 name: " portableNoJRE" ,
13645 pattern: " MicYou-Win-NoJRE-{version}.zip" ,
137- nightlyPattern: " MicYou-Win-NoJRE-{version}" ,
13846 },
13947 ],
14048 },
@@ -146,17 +54,14 @@ const platforms: {
14654 {
14755 name: " dmgArm" ,
14856 pattern: " MicYou-macOS-{version}-arm64.dmg" ,
149- nightlyPattern: " MicYou-macOS-arm64-{version}" ,
15057 },
15158 {
15259 name: " dmgIntel" ,
15360 pattern: " MicYou-macOS-{version}-x64.dmg" ,
154- nightlyPattern: " MicYou-macOS-x64-{version}" ,
15561 },
15662 {
15763 name: " portableNoJRE" ,
15864 pattern: " MicYou-macOS-NoJRE-{version}.tar.gz" ,
159- nightlyPattern: " MicYou-macOS-NoJRE-arm64-{version}" ,
16065 },
16166 ],
16267 },
@@ -168,18 +73,15 @@ const platforms: {
16873 {
16974 name: " deb" ,
17075 pattern: " MicYou-Linux-{version}.deb" ,
171- nightlyPattern: " MicYou-Linux-{version}" ,
17276 },
17377 {
17478 name: " rpm" ,
17579 pattern: " MicYou-Linux-{version}.rpm" ,
176- nightlyPattern: " MicYou-Linux-{version}" ,
17780 },
17881 { name: " arch" , copy: " paru -S micyou-bin" },
17982 {
18083 name: " portableNoJRE" ,
18184 pattern: " MicYou-Linux-NoJRE-{version}.tar.gz" ,
182- nightlyPattern: " MicYou-Linux-NoJRE-{version}" ,
18385 },
18486 ],
18587 },
@@ -191,7 +93,6 @@ const platforms: {
19193 {
19294 name: " apk" ,
19395 pattern: " MicYou-Android-{version}.apk" ,
194- nightlyPattern: " MicYou-Android-{version}" ,
19596 },
19697 ],
19798 },
@@ -203,27 +104,10 @@ const githubUrl = (pattern: string) =>
203104const mirrorUrl = (pattern : string ) =>
204105 ` https://atomgit.com/gh_mirrors/mi/MicYou/releases/download/v${version .value }/${pattern .replace (" {version}" , version .value )} ` ;
205106
206- // nightly.link URL - 需要动态获取 artifact 名称
207- const getNightlyUrl = (pattern : string ): string | null => {
208- if (! nightlyRunId .value ) return null ;
209- const artifactName = findArtifact (pattern );
210- if (! artifactName ) return null ;
211- return ` https://nightly.link/${NIGHTLY_OWNER }/${NIGHTLY_REPO }/actions/runs/${nightlyRunId .value }/${artifactName }.zip ` ;
212- };
213-
214- const getUrl = (pattern : string , nightlyPattern ? : string ) => {
215- if (useNightly .value && nightlyPattern && nightlyRunId .value ) {
216- const url = getNightlyUrl (nightlyPattern );
217- return url || githubUrl (pattern );
218- }
107+ const getUrl = (pattern : string ) => {
219108 return useMirror .value ? mirrorUrl (pattern ) : githubUrl (pattern );
220109};
221110
222- const isNightlyAvailable = (pattern ? : string ): boolean => {
223- if (! pattern || ! nightlyRunId .value ) return false ;
224- return findArtifact (pattern ) !== null ;
225- };
226-
227111const copyCmd = async (cmd : string ) => {
228112 await navigator .clipboard .writeText (cmd );
229113 copied .value = cmd ;
@@ -251,29 +135,8 @@ const changelogLink = computed(() => {
251135 </header >
252136
253137 <div class =" card" >
254- <!-- 版本类型切换 -->
255- <div class =" version-switch" >
256- <span class =" version-label" :class =" { active: !useNightly }" >{{ t.stable }}</span >
257- <label class =" switch" >
258- <input type =" checkbox" v-model =" useNightly" >
259- <span class =" slider" ></span >
260- </label >
261- <span class =" version-label" :class =" { active: useNightly }" >{{ t.nightly }}</span >
262- <span class =" switch-tip" v-if =" useNightly" >{{ t.nightlyTip }}</span >
263- <span class =" loading-indicator" v-if =" useNightly && nightlyLoading" >
264- <iconify-icon icon =" mdi:loading" class =" spin" />
265- </span >
266- </div >
267- <!-- 错误提示 -->
268- <div class =" error-msg" v-if =" useNightly && nightlyError" >
269- <iconify-icon icon =" mdi:alert-circle" />
270- {{ nightlyError }}
271- <button class =" retry-btn" @click =" fetchLatestNightlyRun" >
272- <iconify-icon icon =" mdi:refresh" />
273- </button >
274- </div >
275- <!-- 下载源切换 (仅稳定版显示) -->
276- <div class =" mirror-switch" v-if =" !useNightly" >
138+ <!-- 下载源切换 -->
139+ <div class =" mirror-switch" >
277140 <span class =" source-label" :class =" { active: !useMirror }" >{{ t.sourceGithub }}</span >
278141 <label class =" switch" >
279142 <input type =" checkbox" v-model =" useMirror" >
@@ -292,16 +155,15 @@ const changelogLink = computed(() => {
292155 </div >
293156 <div class =" opts" >
294157 <template v-for =" f in p .files " :key =" f .pattern || f .copy " >
295- <a
296- v-if =" f.pattern"
297- :href =" getUrl(f.pattern, f.nightlyPattern)"
298- class =" btn"
299- :class =" { disabled: useNightly && !isNightlyAvailable(f.nightlyPattern) }"
158+ <a
159+ v-if =" f.pattern"
160+ :href =" getUrl(f.pattern)"
161+ class =" btn"
300162 target =" _blank"
301163 >
302164 <iconify-icon icon =" mdi:download" />{{ t[f.name] }}
303165 </a >
304- <button v-else class =" btn" :class =" { done: copied === f.copy }" @click =" copyCmd(f.copy!)" :disabled = " useNightly " >
166+ <button v-else class =" btn" :class =" { done: copied === f.copy }" @click =" copyCmd(f.copy!)" >
305167 <iconify-icon :icon =" copied === f.copy ? 'mdi:check' : 'mdi:content-copy'" />
306168 {{ copied === f.copy ? t.copied : t[f.name] }}
307169 </button >
@@ -312,6 +174,8 @@ const changelogLink = computed(() => {
312174
313175 <p class =" notes" >
314176 <a :href =" changelogLink" >{{ t.viewReleaseNotes }}</a >
177+ <span class =" separator" >|</span >
178+ <a href =" https://nightly.link/LanRhyme/MicYou/workflows/development/master?preview" target =" _blank" >{{ t.nightly }}</a >
315179 </p >
316180 </div >
317181</template >
@@ -358,27 +222,6 @@ const changelogLink = computed(() => {
358222 border-bottom : 1px solid var (--vp-c-divider );
359223}
360224
361- .version-switch {
362- display : flex ;
363- align-items : center ;
364- justify-content : center ;
365- gap : 12px ;
366- padding : 12px 24px ;
367- background : var (--vp-c-bg );
368- border-bottom : 1px solid var (--vp-c-divider );
369- }
370-
371- .version-label {
372- font-size : 0.875rem ;
373- color : var (--vp-c-text-3 );
374- transition : color 0.2s ;
375- }
376-
377- .version-label.active {
378- color : var (--vp-c-brand-1 );
379- font-weight : 500 ;
380- }
381-
382225.source-label {
383226 font-size : 0.875rem ;
384227 color : var (--vp-c-text-3 );
@@ -518,52 +361,13 @@ input:checked + .slider:before {
518361 transform : translateY (-1px );
519362}
520363
521- .btn :disabled ,
522- .btn.disabled {
364+ .btn :disabled {
523365 opacity : 0.5 ;
524366 cursor : not-allowed ;
525367 transform : none ;
526368 pointer-events : none ;
527369}
528370
529- .loading-indicator {
530- margin-left : 8px ;
531- }
532-
533- .spin {
534- animation : spin 1s linear infinite ;
535- }
536-
537- @keyframes spin {
538- from { transform : rotate (0deg ); }
539- to { transform : rotate (360deg ); }
540- }
541-
542- .error-msg {
543- display : flex ;
544- align-items : center ;
545- justify-content : center ;
546- gap : 8px ;
547- padding : 12px 24px ;
548- background : var (--vp-c-danger-soft );
549- color : var (--vp-c-danger-1 );
550- font-size : 0.875rem ;
551- }
552-
553- .retry-btn {
554- display : inline-flex ;
555- align-items : center ;
556- padding : 4px 8px ;
557- border-radius : 4px ;
558- background : var (--vp-c-bg );
559- border : 1px solid var (--vp-c-divider );
560- cursor : pointer ;
561- }
562-
563- .retry-btn :hover {
564- background : var (--vp-c-brand-soft );
565- }
566-
567371.notes {
568372 text-align : center ;
569373 margin-top : 32px ;
@@ -581,6 +385,11 @@ input:checked + .slider:before {
581385 text-decoration : underline ;
582386}
583387
388+ .separator {
389+ color : var (--vp-c-divider );
390+ margin : 0 12px ;
391+ }
392+
584393@media (max-width : 768px ) {
585394 .row {
586395 flex-direction : column ;
0 commit comments