Compare commits

..

8 Commits

Author SHA1 Message Date
71a8e65085 版本名的更改
Some checks failed
Axolotl desktop CI / guardrails (push) Has been cancelled
Axolotl desktop CI / desktop (macos-latest) (push) Has been cancelled
Axolotl desktop CI / desktop (ubuntu-latest) (push) Has been cancelled
Axolotl desktop CI / desktop (windows-latest) (push) Has been cancelled
Axolotl desktop CI / website (push) Has been cancelled
Repository checks / typos (push) Has been cancelled
Repository checks / tombi (push) Has been cancelled
Rust checks / shear (push) Has been cancelled
Sync source to CNB / Sync Git ref (push) Has been cancelled
Axolotl release / Release preflight (push) Has been cancelled
Prepare pnpm cache / prepare (push) Has been cancelled
Axolotl release / Build (Windows x64) (push) Has been cancelled
Axolotl release / Build (Linux arm64) (push) Has been cancelled
Axolotl release / Build (Linux x64) (push) Has been cancelled
Axolotl release / Build (macOS universal) (push) Has been cancelled
Axolotl release / Verify and publish (push) Has been cancelled
2026-09-13 15:22:55 +08:00
3cf55130a7 change:换了背景图,去掉了固定服务器里写的sls
Some checks failed
Axolotl desktop CI / guardrails (push) Has been cancelled
Axolotl desktop CI / desktop (macos-latest) (push) Has been cancelled
Axolotl desktop CI / desktop (ubuntu-latest) (push) Has been cancelled
Axolotl desktop CI / desktop (windows-latest) (push) Has been cancelled
Axolotl desktop CI / website (push) Has been cancelled
Repository checks / typos (push) Has been cancelled
Repository checks / tombi (push) Has been cancelled
Rust checks / shear (push) Has been cancelled
Sync source to CNB / Sync Git ref (push) Has been cancelled
2026-09-13 15:20:25 +08:00
6fa9461d2d feat:添加了神秘合成下界之星小彩蛋
Some checks failed
Axolotl desktop CI / guardrails (push) Has been cancelled
Axolotl desktop CI / desktop (macos-latest) (push) Has been cancelled
Axolotl desktop CI / desktop (ubuntu-latest) (push) Has been cancelled
Axolotl desktop CI / desktop (windows-latest) (push) Has been cancelled
Axolotl desktop CI / website (push) Has been cancelled
Repository checks / typos (push) Has been cancelled
Repository checks / tombi (push) Has been cancelled
Rust checks / shear (push) Has been cancelled
Sync source to CNB / Sync Git ref (push) Has been cancelled
2026-09-13 14:27:35 +08:00
5b2cc3f94e feat:一些版权描述的语言
Some checks failed
Axolotl desktop CI / guardrails (push) Has been cancelled
Axolotl desktop CI / desktop (macos-latest) (push) Has been cancelled
Axolotl desktop CI / desktop (ubuntu-latest) (push) Has been cancelled
Axolotl desktop CI / desktop (windows-latest) (push) Has been cancelled
Axolotl desktop CI / website (push) Has been cancelled
Repository checks / typos (push) Has been cancelled
Repository checks / tombi (push) Has been cancelled
Rust checks / shear (push) Has been cancelled
Sync source to CNB / Sync Git ref (push) Has been cancelled
2026-09-13 14:01:04 +08:00
9565466571 change:替换了引导页的美西螈图片
Some checks failed
Axolotl desktop CI / guardrails (push) Has been cancelled
Axolotl desktop CI / desktop (macos-latest) (push) Has been cancelled
Axolotl desktop CI / desktop (ubuntu-latest) (push) Has been cancelled
Axolotl desktop CI / desktop (windows-latest) (push) Has been cancelled
Axolotl desktop CI / website (push) Has been cancelled
Repository checks / typos (push) Has been cancelled
Repository checks / tombi (push) Has been cancelled
Rust checks / shear (push) Has been cancelled
Sync source to CNB / Sync Git ref (push) Has been cancelled
2026-09-13 13:52:33 +08:00
567184b061 feat:游戏启动自动置顶+聚焦
Some checks failed
Axolotl desktop CI / guardrails (push) Has been cancelled
Axolotl desktop CI / desktop (macos-latest) (push) Has been cancelled
Axolotl desktop CI / desktop (ubuntu-latest) (push) Has been cancelled
Axolotl desktop CI / desktop (windows-latest) (push) Has been cancelled
Axolotl desktop CI / website (push) Has been cancelled
Repository checks / typos (push) Has been cancelled
Repository checks / tombi (push) Has been cancelled
Rust checks / shear (push) Has been cancelled
Sync source to CNB / Sync Git ref (push) Has been cancelled
2026-09-13 13:41:58 +08:00
9b704919cf feat:添加了启动进度的显示
Some checks failed
Axolotl desktop CI / guardrails (push) Has been cancelled
Axolotl desktop CI / desktop (macos-latest) (push) Has been cancelled
Axolotl desktop CI / desktop (ubuntu-latest) (push) Has been cancelled
Axolotl desktop CI / desktop (windows-latest) (push) Has been cancelled
Axolotl desktop CI / website (push) Has been cancelled
Repository checks / typos (push) Has been cancelled
Repository checks / tombi (push) Has been cancelled
Rust checks / shear (push) Has been cancelled
Sync source to CNB / Sync Git ref (push) Has been cancelled
2026-09-13 13:07:45 +08:00
62d704dec0 feat:侧边栏加了皮肤站标签,可在启动内直接打开皮肤站 2026-09-13 11:01:05 +08:00
69 changed files with 3025 additions and 2135 deletions

View File

@ -1,12 +1,11 @@
{
"name": "@modrinth/app-frontend",
"private": true,
"version": "1.9.6-beta.3",
"version": "1.0.0-beta1",
"type": "module",
"scripts": {
"dev": "vite",
"build": "pnpm contributors:sync && vue-tsc --noEmit && vite build",
"contributors:sync": "node ../../scripts/axolotl/sync-contributors.mjs",
"build": "vue-tsc --noEmit && vite build",
"tsc:check": "vue-tsc --noEmit",
"lint": "eslint . && prettier --check .",
"fix": "eslint . --fix && prettier --write .",

Binary file not shown.

After

Width:  |  Height:  |  Size: 696 KiB

View File

@ -3,6 +3,7 @@ import { AuthFeature, TauriModrinthClient, VerboseLoggingFeature } from '@modrin
import {
ChangeSkinIcon,
CompassIcon,
GlobeIcon,
DownloadIcon,
ExternalIcon,
FlaskConicalIcon,
@ -735,6 +736,10 @@ const messages = defineMessages({
id: 'app.navigation.skin-selector',
defaultMessage: 'Skin selector',
},
starlightSkin: {
id: 'app.navigation.starlight-skin',
defaultMessage: '斯达莱特',
},
library: {
id: 'app.navigation.library',
defaultMessage: 'Library',
@ -2314,6 +2319,13 @@ provideAppUpdateDownloadProgress(appUpdateDownload)
>
<ChangeSkinIcon />
</NavButton>
<NavButton
v-tooltip.right="formatMessage(messages.starlightSkin)"
data-onboarding-id="nav-starlight-skin"
to="/starlight-skin"
>
<GlobeIcon />
</NavButton>
<NavButton
v-tooltip.right="formatMessage(messages.library)"
data-onboarding-id="nav-library"

View File

@ -1,218 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 786.4 652" style="enable-background:new 0 0 786.4 652;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FE4592;}
.st1{fill:#E59CC3;}
.st2{fill:#F0579A;}
.st3{fill:#D982AD;}
.st4{fill:#FED5EB;}
.st5{fill:#FEBAD8;}
.st6{fill:#DF3177;}
.st7{fill:#D772A4;}
.st8{fill:#FCC1E0;}
.st9{fill:#A12D72;}
.st10{fill:#D6518C;}
.st11{fill:#FE3D8C;}
.st12{fill:#FE71B1;}
.st13{fill:#E03178;}
.st14{fill:#FD71B2;}
.st15{fill:#FEB8DA;}
.st16{fill:#E79EC5;}
.st17{fill:#E7A1C6;}
.st18{fill:#43147C;}
.st19{fill:#FE60A4;}
.st20{fill:#E589BD;}
.st21{fill:#E65895;}
.st22{fill:#DC73A6;}
.st23{fill:none;stroke:#231815;stroke-miterlimit:10;}
.st24{fill:#FE6DAF;}
.st25{fill:#C64F8D;}
.st26{fill:#666666;}
.st27{fill:#3F3F3F;}
.st28{opacity:0.22;fill:#36BCDF;}
.st29{opacity:0.84;fill:#FFFFFF;}
</style>
<g>
<polygon class="st0" points="222.7,418.3 203,400.1 215.7,397.4 232.2,411.3 "/>
<polygon class="st1" points="568.1,220.2 503.4,213.9 502.4,225.2 483.6,225.2 483.6,235.2 460.1,235.2 460.1,255 483.6,255
483.6,257.6 515.8,257.6 515.8,265 540.1,265 540.1,257.6 556.9,257.6 556.9,249.6 568.1,249.6 "/>
<polygon class="st2" points="543.5,253.9 531.3,261.2 531.3,245 543.5,238.5 "/>
<polygon class="st3" points="505.3,241.6 491.5,249.3 491.5,232.7 505.3,225.7 "/>
<polygon class="st4" points="526.5,256.2 411.5,233.8 343.4,266.9 445.8,303.3 "/>
<polygon class="st5" points="410.6,286.2 397.4,279.4 467.6,244.7 481.8,247.5 "/>
<polygon class="st6" points="418.5,327.1 576.1,234.8 576.1,249.5 431,339.5 "/>
<polygon class="st6" points="372.1,274.3 533.3,192.4 533.3,211.5 384.6,286.6 "/>
<g>
<polygon class="st7" points="432.4,420.1 414.5,427.4 407.2,383.8 418.7,378.1 "/>
<polygon class="st8" points="437.9,435.6 417.2,444 414.5,427.4 432.4,420.1 "/>
<polygon class="st0" points="453,429.3 437.9,435.6 432.4,420.1 446.2,414.2 "/>
<polygon class="st0" points="443.3,450.6 420.3,459.8 417.3,444 437.9,435.6 "/>
<polygon class="st0" points="417.3,444 402.1,450.2 400.8,433.1 414.5,427.4 "/>
<polygon class="st9" points="427.4,405 411.8,411.1 409.2,396.9 423.3,392.1 "/>
</g>
<polygon class="st7" points="247.1,402 232.3,389.5 282.1,365.1 291.3,374.4 "/>
<polygon class="st8" points="232.2,411.3 215.7,397.4 232.3,389.5 247.1,402 "/>
<polygon class="st0" points="215.7,397.4 209.2,383.1 227,374.9 232.3,389.5 "/>
<polygon class="st10" points="424.5,381.8 446.1,381.8 446.1,392 423.5,388.5 "/>
<g>
<polygon class="st11" points="248.5,323.8 230.8,321.1 230.1,300.8 247.9,303.5 "/>
<polygon class="st11" points="267,346.7 249.2,344.1 248.5,323.8 266.3,326.5 "/>
<polygon class="st11" points="285.4,369.7 267.6,367 267,346.7 284.7,349.5 "/>
<polygon class="st12" points="249.2,344.1 231.4,341.5 230.8,321.1 248.5,323.8 "/>
<polygon class="st12" points="267.6,367.1 249.9,364.4 249.2,344.1 267,346.7 "/>
<polygon class="st13" points="255.3,321.5 248.5,323.8 247.9,303.5 254.7,300.8 "/>
<polygon class="st14" points="247.9,303.5 230.1,300.8 236.9,298.6 254.7,300.8 "/>
<polygon class="st13" points="273.7,344.4 267,346.7 266.3,326.5 273.1,323.9 "/>
<polygon class="st14" points="266.3,326.5 248.5,323.8 255.3,321.5 273.1,323.9 "/>
<polygon class="st13" points="292.1,366.4 285.4,369.7 284.7,349.5 291.5,346.8 "/>
<polygon class="st14" points="284.7,349.5 267,346.7 273.7,344.4 291.5,346.8 "/>
</g>
<g>
<polygon class="st11" points="265.5,255.2 247.9,255.2 247.9,231.7 265.5,235 "/>
<polygon class="st11" points="283.2,278.8 265.5,275.6 265.5,255.2 283.2,258.6 "/>
<polygon class="st11" points="300.8,302.3 283.2,299.1 283.2,278.8 300.8,282.1 "/>
<polygon class="st12" points="265.5,275.6 247.9,272.4 247.9,252.6 265.5,255.2 "/>
<polygon class="st12" points="283.2,299.1 265.5,295.9 265.5,275.6 283.2,278.8 "/>
<polygon class="st13" points="272.4,253.3 265.5,255.2 265.5,235 272.4,232.6 "/>
<polygon class="st14" points="265.5,235 247.9,231.7 254.7,229.7 272.4,232.6 "/>
<polygon class="st13" points="290,276.7 283.2,278.8 283.2,258.6 290.1,256.2 "/>
<polygon class="st14" points="283.2,258.6 265.5,255.3 272.4,253.3 290.1,256.2 "/>
<polygon class="st13" points="307.7,299.3 300.8,302.3 300.8,282.1 307.7,279.6 "/>
<polygon class="st14" points="300.8,282.1 283.2,278.8 290,276.7 307.7,279.6 "/>
</g>
<polyline class="st15" points="402.3,312 401.6,414.3 260.2,388.3 258,290 402.3,312 "/>
<polyline class="st4" points="402.3,312 258,290 312.4,263 445.7,279.8 402.3,312 "/>
<polygon class="st16" points="445.7,279.8 446.4,379.3 401.6,414.3 402.3,312 "/>
<polygon class="st17" points="446.1,392 528.2,335.7 526.5,256.2 445.8,303.3 "/>
<polygon class="st18" points="280.9,353.2 259.2,349.5 258.9,334.2 280.9,338.1 "/>
<polygon class="st19" points="346.8,376.1 305.7,370.1 305.7,346 346.6,352.5 "/>
<polygon class="st18" points="401.8,377.3 378,373 377.3,348.4 402,352 "/>
<rect x="445.8" y="303.3" class="st20" width="15.3" height="70.3"/>
<polygon class="st7" points="443.5,323.7 433.7,332.6 433.5,318 443.5,318 "/>
<g>
<polygon class="st11" points="451.9,325.1 433.5,322.8 433.5,301.6 451.9,303.7 "/>
<polygon class="st11" points="470.4,306.2 451.9,303.8 451.9,282.5 470.4,284.8 "/>
<polygon class="st11" points="488.9,287.1 470.4,284.8 470.4,263.6 488.9,265.8 "/>
<polygon class="st12" points="470.4,327.1 451.9,325 451.9,303.7 470.4,306.2 "/>
<polygon class="st14" points="451.9,303.7 433.5,301.6 439.9,298.9 451.9,300.2 "/>
<polygon class="st14" points="470.4,284.8 451.9,282.5 458.4,279.9 470.4,281.2 "/>
<polygon class="st14" points="488.9,266 470.4,263.7 476.8,261.1 495.5,263 "/>
<polygon class="st13" points="495.5,285 488.9,287 488.9,266 495.5,263 "/>
<polygon class="st21" points="495.5,305.9 488.9,308 488.9,287 495.5,285 "/>
<polygon class="st21" points="477.1,325.1 470.4,327.2 470.4,306.2 477.1,304.1 "/>
<polygon class="st12" points="488.9,308 470.4,306.2 470.4,284.6 488.9,287 "/>
</g>
<polygon class="st22" points="446.1,380.3 401.6,414.3 401.6,393.8 445.7,361.6 "/>
<polygon class="st12" points="378.1,410 401.6,414.3 401.6,393.8 377.8,390 "/>
<polygon class="st12" points="260.2,388.3 281.6,392.2 281.6,374.9 259.8,371.3 "/>
<g>
<polygon class="st9" points="533.7,343.4 522.5,354.9 501.2,347.5 513.5,337.1 "/>
<polygon class="st7" points="548.6,347.8 538,360.3 522.5,354.9 533.7,343.4 "/>
<polygon class="st8" points="554.2,365.4 538,360.3 548.6,347.8 563.1,351.8 "/>
<polygon class="st0" points="545.6,378.3 528.6,371.9 538,360.3 554.2,365.4 "/>
<polygon class="st0" points="563.1,351.8 548.6,347.8 555.6,339.6 569,343.1 "/>
<polygon class="st0" points="567.1,369.9 554.2,365.4 563.1,351.8 575.4,355.4 "/>
</g>
<path class="st23" d="M583.4,311.2"/>
<path class="st23" d="M508.7,382.6"/>
<polygon class="st15" points="431.4,290.3 295.5,271.3 312.4,262.9 445.8,279.9 "/>
<g>
<polygon class="st11" points="310.9,225.2 293.3,222 293.3,201.7 310.9,205 "/>
<polygon class="st11" points="328.5,248.8 310.9,245.6 310.9,225.2 328.5,228.6 "/>
<polygon class="st11" points="346.2,272.4 328.5,269.1 328.5,248.8 346.2,252.1 "/>
<polygon class="st12" points="310.9,245.6 293.3,242.4 293.3,222 310.9,225.2 "/>
<polygon class="st12" points="328.5,269.1 310.9,265.9 310.9,245.6 328.5,248.8 "/>
<polygon class="st13" points="317.8,223.3 310.9,225.2 310.9,205 317.8,202.6 "/>
<polygon class="st14" points="310.9,205 293.3,201.7 300.1,199.7 317.8,202.6 "/>
<polygon class="st13" points="335.4,246.8 328.5,248.8 328.5,228.6 335.5,226.2 "/>
<polygon class="st14" points="328.5,228.6 310.9,225.2 317.8,223.3 335.5,226.2 "/>
<polygon class="st13" points="353.1,269.3 346.2,272.4 346.2,252.1 353.1,249.7 "/>
<polygon class="st14" points="346.2,252.1 328.5,248.8 335.4,246.8 353.1,249.7 "/>
</g>
<polygon class="st15" points="304.6,280.3 283.6,277.3 295.9,271.2 315.1,274 "/>
<polygon class="st15" points="422.7,296.9 399.3,293.9 409.7,286.8 433,289.3 "/>
<polygon class="st22" points="528.2,335.7 483.6,369.7 483.6,349.1 527.7,317 "/>
<g>
<polygon class="st11" points="447.9,393.7 431.3,391.6 431.3,372.5 447.9,374.5 "/>
<polygon class="st11" points="464.5,376.7 447.9,374.6 447.9,355.6 464.5,357.5 "/>
<polygon class="st11" points="481.1,359.6 464.5,357.6 464.5,338.5 481.1,340.5 "/>
<polygon class="st12" points="464.5,395.5 447.9,393.6 447.9,374.5 464.5,376.7 "/>
<polygon class="st14" points="447.9,374.5 431.3,372.5 437.1,370.2 447.9,371.3 "/>
<polygon class="st14" points="464.5,357.6 447.9,355.6 453.7,353.2 464.5,354.3 "/>
<polygon class="st14" points="481.1,340.7 464.5,338.7 470.3,336.3 486.9,338.1 "/>
<polygon class="st13" points="487,357.7 481.1,359.6 481.1,340.7 486.9,338.1 "/>
<polygon class="st21" points="487,376.5 481.1,378.3 481.1,359.5 487,357.7 "/>
<polygon class="st21" points="470.5,393.7 464.5,395.6 464.5,376.7 470.5,374.9 "/>
<polygon class="st12" points="481.1,378.3 464.5,376.7 464.5,357.4 481.1,359.6 "/>
</g>
<polygon class="st24" points="533.3,192.7 378.4,271.2 371.7,270.5 529.2,192.2 "/>
<g>
<polygon class="st11" points="408,281.2 389.5,278.9 389.5,257.6 408,259.8 "/>
<polygon class="st11" points="426.5,262.2 408,259.9 408,238.6 426.5,240.8 "/>
<polygon class="st11" points="445,243.2 426.5,240.9 426.5,219.6 445,221.9 "/>
<polygon class="st12" points="426.5,283.2 408,281.1 408,259.8 426.5,262.2 "/>
<polygon class="st14" points="408,259.8 389.5,257.6 396,255 408,256.2 "/>
<polygon class="st14" points="426.5,240.9 408,238.6 414.4,236 426.5,237.3 "/>
<polygon class="st14" points="445,222 426.5,219.8 432.9,217.2 451.5,219.1 "/>
<polygon class="st13" points="451.6,241 445,243.1 445,222 451.5,219.1 "/>
<polygon class="st21" points="451.6,261.9 445,264.1 445,243 451.6,241 "/>
<polygon class="st21" points="433.1,281.2 426.5,283.2 426.5,262.3 433.1,260.2 "/>
<polygon class="st12" points="445,264.1 426.5,262.2 426.5,240.7 445,243.1 "/>
</g>
<polygon class="st6" points="554,216.8 521.5,233.6 521.5,217.2 554,200.7 "/>
<polygon class="st6" points="576.1,221.7 543.5,238.5 543.5,222 576.1,205.6 "/>
<polygon class="st6" points="576.1,236.2 565.4,242.5 565.4,227.2 576.1,221.7 "/>
<polygon class="st20" points="518.1,324 503.9,334.3 503.8,315.2 518.1,305.4 "/>
<polygon class="st25" points="518.1,343.4 503.9,354.2 503.9,334.3 518.1,324 "/>
<g>
<polygon class="st26" points="251.1,357.9 244,352 236.9,346.1 233.3,346.8 225,360.3 232.1,366.2 239.2,372.1 233.3,379.3
227.4,386.4 221.5,393.5 215.5,400.6 226.2,413.2 229.8,412.4 235.7,405.3 241.6,398.2 247.5,391.1 253.5,384 264.1,396.6
267.7,395.9 273.6,388.7 279.5,381.6 272.4,375.7 265.3,369.8 258.2,363.9 "/>
<polygon class="st26" points="246.4,305.8 240.4,312.9 234.5,320 228.6,327.1 222.7,334.2 233.3,346.8 236.9,346.1 242.8,339
248.7,331.8 254.7,324.7 260.6,317.6 266.5,310.5 272.4,303.4 265.3,297.5 258.2,291.6 254.7,292.3 "/>
<polygon class="st26" points="312.7,312.9 305.6,307 298.5,301 291.4,295.1 284.3,289.2 280.8,289.9 272.4,303.4 279.6,309.3
286.7,315.3 293.8,321.2 300.9,327.1 311.6,339.7 315.1,339 321,331.9 327,324.8 319.9,318.8 "/>
<polygon class="st26" points="315.1,339 311.6,339.7 303.3,353.2 297.3,360.3 291.4,367.4 285.5,374.5 279.5,381.6 290.2,394.2
293.8,393.5 299.7,386.4 305.6,379.3 311.5,372.2 317.5,365 323.4,357.9 329.3,350.8 322.2,344.9 "/>
</g>
<g>
<rect x="225.3" y="350.6" transform="matrix(0.7681 0.6403 -0.6403 0.7681 284.8098 -66.7259)" class="st27" width="18.5" height="18.5"/>
<rect x="239.5" y="362.4" transform="matrix(0.7681 0.6403 -0.6403 0.7681 295.6992 -73.0825)" class="st27" width="18.5" height="18.5"/>
<rect x="253.7" y="374.3" transform="matrix(-0.7681 -0.6403 0.6403 -0.7681 219.343 846.537)" class="st27" width="18.5" height="18.5"/>
<rect x="246.6" y="296.1" transform="matrix(-0.6403 0.7681 -0.7681 -0.6403 654.23 304.3015)" class="st27" width="18.5" height="18.5"/>
<rect x="234.8" y="310.3" transform="matrix(-0.6403 0.7681 -0.7681 -0.6403 645.7056 336.7339)" class="st27" width="18.5" height="18.5"/>
<rect x="222.9" y="324.5" transform="matrix(0.6403 -0.7681 0.7681 0.6403 -172.8613 298.3588)" class="st27" width="18.5" height="18.5"/>
<rect x="301.1" y="317.4" transform="matrix(-0.7681 -0.6403 0.6403 -0.7681 339.6122 776.3314)" class="st27" width="18.5" height="18.5"/>
<rect x="286.9" y="305.6" transform="matrix(-0.7681 -0.6403 0.6403 -0.7681 322.0609 746.2641)" class="st27" width="18.5" height="18.5"/>
<rect x="272.7" y="293.7" transform="matrix(0.7681 0.6403 -0.6403 0.7681 259.3836 -110.2836)" class="st27" width="18.5" height="18.5"/>
<rect x="279.8" y="371.9" transform="matrix(0.6403 -0.7681 0.7681 0.6403 -188.8274 359.1042)" class="st27" width="18.5" height="18.5"/>
<rect x="291.6" y="357.7" transform="matrix(0.6403 -0.7681 0.7681 0.6403 -173.641 363.0958)" class="st27" width="18.5" height="18.5"/>
<rect x="303.5" y="343.5" transform="matrix(-0.6403 0.7681 -0.7681 -0.6403 783.9594 338.399)" class="st27" width="18.5" height="18.5"/>
<g>
<rect x="227.6" y="376.7" transform="matrix(0.6403 -0.7681 0.7681 0.6403 -211.2171 320.7485)" class="st27" width="18.5" height="18.5"/>
<rect x="215.8" y="390.9" transform="matrix(0.6403 -0.7681 0.7681 0.6403 -226.4035 316.757)" class="st27" width="18.5" height="18.5"/>
</g>
<rect x="244.7" y="315.5" transform="matrix(0.7681 0.6403 -0.6403 0.7681 283.0002 -94.8372)" class="st28" width="55.5" height="55.5"/>
<rect x="289.3" y="331.6" transform="matrix(-0.7681 -0.6403 0.6403 -0.7681 309.5449 793.8829)" class="st29" width="18.5" height="18.5"/>
</g>
<polygon class="st0" points="244,416.4 232.2,411.3 247.1,402 261.8,405.6 "/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 14 KiB

View File

@ -1,200 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 786.4 652" style="enable-background:new 0 0 786.4 652;" xml:space="preserve">
<style type="text/css">
.st0{fill:#E59CC3;}
.st1{fill:#F0579A;}
.st2{fill:#D982AD;}
.st3{fill:#FED5EB;}
.st4{fill:#FEBAD8;}
.st5{fill:#DF3177;}
.st6{fill:#D772A4;}
.st7{fill:#FCC1E0;}
.st8{fill:#FE4592;}
.st9{fill:#A12D72;}
.st10{fill:#D6518C;}
.st11{fill:#FE3D8C;}
.st12{fill:#FE71B1;}
.st13{fill:#E03178;}
.st14{fill:#FD71B2;}
.st15{fill:#FEB8DA;}
.st16{fill:#F4ABD2;}
.st17{fill:#E79EC5;}
.st18{fill:#E7A1C6;}
.st19{fill:#43147C;}
.st20{fill:#FE60A4;}
.st21{fill:#E589BD;}
.st22{fill:#E65895;}
.st23{fill:#DC73A6;}
.st24{fill:none;stroke:#231815;stroke-miterlimit:10;}
.st25{fill:#DB8AB4;}
.st26{fill:#FE6DAF;}
.st27{fill:#C64F8D;}
.st28{fill:#443733;}
.st29{fill:#231815;}
.st30{fill:#675B59;}
.st31{fill:#8C7D79;}
.st32{fill:#E5B11F;}
.st33{fill:#B78C20;}
.st34{fill:#6D3E38;}
.st35{fill:#99594B;}
.st36{fill:#824C43;}
.st37{fill:#EACFA9;}
.st38{fill:#AA967E;}
.st39{fill:#C9B299;}
</style>
<g>
<polygon class="st0" points="566.9,216.9 502.2,210.6 501.2,221.9 482.4,221.9 482.4,231.9 458.9,231.9 458.9,251.7 482.4,251.7
482.4,254.3 514.6,254.3 514.6,261.7 538.9,261.7 538.9,254.3 555.7,254.3 555.7,246.3 566.9,246.3 "/>
<polygon class="st1" points="542.3,250.6 530.1,257.9 530.1,241.7 542.3,235.2 "/>
<polygon class="st2" points="504.1,238.3 490.3,246 490.3,229.4 504.1,222.4 "/>
<polygon class="st3" points="525.3,252.9 410.3,230.5 342.2,263.6 444.6,300 "/>
<polygon class="st4" points="409.4,282.9 396.2,276.1 466.4,241.4 480.6,244.2 "/>
<polygon class="st5" points="417.3,323.8 574.9,231.5 574.9,246.2 429.8,336.2 "/>
<polygon class="st5" points="370.9,271 532.1,189.1 532.1,208.2 383.4,283.3 "/>
<g>
<polygon class="st6" points="439.1,423.4 421.3,430.7 414,387.1 425.5,381.4 "/>
<polygon class="st7" points="444.6,439 423.9,447.3 421.3,430.7 439.1,423.4 "/>
<polygon class="st8" points="459.7,432.7 444.6,439 439.1,423.4 452.9,417.5 "/>
<polygon class="st8" points="450,453.9 427,463.1 424,447.3 444.6,439 "/>
<polygon class="st8" points="424,447.3 408.8,453.5 407.5,436.4 421.3,430.7 "/>
<polygon class="st9" points="434.1,408.4 418.5,414.4 416,400.2 430,395.4 "/>
</g>
<g>
<polygon class="st6" points="251.1,396.2 235,385.6 281.6,355.5 291.8,363.7 "/>
<polygon class="st7" points="237.3,407.2 219.3,395.4 235,385.6 251.1,396.2 "/>
<polygon class="st8" points="251,415.6 237.3,407.2 251.1,396.2 262.6,404.1 "/>
<polygon class="st8" points="223.9,418 204.2,404.9 219.3,395.4 237.3,407.2 "/>
<polygon class="st8" points="219.3,395.4 207.8,387.5 224.6,378 235,385.6 "/>
</g>
<polygon class="st10" points="423.3,378.5 444.9,378.5 444.9,388.7 422.3,385.2 "/>
<g>
<polygon class="st11" points="247.3,320.5 229.6,317.8 228.9,297.5 246.7,300.2 "/>
<polygon class="st11" points="265.8,343.4 248,340.8 247.3,320.5 265.1,323.2 "/>
<polygon class="st11" points="284.2,366.4 266.4,363.7 265.8,343.4 283.5,346.2 "/>
<polygon class="st12" points="248,340.8 230.2,338.2 229.6,317.8 247.3,320.5 "/>
<polygon class="st12" points="266.4,363.8 248.7,361.1 248,340.8 265.8,343.4 "/>
<polygon class="st13" points="254.1,318.2 247.3,320.5 246.7,300.2 253.5,297.5 "/>
<polygon class="st14" points="246.7,300.2 228.9,297.5 235.7,295.3 253.5,297.5 "/>
<polygon class="st13" points="272.5,341.1 265.8,343.4 265.1,323.2 271.9,320.6 "/>
<polygon class="st14" points="265.1,323.2 247.3,320.5 254.1,318.2 271.9,320.6 "/>
<polygon class="st13" points="290.9,363.1 284.2,366.4 283.5,346.2 290.3,343.5 "/>
<polygon class="st14" points="283.5,346.2 265.8,343.4 272.5,341.1 290.3,343.5 "/>
</g>
<g>
<polygon class="st11" points="264.3,251.9 246.7,251.9 246.7,228.4 264.3,231.7 "/>
<polygon class="st11" points="282,275.5 264.3,272.3 264.3,251.9 282,255.3 "/>
<polygon class="st11" points="299.6,299 282,295.8 282,275.5 299.6,278.8 "/>
<polygon class="st12" points="264.3,272.3 246.7,269.1 246.7,249.3 264.3,251.9 "/>
<polygon class="st12" points="282,295.8 264.3,292.6 264.3,272.3 282,275.5 "/>
<polygon class="st13" points="271.2,250 264.3,251.9 264.3,231.7 271.2,229.3 "/>
<polygon class="st14" points="264.3,231.7 246.7,228.4 253.5,226.4 271.2,229.3 "/>
<polygon class="st13" points="288.8,273.4 282,275.5 282,255.3 288.9,252.9 "/>
<polygon class="st14" points="282,255.3 264.3,252 271.2,250 288.9,252.9 "/>
<polygon class="st13" points="306.5,296 299.6,299 299.6,278.8 306.5,276.3 "/>
<polygon class="st14" points="299.6,278.8 282,275.5 288.8,273.4 306.5,276.3 "/>
</g>
<polyline class="st15" points="401.1,308.7 400.4,411 259,385 256.8,286.7 401.1,308.7 "/>
<polyline class="st16" points="401.1,308.7 256.8,286.7 311.2,259.7 444.5,276.5 401.1,308.7 "/>
<polygon class="st17" points="444.5,276.5 445.2,376 400.4,411 401.1,308.7 "/>
<polygon class="st18" points="444.9,388.7 527,332.4 525.3,252.9 444.6,300 "/>
<polygon class="st19" points="279.8,355.5 258.2,351.6 257.6,327.1 279.3,330.3 "/>
<polygon class="st20" points="345.6,372.8 304.5,366.8 304.5,342.7 345.4,349.2 "/>
<polygon class="st19" points="400.6,374 376.8,369.7 376.1,345.1 400.8,348.7 "/>
<rect x="444.6" y="300" class="st21" width="15.3" height="70.3"/>
<polygon class="st6" points="442.3,320.4 432.5,329.3 432.3,314.7 442.3,314.7 "/>
<g>
<polygon class="st11" points="450.7,321.8 432.3,319.5 432.3,298.3 450.7,300.4 "/>
<polygon class="st11" points="469.2,302.9 450.7,300.5 450.7,279.2 469.2,281.5 "/>
<polygon class="st11" points="487.7,283.8 469.2,281.5 469.2,260.3 487.7,262.5 "/>
<polygon class="st12" points="469.2,323.8 450.7,321.7 450.7,300.4 469.2,302.9 "/>
<polygon class="st14" points="450.7,300.4 432.3,298.3 438.7,295.6 450.7,296.9 "/>
<polygon class="st14" points="469.2,281.5 450.7,279.2 457.2,276.6 469.2,277.9 "/>
<polygon class="st14" points="487.7,262.7 469.2,260.4 475.6,257.8 494.3,259.7 "/>
<polygon class="st13" points="494.3,281.7 487.7,283.7 487.7,262.7 494.3,259.7 "/>
<polygon class="st22" points="494.3,302.6 487.7,304.7 487.7,283.7 494.3,281.7 "/>
<polygon class="st22" points="475.9,321.8 469.2,323.9 469.2,302.9 475.9,300.8 "/>
<polygon class="st12" points="487.7,304.7 469.2,302.9 469.2,281.3 487.7,283.7 "/>
</g>
<polygon class="st23" points="444.9,377 400.4,411 400.4,390.5 444.5,358.3 "/>
<polygon class="st12" points="376.9,406.7 400.4,411 400.4,390.5 376.6,386.7 "/>
<polygon class="st12" points="259,385 280.4,388.9 280.4,371.6 258.6,368 "/>
<g>
<polygon class="st9" points="532.5,340.1 521.3,351.6 500,344.2 512.3,333.8 "/>
<polygon class="st6" points="547.4,344.5 536.8,357 521.3,351.6 532.5,340.1 "/>
<polygon class="st7" points="553,362.1 536.8,357 547.4,344.5 561.9,348.5 "/>
<polygon class="st8" points="544.4,375 527.4,368.6 536.8,357 553,362.1 "/>
<polygon class="st8" points="561.9,348.5 547.4,344.5 554.4,336.3 567.8,339.8 "/>
<polygon class="st8" points="565.9,366.6 553,362.1 561.9,348.5 574.2,352.1 "/>
</g>
<path class="st24" d="M582.2,307.9"/>
<path class="st24" d="M507.5,379.3"/>
<polygon class="st25" points="430.2,287 294.3,268 311.2,259.6 444.6,276.6 "/>
<g>
<polygon class="st11" points="309.7,221.9 292.1,218.7 292.1,198.4 309.7,201.7 "/>
<polygon class="st11" points="327.3,245.5 309.7,242.3 309.7,221.9 327.3,225.3 "/>
<polygon class="st11" points="345,269.1 327.3,265.8 327.3,245.5 345,248.8 "/>
<polygon class="st12" points="309.7,242.3 292.1,239.1 292.1,218.7 309.7,221.9 "/>
<polygon class="st12" points="327.3,265.8 309.7,262.6 309.7,242.3 327.3,245.5 "/>
<polygon class="st13" points="316.6,220 309.7,221.9 309.7,201.7 316.6,199.3 "/>
<polygon class="st14" points="309.7,201.7 292.1,198.4 298.9,196.4 316.6,199.3 "/>
<polygon class="st13" points="334.2,243.5 327.3,245.5 327.3,225.3 334.3,222.9 "/>
<polygon class="st14" points="327.3,225.3 309.7,221.9 316.6,220 334.3,222.9 "/>
<polygon class="st13" points="351.9,266 345,269.1 345,248.8 351.9,246.4 "/>
<polygon class="st14" points="345,248.8 327.3,245.5 334.2,243.5 351.9,246.4 "/>
</g>
<polygon class="st15" points="303.4,277 282.4,274 294.7,267.9 313.9,270.7 "/>
<polygon class="st25" points="421.5,293.6 398.1,290.6 408.5,283.5 431.8,286 "/>
<polygon class="st23" points="527,332.4 482.4,366.4 482.4,345.8 526.5,313.7 "/>
<g>
<polygon class="st11" points="446.7,390.4 430.1,388.3 430.1,369.2 446.7,371.2 "/>
<polygon class="st11" points="463.3,373.4 446.7,371.3 446.7,352.3 463.3,354.2 "/>
<polygon class="st11" points="479.9,356.3 463.3,354.3 463.3,335.2 479.9,337.2 "/>
<polygon class="st12" points="463.3,392.2 446.7,390.3 446.7,371.2 463.3,373.4 "/>
<polygon class="st14" points="446.7,371.2 430.1,369.2 435.9,366.9 446.7,368 "/>
<polygon class="st14" points="463.3,354.3 446.7,352.3 452.5,349.9 463.3,351 "/>
<polygon class="st14" points="479.9,337.4 463.3,335.4 469.1,333 485.7,334.8 "/>
<polygon class="st13" points="485.8,354.4 479.9,356.3 479.9,337.4 485.7,334.8 "/>
<polygon class="st22" points="485.8,373.2 479.9,375 479.9,356.2 485.8,354.4 "/>
<polygon class="st22" points="469.3,390.4 463.3,392.3 463.3,373.4 469.3,371.6 "/>
<polygon class="st12" points="479.9,375 463.3,373.4 463.3,354.1 479.9,356.3 "/>
</g>
<polygon class="st26" points="532.1,189.4 377.2,267.9 370.5,267.2 528,188.9 "/>
<g>
<polygon class="st11" points="406.8,277.9 388.3,275.6 388.3,254.3 406.8,256.5 "/>
<polygon class="st11" points="425.3,258.9 406.8,256.6 406.8,235.3 425.3,237.5 "/>
<polygon class="st11" points="443.8,239.9 425.3,237.6 425.3,216.3 443.8,218.6 "/>
<polygon class="st12" points="425.3,279.9 406.8,277.8 406.8,256.5 425.3,258.9 "/>
<polygon class="st14" points="406.8,256.5 388.3,254.3 394.8,251.7 406.8,252.9 "/>
<polygon class="st14" points="425.3,237.6 406.8,235.3 413.2,232.7 425.3,234 "/>
<polygon class="st14" points="443.8,218.7 425.3,216.5 431.7,213.9 450.3,215.8 "/>
<polygon class="st13" points="450.4,237.7 443.8,239.8 443.8,218.7 450.3,215.8 "/>
<polygon class="st22" points="450.4,258.6 443.8,260.8 443.8,239.7 450.4,237.7 "/>
<polygon class="st22" points="431.9,277.9 425.3,279.9 425.3,259 431.9,256.9 "/>
<polygon class="st12" points="443.8,260.8 425.3,258.9 425.3,237.4 443.8,239.8 "/>
</g>
<polygon class="st5" points="552.8,213.5 520.3,230.3 520.3,213.9 552.8,197.4 "/>
<polygon class="st5" points="574.9,218.4 542.3,235.2 542.3,218.7 574.9,202.3 "/>
<polygon class="st5" points="574.9,232.9 564.2,239.2 564.2,223.9 574.9,218.4 "/>
<polygon class="st21" points="516.9,320.7 502.7,331 502.6,311.9 516.9,302.1 "/>
<polygon class="st27" points="516.9,340.1 502.7,350.9 502.7,331 516.9,320.7 "/>
<polygon class="st28" points="361.3,312.8 271,299.6 271,252.5 361.3,263.2 "/>
<polygon class="st29" points="409.4,282.9 361.3,312.8 361.3,263.2 409.4,238.4 "/>
<polygon class="st30" points="457.6,254.2 446.5,254.2 369.5,226.5 253.5,240.6 253.5,238.1 244.4,238.1 244.4,242.1 244.5,242.1
244.4,242.1 298.4,293 457.6,258.3 457.6,258.2 457.6,258.2 "/>
<polygon class="st31" points="457.6,254.2 298.4,288.9 244.4,238.1 369,218.7 "/>
<polygon class="st32" points="420.3,262.8 340.2,245.5 355.8,242.6 436.7,258.8 "/>
<polygon class="st33" points="436.8,277.9 420.5,281.8 420.3,262.8 436.7,258.8 "/>
<polygon class="st32" points="436.8,296.4 420.5,300.9 420.4,281.9 436.8,277.9 "/>
<g>
<polygon class="st34" points="446.3,430 417.4,453.6 421.4,390 455.3,362.8 "/>
<polygon class="st35" points="409.1,455.2 368.8,450.9 363.5,386.6 411.3,391.6 "/>
<polygon class="st36" points="417.4,453.6 409.1,455.2 411.3,391.6 421.4,390 "/>
<polygon class="st37" points="415.6,388 370.2,383.6 363.5,386.6 411.3,391.6 "/>
<polygon class="st37" points="449,363.1 415.6,388 421.4,390 455.3,362.8 "/>
<polygon class="st38" points="415.5,390.9 415.6,388 411.3,391.6 "/>
<polygon class="st39" points="421.4,390 415.6,388 415.5,390.9 "/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 169 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 198 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 918 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 890 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 962 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1000 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 696 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@ -0,0 +1,108 @@
<script setup lang="ts">
import { DownloadIcon } from '@modrinth/assets'
import { defineMessages, useVIntl } from '@modrinth/ui'
import { computed, onUnmounted, ref } from 'vue'
import { loading_listener } from '@/helpers/events'
import type { LoadingBar } from '@/helpers/state'
const { formatMessage } = useVIntl()
const messages = defineMessages({
title: { id: 'app.home.launch-progress.title', defaultMessage: '启动进度' },
empty: { id: 'app.home.launch-progress.empty', defaultMessage: '启动游戏后,这里会显示进度。' },
})
interface LoadingEventPayload {
event: LoadingBar['bar_type']
loader_uuid: string
fraction: number | null
message: string
}
interface LaunchProgressItem {
key: string
message: string
fraction: number | null
}
// 启动相关(以及准备)阶段会发的 loading 类型;安装/下载也一并显示,便于用户看到进度
const LAUNCH_BAR_TYPES = new Set([
'minecraft_download',
'instance_update',
'zip_extract',
'pack_download',
'pack_file_download',
])
const progressItems = ref<LaunchProgressItem[]>([])
const activeMap = new Map<string, LaunchProgressItem>()
function isVisible(barType: LoadingBar['bar_type']): boolean {
const type = barType?.type ?? ''
return LAUNCH_BAR_TYPES.has(type)
}
function applyEvent(payload: LoadingEventPayload) {
// fraction 为 null 约定为「完成」,移除该进度条
if (payload.fraction === null) {
activeMap.delete(payload.loader_uuid)
progressItems.value = Array.from(activeMap.values())
return
}
if (!isVisible(payload.event)) return
activeMap.set(payload.loader_uuid, {
key: payload.loader_uuid,
message: payload.message,
fraction: payload.fraction,
})
progressItems.value = Array.from(activeMap.values())
}
const hasProgress = computed(() => progressItems.value.length > 0)
function percent(item: LaunchProgressItem): string {
if (item.fraction == null || !Number.isFinite(item.fraction)) return ''
return `${Math.round(Math.max(0, Math.min(1, item.fraction)) * 100)}%`
}
const unlistenLoading = await loading_listener((payload: LoadingEventPayload) => {
applyEvent(payload)
})
onUnmounted(() => {
unlistenLoading?.()
})
</script>
<template>
<section
v-if="hasProgress"
class="flex min-w-0 flex-col gap-3 border-0 border-b-[1px] border-solid border-[--brand-gradient-border] p-4"
>
<div class="flex items-center gap-2">
<DownloadIcon class="size-4 shrink-0 text-secondary" aria-hidden="true" />
<h2 class="m-0 truncate text-lg">
{{ formatMessage(messages.title) }}
</h2>
</div>
<ul class="m-0 flex list-none flex-col gap-2 p-0">
<li v-for="item in progressItems" :key="item.key" class="flex min-w-0 flex-col gap-1">
<div class="flex min-w-0 items-center justify-between gap-2 text-sm text-primary">
<span class="min-w-0 truncate">{{ item.message }}</span>
<span v-if="percent(item)" class="shrink-0 tabular-nums text-secondary">
{{ percent(item) }}
</span>
</div>
<div class="h-1 w-full overflow-hidden rounded-full bg-surface-4">
<div
class="h-full rounded-full bg-brand transition-[width] duration-200"
:style="{ width: percent(item) || '100%' }"
/>
</div>
</li>
</ul>
</section>
</template>

View File

@ -27,7 +27,6 @@ import { kill } from '@/helpers/instance'
import type { GameInstance } from '@/helpers/types'
import {
type ServerWorld,
PROTECTED_SERVER_ADDRESS,
set_world_display_status,
start_join_server,
type WorldWithInstance,
@ -79,10 +78,6 @@ const messages = defineMessages({
id: 'app.home.servers.more-options',
defaultMessage: 'More options',
},
protectedServerName: {
id: 'app.home.servers.protected-name',
defaultMessage: 'Starlight Server',
},
})
const startingServerKey = ref<string | null>(null)
@ -90,29 +85,13 @@ const startingServerKey = ref<string | null>(null)
const instanceById = computed(
() => new Map(props.instances.map((instance) => [instance.id, instance])),
)
const servers = computed(() => {
const favoriteServers = favoriteWorlds.value.flatMap((world) => {
if (world.type !== 'server' || world.address === PROTECTED_SERVER_ADDRESS) return []
const servers = computed(() =>
favoriteWorlds.value.flatMap((world) => {
if (world.type !== 'server') return []
const instance = instanceById.value.get(world.instance_id)
return instance ? [{ instance, world: world as ServerWorld & WorldWithInstance }] : []
})
const protectedInstance =
props.instances.find((instance) => instance.install_stage === 'installed') ?? props.instances[0]
if (!protectedInstance) return favoriteServers
const protectedServer: ServerWorld & WorldWithInstance = {
instance_id: protectedInstance.id,
name: formatMessage(messages.protectedServerName),
last_played: undefined,
icon: undefined,
display_status: 'favorite',
type: 'server',
index: -1,
address: PROTECTED_SERVER_ADDRESS,
pack_status: 'prompt',
}
return [{ instance: protectedInstance, world: protectedServer }, ...favoriteServers]
})
}),
)
function serverKey(world: ServerWorld & WorldWithInstance): string {
return `${world.instance_id}:${world.address}`
@ -261,7 +240,6 @@ async function unpinServer(world: ServerWorld & WorldWithInstance) {
</button>
</ButtonStyled>
<ButtonStyled
v-if="server.world.address !== PROTECTED_SERVER_ADDRESS"
circular
size="small"
type="transparent"

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,6 @@
<script setup lang="ts">
import * as THREE from 'three'
import { type GLTF, GLTFLoader } from 'three/examples/jsm/Addons.js'
import { onMounted, onScopeDispose, useTemplateRef } from 'vue'
import { useTheming } from '@/store/theme'
@ -17,20 +16,6 @@ function isDarkMode() {
return ['dark', 'oled'].includes(themeStore.selectedTheme)
}
function loadGLTF(url: string): Promise<GLTF> {
return new Promise((res, rej) => {
const loader = new GLTFLoader()
loader.load(
url,
(data) => {
res(data)
},
undefined,
rej,
)
})
}
function createTip(position: THREE.Vector3, color: THREE.ColorRepresentation = 0x00ff00) {
const tipGeometry = new THREE.SphereGeometry(2)
const tipMaterial = new THREE.MeshBasicMaterial({ color })
@ -224,43 +209,16 @@ function main() {
scene.add(createWater(waterMaterial, new THREE.Vector3(2, -8, -10)))
scene.add(createWater(waterMaterial, new THREE.Vector3(16, -8, -26)))
async function load() {
const axlGLTF = await loadGLTF('/models/axolotl.gltf')
// 静止的下界之星(替换原动态美西螈)
const starTexture = new THREE.TextureLoader().load('/models/netherstar.png')
starTexture.colorSpace = THREE.SRGBColorSpace
const starMaterial = new THREE.SpriteMaterial({ map: starTexture, transparent: true })
const starSprite = new THREE.Sprite(starMaterial)
starSprite.scale.set(8, 8, 1)
starSprite.position.set(0, -2.5, 0)
scene.add(starSprite)
const axlModel = axlGLTF.scene
axlModel.scale.multiplyScalar(5)
axlModel.rotateY(Math.PI / 2)
axlModel.position.add(new THREE.Vector3(0, -2.5, 0))
scene.add(axlModel)
const mixer = new THREE.AnimationMixer(axlModel)
const axlSwimAnim = axlGLTF.animations.filter((a) => a.name === 'swim')[0]
if (!axlSwimAnim) return console.error('Missing animation swim')
mixer.clipAction(axlSwimAnim).play()
// // Axl Label
// const axlLabelGLTF = await loadGLTF('/models/axl_label.glb')
// const axlLabel = axlLabelGLTF.scene
// axlLabel.scale.multiplyScalar(8)
// axlLabel.rotateY(-Math.PI / 2)
// axlLabel.position.set(0, 5.2, 0)
// scene.add(axlLabel)
const originAxlModelPosition = axlModel.position.clone()
return function (deltaTime: number, elapsedTime: number) {
axlModel.position.set(
originAxlModelPosition.x,
originAxlModelPosition.y + Math.sin(elapsedTime),
originAxlModelPosition.z,
)
axlModel.rotation.y = Math.sin(elapsedTime * 0.3) * 0.2 + (Math.PI * 100) / 180
mixer.update(deltaTime)
}
}
let updateGLTF = (_deltaTime: number, _elapsedTime: number) => {}
load().then((updateFn) => {
if (updateFn) updateGLTF = updateFn
})
const circleMaterial = createCircleMaterial()
circleMaterial.uniforms.color.value = new THREE.Color(accentColor).multiplyScalar(

View File

@ -1,6 +1,7 @@
<script setup lang="ts">
import searchingVisual from '@/assets/axo-searching.svg?url'
import teachingVisual from '@/assets/axo-teaching.svg?url'
import searchingVisual from '@/assets/star-searching.svg?url'
import teachingVisual from '@/assets/star-teaching.svg?url'
defineProps<{
alt: string

View File

@ -79,7 +79,7 @@ const gameModal = ref<InstanceType<typeof EasterEggGameModal> | null>(null)
const contributorsModal = ref<InstanceType<typeof EasterEggContributorsModal> | null>(null)
let typedBuffer = ''
const secretCodes = ['cyf112233', 'cxkcxkckx']
const secretCodes = ['starlight']
const konamiSequence = [
'ArrowUp',
@ -139,7 +139,7 @@ const messages = defineMessages({
},
copyright: {
id: 'app.settings.about.copyright',
defaultMessage: 'Copyright © Starlight All Rights Reserved.',
defaultMessage: 'Copyright © Axolotl All Rights Reserved.',
},
version: {
id: 'app.settings.about.version',
@ -159,7 +159,7 @@ const messages = defineMessages({
},
attribution: {
id: 'app.settings.about.attribution',
defaultMessage: 'Starlight Launcher is a modified version of the open-source Modrinth codebase.',
defaultMessage: 'Starlight Launcher is a modified version of the Axolotl Launcher, which is based on the open-source Modrinth codebase.',
},
notAffiliated: {
id: 'app.settings.about.not-affiliated',

View File

@ -9,7 +9,7 @@ export type AboutMemberExperience = {
}
// 长按「关于」页成员名字触发的彩蛋体验。
// 原 'axolotl-merge' 彩蛋已移除,改为通用占位组件 AboutEasterEgg.vue
// 下界之星合成彩蛋AboutEasterEgg.vue由长按成员名 / 暗号 / Konami 秘技触发
// 若要为特定成员挂载自定义彩蛋,在这里新增条目即可。
const memberExperiences: Record<string, AboutMemberExperience> = {
'easter-egg': {

View File

@ -51,7 +51,6 @@ import type {
} from '@/helpers/worlds.ts'
import {
getWorldIdentifier,
PROTECTED_SERVER_ADDRESS,
set_world_display_status,
} from '@/helpers/worlds.ts'
@ -149,9 +148,6 @@ const managed = computed(() => props.managed)
const shortcutInstanceId = computed(() => props.shortcutInstanceId ?? props.instanceId)
const homePinTarget = computed(() => props.shortcutInstanceId ?? props.instanceId)
const pinnedToHome = computed(() => props.world.display_status === 'favorite')
const protectedServer = computed(
() => props.world.type === 'server' && props.world.address === PROTECTED_SERVER_ADDRESS,
)
async function updateHomePin() {
if (!homePinTarget.value) return
@ -519,8 +515,8 @@ const messages = defineMessages({
{
id: 'edit',
action: () => emit('edit'),
shown: !instanceId && !protectedServer,
disabled: locked || managed || protectedServer,
shown: !instanceId,
disabled: locked || managed,
tooltip: locked
? formatMessage(messages.worldInUse)
: managed
@ -538,7 +534,7 @@ const messages = defineMessages({
},
{
id: pinnedToHome ? 'unpin-home' : 'pin-home',
shown: !!homePinTarget && !protectedServer,
shown: !!homePinTarget,
action: updateHomePin,
},
{
@ -555,8 +551,8 @@ const messages = defineMessages({
color: 'red',
hoverFilled: true,
action: () => emit('delete'),
shown: !instanceId && !protectedServer,
disabled: locked || managed || protectedServer,
shown: !instanceId,
disabled: locked || managed,
tooltip: locked
? formatMessage(messages.worldInUse)
: managed

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,4 @@
import contributorsData from './contributors.json'
import teamData from './team.json'
@ -10,7 +11,7 @@ export interface TeamMember {
export interface Contributor {
name: string
avatarUrl: string
avatar: string
url: string
contributions: number
}
@ -26,4 +27,9 @@ export const teamMembers: (TeamMember & { avatarUrl: string })[] = teamData.map(
avatarUrl: teamAvatarModules[`./avatars/${member.avatar}`],
}))
export const contributors = contributorsData as Contributor[]
export const contributors: (Contributor & { avatarUrl: string })[] = (
contributorsData as Contributor[]
).map((contributor) => ({
...contributor,
avatarUrl: teamAvatarModules[`./avatars/${contributor.avatar}`],
}))

View File

@ -18,7 +18,6 @@ type BaseWorld = {
export type WorldType = 'singleplayer' | 'server'
export type DisplayStatus = 'normal' | 'hidden' | 'favorite'
export const PROTECTED_SERVER_ADDRESS = 'slv4.starlight.cool'
export type SingleplayerWorld = BaseWorld & {
type: 'singleplayer'

View File

@ -6654,7 +6654,7 @@
"message": "Help support continued development"
},
"app.settings.about.attribution": {
"message": "Starlight Launcher is a modified version of the open-source Modrinth codebase."
"message": "Starlight Launcher is a modified version of the Axolotl Launcher, which is based on the open-source Modrinth codebase."
},
"app.settings.about.community-support": {
"message": "Project & community"

View File

@ -6708,7 +6708,7 @@
"message": "支持项目持续开发"
},
"app.settings.about.attribution": {
"message": "Starlight Launcher 是开源项目 Modrinth 代码库的修改版本。"
"message": "Starlight Launcher 是基于 Axolotl 启动器修改的版本Axolotl 启动器基于开源项目 Modrinth 代码库。"
},
"app.settings.about.community-support": {
"message": "项目与社区"

View File

@ -6423,7 +6423,7 @@
"message": "支持專案持續開發"
},
"app.settings.about.attribution": {
"message": "Starlight Launcher 是開源專案 Modrinth 程式碼庫的修改版本。"
"message": "Starlight Launcher 是基於 Axolotl 啟動器修改的版本Axolotl 啟動器基於開源專案 Modrinth 程式碼庫。"
},
"app.settings.about.community-support": {
"message": "專案與社群"

View File

@ -30,6 +30,7 @@ import HomeDashboard from '@/components/home/HomeDashboard.vue'
import HomeInstancePickerModal from '@/components/home/HomeInstancePickerModal.vue'
import HomeMinecraftNews from '@/components/home/HomeMinecraftNews.vue'
import HomeMinimal from '@/components/home/HomeMinimal.vue'
import HomeLaunchProgress from '@/components/home/HomeLaunchProgress.vue'
import HomePlayInsights from '@/components/home/HomePlayInsights.vue'
import { useNetworkStatus } from '@/composables/useNetworkStatus'
import { get_default_user, users } from '@/helpers/auth'
@ -378,6 +379,7 @@ onUnmounted(() => {
class="flex min-w-0 flex-col slide-enter-active"
:class="{ 'slide-enter-from': !animateSidebarShow }"
>
<HomeLaunchProgress />
<HomePlayInsights />
<HomeDailyChallenge />
<HomeMinecraftNews />

View File

@ -376,7 +376,7 @@ const pageTitle: MessageDescriptor = settingsPageTitle
<div class="settings-footer-version min-w-0">
<p class="m-0 break-words">{{ AxolotlBrandConfig.productName }} {{ version }}</p>
<p class="m-0 truncate">{{ platformName() }} {{ osVersion }}</p>
<p class="m-0 text-xs text-secondary">Powered by Starlight</p>
<p class="m-0 text-xs text-secondary">Powered by Axolotl</p>
</div>
</div>
</footer>

View File

@ -0,0 +1,41 @@
<script setup lang="ts">
import { defineMessages, useVIntl } from '@modrinth/ui'
import { ref } from 'vue'
const { formatMessage } = useVIntl()
const messages = defineMessages({
title: {
id: 'app.starlight-skin.title',
defaultMessage: '斯达莱特',
},
frameTitle: {
id: 'app.starlight-skin.frame-title',
defaultMessage: 'StarLight Skin Site',
},
})
const SKIN_SITE_URL = 'https://skin.starlight.cool/'
const loading = ref(true)
function onLoad() {
loading.value = false
}
</script>
<template>
<div class="flex h-full min-h-0 flex-col">
<div v-if="loading" class="flex flex-1 items-center justify-center">
<span class="text-secondary">{{ formatMessage(messages.title) }}</span>
</div>
<iframe
:src="SKIN_SITE_URL"
:title="formatMessage(messages.frameTitle)"
class="h-full min-h-0 w-full flex-1 border-0"
:class="loading ? 'hidden' : ''"
@load="onLoad"
/>
</div>
</template>

View File

@ -93,6 +93,14 @@ export default new createRouter({
discordActivity: 'Changing skins...',
},
},
{
path: '/starlight-skin',
name: 'Starlight skin',
component: () => import('@/pages/StarlightSkin.vue'),
meta: {
breadcrumb: [{ name: 'Starlight skin' }],
},
},
{
path: '/multiplayer',
name: 'Multiplayer',

View File

@ -26,7 +26,10 @@
"core:window:allow-set-effects",
"core:window:allow-set-cursor-grab",
"core:window:allow-set-cursor-visible",
"core:window:allow-start-dragging",
"core:window:allow-set-always-on-top",
"core:window:allow-set-focus",
"core:webview:allow-set-webview-zoom"
]
}

View File

@ -354,7 +354,7 @@ unsafe extern "system" fn maximize_if_owned_by_process(
_: windows::Win32::Foundation::LPARAM,
) -> windows::core::BOOL {
use windows::Win32::UI::WindowsAndMessaging::{
GetWindowThreadProcessId, IsWindowVisible, SW_MAXIMIZE, ShowWindow,
GetWindowThreadProcessId, IsWindowVisible, SW_MAXIMIZE, SetForegroundWindow, ShowWindow,
};
use windows::core::BOOL;
@ -363,6 +363,7 @@ unsafe extern "system" fn maximize_if_owned_by_process(
if window_pid == MAXIMIZE_PROCESS_ID.load(Ordering::Relaxed)
&& unsafe { IsWindowVisible(hwnd).as_bool() }
{
let _ = unsafe { SetForegroundWindow(hwnd) };
let _ = unsafe { ShowWindow(hwnd, SW_MAXIMIZE) };
MAXIMIZE_WINDOW_FOUND.store(true, Ordering::Relaxed);
return BOOL(0);

View File

@ -117,7 +117,8 @@
"style-src": "'unsafe-inline' 'self'",
"script-src": "'self' 'unsafe-eval' 'wasm-unsafe-eval'",
"worker-src": "'self' blob:",
"frame-src": "https://www.youtube.com https://www.youtube-nocookie.com https://discord.com 'self' http://axolotl-skin.localhost axolotl-skin://localhost",
"frame-src": "https://www.youtube.com https://www.youtube-nocookie.com https://discord.com 'self' http://axolotl-skin.localhost axolotl-skin://localhost https://skin.starlight.cool",
"media-src": "'self' data: https://*.githubusercontent.com"
},
"csp": {
@ -127,8 +128,7 @@
"img-src": "https: 'unsafe-inline' 'self' asset: http://asset.localhost http://textures.minecraft.net blob: data:",
"style-src": "'unsafe-inline' 'self'",
"script-src": "'self' 'unsafe-eval' 'wasm-unsafe-eval'",
"worker-src": "'self' blob:",
"frame-src": "https://www.youtube.com https://www.youtube-nocookie.com https://discord.com 'self' axolotl-skin://localhost http://axolotl-skin.localhost",
"frame-src": "https://www.youtube.com https://www.youtube-nocookie.com https://discord.com 'self' axolotl-skin://localhost http://axolotl-skin.localhost https://skin.starlight.cool",
"media-src": "'self' data: https://*.githubusercontent.com"
}
}

View File

@ -1,114 +0,0 @@
import fs from 'node:fs/promises'
import { existsSync } from 'node:fs'
import { fileURLToPath } from 'node:url'
const REPOSITORY = 'Mystic-Stars/Axolotl'
const PER_PAGE = 100
const MAX_PAGES = 10
const OUTPUT_PATH = fileURLToPath(
new URL('../../apps/app-frontend/src/data/about/contributors.json', import.meta.url),
)
function requestHeaders() {
const headers = {
Accept: 'application/vnd.github+json',
'User-Agent': 'Axolotl-Launcher-Contributors-Sync',
}
const token = process.env.AXOLOTL_GITHUB_TOKEN || process.env.GITHUB_TOKEN
if (token) {
headers.Authorization = `Bearer ${token}`
}
return headers
}
async function fetchPage(page) {
const url = new URL(`https://api.github.com/repos/${REPOSITORY}/contributors`)
url.searchParams.set('per_page', String(PER_PAGE))
url.searchParams.set('page', String(page))
let failure
for (let attempt = 1; attempt <= 3; attempt++) {
try {
const response = await fetch(url, {
headers: requestHeaders(),
signal: AbortSignal.timeout(30_000),
})
if (!response.ok) throw new Error(`HTTP ${response.status}`)
return await response.json()
} catch (error) {
failure = error
if (attempt < 3) await new Promise((resolve) => setTimeout(resolve, attempt * 1_000))
}
}
throw new Error(`Unable to fetch contributors from ${url}`, { cause: failure })
}
function normalizeContributor(contributor) {
if (!contributor || typeof contributor !== 'object' || Array.isArray(contributor))
return undefined
if (typeof contributor.login !== 'string' || !contributor.login) return undefined
if (typeof contributor.html_url !== 'string' || !contributor.html_url) return undefined
if (typeof contributor.avatar_url !== 'string' || !contributor.avatar_url) return undefined
if (!Number.isInteger(contributor.contributions) || contributor.contributions < 1)
return undefined
const avatarUrl = new URL(contributor.avatar_url)
avatarUrl.searchParams.set('s', '96')
return {
name: contributor.login,
avatarUrl: avatarUrl.toString(),
url: contributor.html_url,
contributions: contributor.contributions,
}
}
async function fetchContributors() {
const pages = []
for (let page = 1; page <= MAX_PAGES; page++) {
const contributors = await fetchPage(page)
if (!Array.isArray(contributors)) throw new Error(`Page ${page} did not contain an array`)
pages.push(contributors)
if (contributors.length < PER_PAGE) break
}
const contributors = pages
.flat()
.map(normalizeContributor)
.filter((contributor) => contributor !== undefined)
.sort(
(left, right) =>
right.contributions - left.contributions || left.name.localeCompare(right.name),
)
if (contributors.length === 0)
throw new Error('The contributors response did not contain any people')
return contributors
}
async function main() {
let contributors
try {
contributors = await fetchContributors()
} catch (error) {
if (existsSync(OUTPUT_PATH)) {
console.warn(
`Unable to refresh contributors, keeping the existing snapshot: ${error.message}`,
)
return
}
throw error
}
const nextText = `${JSON.stringify(contributors, null, '\t')}\n`
const currentText = existsSync(OUTPUT_PATH) ? await fs.readFile(OUTPUT_PATH, 'utf8') : ''
if (currentText === nextText) {
console.log(`Contributors are up to date (${contributors.length} people).`)
return
}
await fs.writeFile(OUTPUT_PATH, nextText)
console.log(`Synchronized ${contributors.length} contributors from ${REPOSITORY}.`)
}
await main()

View File

@ -0,0 +1,59 @@
p = r"D:\Project\Starlight_Lancher\apps\app-frontend\src\App.vue"
lines = open(p, encoding="utf-8").read().split("\n")
# 1) import 加 GlobeIcon在第 5 行(CompassIcon)后、第 4 行(ChangeSkinIcon)开始那个块里
# 找到 "CompassIcon," 那行,在其后插 "GlobeIcon,"
imp_idx = None
for i, ln in enumerate(lines):
if ln.strip() == "CompassIcon,":
imp_idx = i
break
assert imp_idx is not None, "找不到 CompassIcon import"
lines.insert(imp_idx + 1, "\tGlobeIcon,")
# 2) messages 加 starlightSkin在 skinSelector 块结束的 '},' (原737) 后插
# 因为上面插了1行行号+1。重新定位 skinSelector 块
skin_msg_idx = None
for i, ln in enumerate(lines):
if "app.navigation.skin-selector" in ln:
# 找它下面的 '},'
for j in range(i, i + 5):
if lines[j].strip() == "},":
skin_msg_idx = j
break
break
assert skin_msg_idx is not None, "找不到 skinSelector 消息块结尾"
new_msg = [
"\tstarlightSkin: {",
"\t\tid: 'app.navigation.starlight-skin',",
"\t\tdefaultMessage: '斯达莱特',",
"\t},",
]
lines[skin_msg_idx + 1:skin_msg_idx + 1] = new_msg
# 3) 模板加 NavButton在 skin selector 的 </NavButton> (原2316) 后
# 找 'nav-skins' 的块结尾 </NavButton>
nav_idx = None
for i, ln in enumerate(lines):
if 'data-onboarding-id="nav-skins"' in ln:
for j in range(i, i + 10):
if "</NavButton>" in lines[j]:
nav_idx = j
break
break
assert nav_idx is not None, "找不到 nav-skins 的 NavButton 结尾"
new_nav = [
"\t\t\t\t<NavButton",
'\t\t\t\t\tv-tooltip.right="formatMessage(messages.starlightSkin)"',
'\t\t\t\t\tdata-onboarding-id="nav-starlight-skin"',
'\t\t\t\t\tto="/starlight-skin"',
"\t\t\t\t>",
"\t\t\t\t\t<GlobeIcon />",
"\t\t\t\t</NavButton>",
]
lines[nav_idx + 1:nav_idx + 1] = new_nav
open(p, "w", encoding="utf-8", newline="\n").write("\n".join(lines))
print("完成。")
print("import 行:", lines[imp_idx], "->", lines[imp_idx+1])

View File

@ -0,0 +1,27 @@
p = r"D:\Project\Starlight_Lancher\apps\app-frontend\src\routes.js"
lines = open(p, encoding="utf-8").read().split("\n")
# 在第 95 行 (idx 94) 的 '},' 之后插入新路由;即 idx 95 前插
anchor_idx = 95 # 0-based, 原第96行 '{'
# 校验
assert lines[94].strip() == "},", f"第95行不是 '}},' 而是: {lines[94]!r}"
assert lines[95].strip() == "{", f"第96行不是 '{{' 而是: {lines[95]!r}"
new_route = [
"\t\t{",
"\t\t\tpath: '/starlight-skin',",
"\t\t\tname: 'Starlight skin',",
"\t\t\tcomponent: () => import('@/pages/StarlightSkin.vue'),",
"\t\t\tmeta: {",
"\t\t\t\tbreadcrumb: [{ name: 'Starlight skin' }],",
"\t\t\t},",
"\t\t},",
]
lines[95:95] = new_route
open(p, "w", encoding="utf-8", newline="\n").write("\n".join(lines))
print("插入完成,新行数:", len(lines))
print("--- 插入区 86-105 ---")
for i in range(85, 106):
if i < len(lines):
print(f"{i+1}: {lines[i]}")

View File

@ -0,0 +1,20 @@
p = r"D:\Project\Starlight_Lancher\apps\app-frontend\src\components\ui\AboutScene.vue"
t = open(p, encoding="utf-8").read()
old = "\tlet updateGLTF = (_deltaTime: number, _elapsedTime: number) => {}"
new = """\t// 静止的下界之星(替换原动态美西螈)
\tconst starTexture = new THREE.TextureLoader().load('/models/netherstar.png')
\tstarTexture.colorSpace = THREE.SRGBColorSpace
\tconst starMaterial = new THREE.SpriteMaterial({ map: starTexture, transparent: true })
\tconst starSprite = new THREE.Sprite(starMaterial)
\tstarSprite.scale.set(8, 8, 1)
\tstarSprite.position.set(0, -2.5, 0)
\tscene.add(starSprite)
\tlet updateGLTF = (_deltaTime: number, _elapsedTime: number) => {}"""
assert old in t, "找不到 updateGLTF 空行"
t = t.replace(old, new, 1)
open(p, "w", encoding="utf-8", newline="\n").write(t)
print("done")

View File

@ -0,0 +1,8 @@
p = r"D:\Project\Starlight_Lancher\target\release\Starlight Launcher.exe"
data = open(p, "rb").read()
print("exe 大小:", len(data))
print("内嵌 PNG 数量(粗估):", data.count(b"\x89PNG\r\n\x1a\n"))
ico = open(r"D:\Project\Starlight_Lancher\apps\app\icons\icon.ico", "rb").read()
print("icon.ico 大小:", len(ico))
print("icon.ico 原样出现在 exe:", ico in data)

View File

@ -0,0 +1,7 @@
t = open(r"D:\Project\Starlight_Lancher\apps\app-frontend\src\components\ui\AboutEasterEgg.vue", encoding="utf-8").read()
print("含 </script>:", "</script>" in t)
print("含 drawAxolotl:", "drawAxolotl" in t)
print("含 axolotl-balls import:", "axolotl-balls" in t)
print("行数:", len(t.split("\n")))
print("末 120 字:", repr(t[-120:]))

View File

@ -0,0 +1,19 @@
# 1) 游戏文件:改内部标识符
p = r"D:\Project\Starlight_Lancher\apps\app-frontend\src\components\ui\AboutEasterEgg.vue"
t = open(p, encoding="utf-8").read()
t = t.replace("AXOLOTL_MERGE_BEST_STORAGE_KEY", "MERGE_BEST_STORAGE_KEY")
t = t.replace("drawAxolotl", "drawMergePiece")
open(p, "w", encoding="utf-8", newline="\n").write(t)
print("游戏文件: AXOLOTL_MERGE_BEST_STORAGE_KEY -> MERGE_BEST_STORAGE_KEY, drawAxolotl -> drawMergePiece")
print(" 残留 axolotl:", "axolotl" in t.lower())
# 2) about-member-experiences.ts更新过时注释
p2 = r"D:\Project\Starlight_Lancher\apps\app-frontend\src\components\ui\settings\about-member-experiences.ts"
t2 = open(p2, encoding="utf-8").read()
old = "// 原 'axolotl-merge' 彩蛋已移除,改为通用占位组件 AboutEasterEgg.vue。"
new = "// 下界之星合成彩蛋AboutEasterEgg.vue由长按成员名 / 暗号 / Konami 秘技触发。"
assert old in t2, "注释找不到"
t2 = t2.replace(old, new, 1)
open(p2, "w", encoding="utf-8", newline="\n").write(t2)
print("注释已更新")

View File

@ -0,0 +1,55 @@
p = r"D:\Project\Starlight_Lancher\apps\app-frontend\src\components\ui\AboutEasterEgg.vue"
t = open(p, encoding="utf-8").read()
# 1) import 6 张星图
old_imports = """import blueBall from '@/assets/axolotl-balls/blueball.png'
import cyanBall from '@/assets/axolotl-balls/cyanball.png'
import pinkBall from '@/assets/axolotl-balls/pinkball.png'
import redBall from '@/assets/axolotl-balls/redball.png'
import superBall from '@/assets/axolotl-balls/superball.png'
import yellowBall from '@/assets/axolotl-balls/yellowball.png'"""
new_imports = """import blueStar from '@/assets/stars/blue.png'
import greenStar from '@/assets/stars/green.png'
import pinkStar from '@/assets/stars/pink.png'
import redStar from '@/assets/stars/red.png'
import yellowStar from '@/assets/stars/yellow.png'
import superStar from '@/assets/stars/super.png'"""
assert old_imports in t, "import 块找不到"
t = t.replace(old_imports, new_imports, 1)
# 2) 配置数组(保持 6颜色换星色
old_cfg = """const colors = ['#ff8fb3', '#ffd866', '#54c9c4', '#e5484d', '#5c8ee8', '#f05ed2']
const rainbowColors = ['#ff8fb3', '#ffd866', '#54c9c4', '#5c8ee8', '#f05ed2', '#ffffff']
const radiusRatios = [0.055, 0.075, 0.098, 0.128, 0.164, 0.21]
const points = [10, 25, 60, 120, 250, 1200]"""
new_cfg = """const colors = ['#4f9cff', '#1bd96a', '#ff8fb3', '#e5484d', '#ffd866', '#e0e0ff']
const rainbowColors = ['#4f9cff', '#1bd96a', '#ff8fb3', '#e5484d', '#ffd866', '#ffffff']
const radiusRatios = [0.055, 0.075, 0.098, 0.128, 0.164, 0.21]
const points = [10, 25, 60, 120, 250, 1200]"""
assert old_cfg in t, "配置数组找不到"
t = t.replace(old_cfg, new_cfg, 1)
# 3) 球图数组blue->green->pink->red->yellow->super
old_balls = "const ballImages = [pinkBall, yellowBall, cyanBall, redBall, blueBall, superBall]"
new_balls = "const ballImages = [blueStar, greenStar, pinkStar, redStar, yellowStar, superStar]"
assert old_balls in t, "ballImages 找不到"
t = t.replace(old_balls, new_balls, 1)
# 4) 存储 key
t = t.replace("const AXOLOTL_MERGE_BEST_STORAGE_KEY = 'axolotl-merge-best-score'",
"const AXOLOTL_MERGE_BEST_STORAGE_KEY = 'starlight-merge-best-score'", 1)
# 5) 文案
text_map = {
"defaultMessage: 'The axolotls got stranded!'": "defaultMessage: 'The stars fell down!'",
"defaultMessage: 'Rainbow axolotl!'": "defaultMessage: 'Nether star!'",
"defaultMessage: 'The tide is falling — keep your axolotls underwater!'": "defaultMessage: 'The sky is falling — keep merging!'",
"defaultMessage: 'Click to drop a pink axolotl'": "defaultMessage: 'Click to drop a star'",
}
for o, n in text_map.items():
assert o in t, f"文案找不到: {o}"
t = t.replace(o, n, 1)
open(p, "w", encoding="utf-8", newline="\n").write(t)
print("done")

View File

@ -0,0 +1,54 @@
p = r"D:\Project\Starlight_Lancher\apps\app-frontend\src\components\ui\AboutEasterEgg.vue"
t = open(p, encoding="utf-8").read()
# 1) import 换 5 张星图
old_imports = """import blueBall from '@/assets/axolotl-balls/blueball.png'
import cyanBall from '@/assets/axolotl-balls/cyanball.png'
import pinkBall from '@/assets/axolotl-balls/pinkball.png'
import redBall from '@/assets/axolotl-balls/redball.png'
import superBall from '@/assets/axolotl-balls/superball.png'
import yellowBall from '@/assets/axolotl-balls/yellowball.png'"""
new_imports = """import blueStar from '@/assets/stars/blue.png'
import greenStar from '@/assets/stars/green.png'
import pinkStar from '@/assets/stars/pink.png'
import redStar from '@/assets/stars/red.png'
import superStar from '@/assets/stars/super.png'"""
assert old_imports in t, "import 块找不到"
t = t.replace(old_imports, new_imports, 1)
# 2) 颜色 / 半径 / 分数 / 球图数组6→5
old_cfg = """const colors = ['#ff8fb3', '#ffd866', '#54c9c4', '#e5484d', '#5c8ee8', '#f05ed2']
const rainbowColors = ['#ff8fb3', '#ffd866', '#54c9c4', '#5c8ee8', '#f05ed2', '#ffffff']
const radiusRatios = [0.055, 0.075, 0.098, 0.128, 0.164, 0.21]
const points = [10, 25, 60, 120, 250, 1200]"""
new_cfg = """const colors = ['#4f9cff', '#1bd96a', '#ff8fb3', '#e5484d', '#ffd866']
const rainbowColors = ['#4f9cff', '#1bd96a', '#ff8fb3', '#e5484d', '#ffd866']
const radiusRatios = [0.055, 0.078, 0.105, 0.14, 0.185]
const points = [10, 25, 60, 130, 300]"""
assert old_cfg in t, "配置数组找不到"
t = t.replace(old_cfg, new_cfg, 1)
# 3) 球图数组(顺序 blue->green->pink->red->super
old_balls = "const ballImages = [pinkBall, yellowBall, cyanBall, redBall, blueBall, superBall]"
new_balls = "const ballImages = [blueStar, greenStar, pinkStar, redStar, superStar]"
assert old_balls in t, "ballImages 找不到"
t = t.replace(old_balls, new_balls, 1)
# 4) 存储 key
t = t.replace("const AXOLOTL_MERGE_BEST_STORAGE_KEY = 'axolotl-merge-best-score'",
"const AXOLOTL_MERGE_BEST_STORAGE_KEY = 'starlight-merge-best-score'", 1)
# 5) 文案messages
text_map = {
"defaultMessage: 'The axolotls got stranded!'": "defaultMessage: 'The stars fell down!'",
"defaultMessage: 'Rainbow axolotl!'": "defaultMessage: 'Nether star!'",
"defaultMessage: 'The tide is falling — keep your axolotls underwater!'": "defaultMessage: 'The sky is falling — keep merging!'",
"defaultMessage: 'Click to drop a pink axolotl'": "defaultMessage: 'Click to drop a star'",
}
for o, n in text_map.items():
assert o in t, f"文案找不到: {o}"
t = t.replace(o, n, 1)
open(p, "w", encoding="utf-8", newline="\n").write(t)
print("done")

View File

@ -0,0 +1,32 @@
p = r"D:\Project\Starlight_Lancher\apps\app-frontend\src\components\ui\AboutScene.vue"
t = open(p, encoding="utf-8").read()
# 1) 删 import 行
old_imp = "import { type GLTF, GLTFLoader } from 'three/examples/jsm/Addons.js'\n"
assert old_imp in t, "import 行找不到"
t = t.replace(old_imp, "", 1)
# 2) 删 loadGLTF 函数(含前后空行)
old_fn = """function loadGLTF(url: string): Promise<GLTF> {
return new Promise((res, rej) => {
const loader = new GLTFLoader()
loader.load(
url,
(data) => {
res(data)
},
undefined,
rej,
)
})
}
"""
assert old_fn in t, "loadGLTF 函数找不到"
t = t.replace(old_fn, "", 1)
open(p, "w", encoding="utf-8", newline="\n").write(t)
print("done")
print("残留 GLTFLoader:", "GLTFLoader" in t)
print("残留 loadGLTF:", "loadGLTF" in t)

View File

@ -0,0 +1,42 @@
import struct
p = r"D:\Project\Starlight_Lancher\target\release\Starlight Launcher.exe"
data = open(p, "rb").read()
# 粗解析 PE找 .rsrc 段的 RT_GROUP_ICON 和 RT_ICON
# 简化:直接找所有 PNG现代 ico 用 png 存大图)和 BMP(ico用)
# 提取第一个 PNG 看尺寸
import io
PNG_SIG = b"\x89PNG\r\n\x1a\n"
results = []
idx = 0
while True:
i = data.find(PNG_SIG, idx)
if i < 0:
break
# PNG IHDR: 宽高在 sig 后 16 字节起 (8 sig + 4 len + 4 'IHDR' = 16)
try:
w = struct.unpack(">I", data[i+16:i+20])[0]
h = struct.unpack(">I", data[i+20:i+24])[0]
if 8 <= w <= 1024 and 8 <= h <= 1024:
results.append((i, w, h))
except Exception:
pass
idx = i + 8
print("找到疑似图标 PNG (offset,w,h) 前 30 个:")
for r in results[:30]:
print(r)
# 对比icon.ico 里各图像尺寸
ico = open(r"D:\Project\Starlight_Lancher\apps\app\icons\icon.ico", "rb").read()
n = struct.unpack("<H", ico[4:6])[0]
print(f"\nicon.ico 含 {n} 个图像:")
off = 6
for k in range(n):
w = ico[off] or 256
h = ico[off+1] or 256
size = struct.unpack("<I", ico[off+8:off+12])[0]
print(f" 图像{k}: {w}x{h}, {size} bytes")
off += 16

View File

@ -0,0 +1,10 @@
p = r"D:\Project\Starlight_Lancher\apps\app-frontend\src\components\ui\settings\AboutSettings.vue"
t = open(p, encoding="utf-8").read()
old = "defaultMessage: 'Starlight Launcher is a modified version of the open-source Modrinth codebase.',"
new = "defaultMessage: 'Starlight Launcher is a modified version of the Axolotl Launcher, which is based on the open-source Modrinth codebase.',"
assert old in t, "找不到第162行原句"
t = t.replace(old, new, 1)
open(p, "w", encoding="utf-8", newline="\n").write(t)
print("done")
print("新句:", new)

View File

@ -0,0 +1,20 @@
f1 = r"D:\Project\Starlight_Lancher\apps\app-frontend\src\components\ui\settings\AboutSettings.vue"
t1 = open(f1, encoding="utf-8").read()
old1 = "defaultMessage: 'Copyright © Starlight All Rights Reserved.',"
new1 = "defaultMessage: 'Copyright © Axolotl All Rights Reserved.',"
assert old1 in t1, "AboutSettings 找不到版权行"
t1 = t1.replace(old1, new1, 1)
open(f1, "w", encoding="utf-8", newline="\n").write(t1)
f2 = r"D:\Project\Starlight_Lancher\apps\app-frontend\src\pages\Settings.vue"
t2 = open(f2, encoding="utf-8").read()
old2 = "Powered by Starlight"
new2 = "Powered by Axolotl"
assert old2 in t2, "Settings 找不到 Powered by 行"
t2 = t2.replace(old2, new2, 1)
open(f2, "w", encoding="utf-8", newline="\n").write(t2)
print("done")
print("AboutSettings:142 ->", new1)
print("Settings:379 ->", new2)

View File

@ -0,0 +1,13 @@
p = r"D:\Project\Starlight_Lancher\apps\app\tauri.conf.json"
lines = open(p, encoding="utf-8").read().split("\n")
correct = ' "frame-src": "https://www.youtube.com https://www.youtube-nocookie.com https://discord.com \'self\' axolotl-skin://localhost http://axolotl-skin.localhost https://skin.starlight.cool",'
print("BEFORE 129-135:")
for i in range(128, 135):
print(f"{i+1}: {lines[i]}")
# 合并 131-134 (idx 130..133) 为一行
lines[130:134] = [correct]
open(p, "w", encoding="utf-8", newline="\n").write("\n".join(lines))
print("AFTER 128-135:")
for i in range(127, 135):
print(f"{i+1}: {lines[i]}")

28
tools/icon_work/fix_fg.py Normal file
View File

@ -0,0 +1,28 @@
p = r"D:\Project\Starlight_Lancher\apps\app\src\lightweight_mode.rs"
t = open(p, encoding="utf-8").read()
# 1) import 加 SetForegroundWindow
old_import = """ use windows::Win32::UI::WindowsAndMessaging::{
GetWindowThreadProcessId, IsWindowVisible, SW_MAXIMIZE, ShowWindow,
};"""
new_import = """ use windows::Win32::UI::WindowsAndMessaging::{
GetWindowThreadProcessId, IsWindowVisible, SW_MAXIMIZE, SetForegroundWindow, ShowWindow,
};"""
assert old_import in t, "找不到 import 块"
t = t.replace(old_import, new_import, 1)
# 2) 最大化前先聚焦到前台
old_show = " let _ = unsafe { ShowWindow(hwnd, SW_MAXIMIZE) };"
new_show = (
" let _ = unsafe { SetForegroundWindow(hwnd) };\n"
" let _ = unsafe { ShowWindow(hwnd, SW_MAXIMIZE) };"
)
assert old_show in t, "找不到 ShowWindow 行"
t = t.replace(old_show, new_show, 1)
open(p, "w", encoding="utf-8", newline="\n").write(t)
print("done")
l = t.split("\n")
for i in range(354, 372):
print(f"{i+1}: {l[i]}")

View File

@ -0,0 +1,32 @@
import os, glob
ROOT = r"D:\Project\Starlight_Lancher"
LOCALES = os.path.join(ROOT, "apps", "app-frontend", "src", "locales")
REPLACEMENTS = {
"en-US": (
'"message": "Starlight Launcher is a modified version of the open-source Modrinth codebase."',
'"message": "Starlight Launcher is a modified version of the Axolotl Launcher, which is based on the open-source Modrinth codebase."',
),
"zh-CN": (
'"message": "Starlight Launcher 是开源项目 Modrinth 代码库的修改版本。"',
'"message": "Starlight Launcher 是基于 Axolotl 启动器修改的版本Axolotl 启动器基于开源项目 Modrinth 代码库。"',
),
"zh-TW": (
'"message": "Starlight Launcher 是開源專案 Modrinth 程式碼庫的修改版本。"',
'"message": "Starlight Launcher 是基於 Axolotl 啟動器修改的版本Axolotl 啟動器基於開源專案 Modrinth 程式碼庫。"',
),
}
for lang, (old, new) in REPLACEMENTS.items():
p = os.path.join(LOCALES, lang, "index.json")
t = open(p, encoding="utf-8").read()
if old not in t:
print(f"{lang}: 找不到原句,跳过")
continue
t = t.replace(old, new, 1)
open(p, "w", encoding="utf-8", newline="\n").write(t)
import json
json.load(open(p, encoding="utf-8")) # 校验合法
print(f"{lang}: 已改")

26
tools/icon_work/fix_lp.py Normal file
View File

@ -0,0 +1,26 @@
p = r"D:\Project\Starlight_Lancher\apps\app-frontend\src\components\home\HomeLaunchProgress.vue"
t = open(p, encoding="utf-8").read()
# 1) 给最外层 section 加 v-if
t = t.replace(
'\t<section\n\t\tclass="flex min-w-0 flex-col gap-3 border-0 border-b-[1px] border-solid border-[--brand-gradient-border] p-4"\n\t>',
'\t<section\n\t\tv-if="hasProgress"\n\t\tclass="flex min-w-0 flex-col gap-3 border-0 border-b-[1px] border-solid border-[--brand-gradient-border] p-4"\n\t>',
)
# 2) 删空态 <p v-if="!hasProgress">...</p>
import re
t = re.sub(
r'\t\t<p v-if="!hasProgress"[^>]*>\n\t\t\t\{\{ formatMessage\(messages\.empty\) \}\}\n\t\t</p>\n',
"",
t,
)
# 3) <ul v-else ...> 改 <ul ...>
t = t.replace('<ul v-else class="m-0 flex list-none flex-col gap-2 p-0">', '<ul class="m-0 flex list-none flex-col gap-2 p-0">')
open(p, "w", encoding="utf-8", newline="\n").write(t)
print("done")
print("--- 78-95 ---")
for i, line in enumerate(t.split("\n")[77:95], 78):
print(f"{i}: {line}")

View File

@ -0,0 +1,72 @@
p = r"D:\Project\Starlight_Lancher\apps\app-frontend\src\components\home\HomePinnedServers.vue"
t = open(p, encoding="utf-8").read()
# 1) servers computed 简化为"只返回收藏服务器"
old_servers = """const servers = computed(() => {
const favoriteServers = favoriteWorlds.value.flatMap((world) => {
if (world.type !== 'server' || world.address === PROTECTED_SERVER_ADDRESS) return []
const instance = instanceById.value.get(world.instance_id)
return instance ? [{ instance, world: world as ServerWorld & WorldWithInstance }] : []
})
const protectedInstance =
props.instances.find((instance) => instance.install_stage === 'installed') ?? props.instances[0]
if (!protectedInstance) return favoriteServers
const protectedServer: ServerWorld & WorldWithInstance = {
instance_id: protectedInstance.id,
name: formatMessage(messages.protectedServerName),
last_played: undefined,
icon: undefined,
display_status: 'favorite',
type: 'server',
index: -1,
address: PROTECTED_SERVER_ADDRESS,
pack_status: 'prompt',
}
return [{ instance: protectedInstance, world: protectedServer }, ...favoriteServers]
})"""
new_servers = """const servers = computed(() =>
favoriteWorlds.value.flatMap((world) => {
if (world.type !== 'server') return []
const instance = instanceById.value.get(world.instance_id)
return instance ? [{ instance, world: world as ServerWorld & WorldWithInstance }] : []
}),
)"""
assert old_servers in t, "servers computed 找不到"
t = t.replace(old_servers, new_servers, 1)
# 2) 删 protectedServerName message
old_msg = """ protectedServerName: {
id: 'app.home.servers.protected-name',
defaultMessage: 'Starlight Server',
},
"""
assert old_msg in t, "protectedServerName message 找不到"
t = t.replace(old_msg, "", 1)
# 3) import 去掉 PROTECTED_SERVER_ADDRESS
t = t.replace("\tPROTECTED_SERVER_ADDRESS,\n", "", 1)
# 4) 模板:去掉 v-if让所有服务器都能取消固定
t = t.replace(
""" <ButtonStyled
v-if="server.world.address !== PROTECTED_SERVER_ADDRESS"
circular
size="small"
type="transparent"
class="home-server-menu"
>""",
""" <ButtonStyled
circular
size="small"
type="transparent"
class="home-server-menu"
>""",
1,
)
open(p, "w", encoding="utf-8", newline="\n").write(t)
print("HomePinnedServers done")
print(" 残留 PROTECTED:", "PROTECTED" in t)
print(" 残留 protectedServerName:", "protectedServerName" in t)

View File

@ -0,0 +1,22 @@
p = r"D:\Project\Starlight_Lancher\apps\app-frontend\package.json"
t = open(p, encoding="utf-8").read()
# 1) build 去掉 contributors:sync
t = t.replace(
'"build": "pnpm contributors:sync && vue-tsc --noEmit && vite build",',
'"build": "vue-tsc --noEmit && vite build",',
)
# 2) 删除 contributors:sync 那一行
t = t.replace(
'\t\t"contributors:sync": "node ../../scripts/axolotl/sync-contributors.mjs",\n',
"",
)
open(p, "w", encoding="utf-8", newline="\n").write(t)
import json
json.load(open(p, encoding="utf-8")) # 校验合法
print("done")
l = t.split("\n")
for i in range(5, 13):
print(f"{i+1}: {l[i]}")

View File

@ -0,0 +1,10 @@
p = r"D:\Project\Starlight_Lancher\apps\app-frontend\src\components\ui\settings\AboutSettings.vue"
t = open(p, encoding="utf-8").read()
old = "const secretCodes = ['cyf112233', 'cxkcxkckx']"
new = "const secretCodes = ['starlight']"
assert old in t, "找不到 secretCodes"
t = t.replace(old, new, 1)
open(p, "w", encoding="utf-8", newline="\n").write(t)
print("done")
print("新:", new)

View File

@ -0,0 +1,8 @@
import json
p = r"D:\Project\Starlight_Lancher\apps\app-frontend\package.json"
data = json.load(open(p, encoding="utf-8"))
old = data["version"]
data["version"] = "1.0.0-beta1"
open(p, "w", encoding="utf-8", newline="\n").write(json.dumps(data, ensure_ascii=False, indent="\t") + "\n")
print(f"version: {old} -> 1.0.0-beta1")

View File

@ -0,0 +1,26 @@
p = r"D:\Project\Starlight_Lancher\apps\app-frontend\src\components\ui\world\WorldItem.vue"
t = open(p, encoding="utf-8").read()
# 1) 删 protectedServer 定义
old_def = """const protectedServer = computed(
() => props.world.type === 'server' && props.world.address === PROTECTED_SERVER_ADDRESS,
)
"""
assert old_def in t, "protectedServer 定义找不到"
t = t.replace(old_def, "", 1)
# 2) import 去掉 PROTECTED_SERVER_ADDRESS
t = t.replace("\tPROTECTED_SERVER_ADDRESS,\n", "", 1)
# 3) 逻辑里去掉 protectedServer
t = t.replace("shown: !instanceId && !protectedServer,", "shown: !instanceId,", 1) # edit
t = t.replace("disabled: locked || managed || protectedServer,", "disabled: locked || managed,", 1) # edit
t = t.replace("shown: !!homePinTarget && !protectedServer,", "shown: !!homePinTarget,", 1) # pin
t = t.replace("shown: !instanceId && !protectedServer,", "shown: !instanceId,", 1) # delete
t = t.replace("disabled: locked || managed || protectedServer,", "disabled: locked || managed,", 1) # delete
open(p, "w", encoding="utf-8", newline="\n").write(t)
print("WorldItem done")
print(" 残留 protectedServer:", "protectedServer" in t)
print(" 残留 PROTECTED_SERVER:", "PROTECTED_SERVER" in t)

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,6 @@
import glob
for f in glob.glob("packages/app-lib/src/event/*.rs"):
print("=== " + f + " ===")
print(open(f, encoding="utf-8").read()[:4000])
print()

View File

@ -0,0 +1,9 @@
t = open(r"D:\Project\Starlight_Lancher\tools\icon_work\old_merge_game.vue", encoding="utf-8").read()
lines = t.split("\n")
print("总行数:", len(lines))
print("\n=== 含 ball/color/radius/point/axolotl/import 的行 ===")
for i, line in enumerate(lines):
low = line.lower()
if any(k in low for k in ("ball", "color", "radius", "point", "axolotl", "import ", "level", "tier")):
print(f"{i+1}: {line.rstrip()[:140]}")

View File

@ -0,0 +1,9 @@
t = open(r"D:\Project\Starlight_Lancher\tools\icon_work\old_merge_game.vue", encoding="utf-8").read()
lines = t.split("\n")
print("=== messages 全貌 (221-253) ===")
for i in range(220, 254):
print(f"{i+1}: {lines[i]}")
print("\n=== drawAxolotl 绘制 (689-757) ===")
for i in range(688, 757):
print(f"{i+1}: {lines[i]}")

View File

@ -0,0 +1,48 @@
p = r"D:\Project\Starlight_Lancher\apps\app-frontend\src\components\ui\AboutScene.vue"
t = open(p, encoding="utf-8").read()
old = """ async function load() {
const axlGLTF = await loadGLTF('/models/axolotl.gltf')
const axlModel = axlGLTF.scene
axlModel.scale.multiplyScalar(5)
axlModel.rotateY(Math.PI / 2)
axlModel.position.add(new THREE.Vector3(0, -2.5, 0))
scene.add(axlModel)
const mixer = new THREE.AnimationMixer(axlModel)
const axlSwimAnim = axlGLTF.animations.filter((a) => a.name === 'swim')[0]
if (!axlSwimAnim) return console.error('Missing animation swim')
mixer.clipAction(axlSwimAnim).play()
// // Axl Label
// const axlLabelGLTF = await loadGLTF('/models/axl_label.glb')
// const axlLabel = axlLabelGLTF.scene
// axlLabel.scale.multiplyScalar(8)
// axlLabel.rotateY(-Math.PI / 2)
// axlLabel.position.set(0, 5.2, 0)
// scene.add(axlLabel)
const originAxlModelPosition = axlModel.position.clone()
return function (deltaTime: number, elapsedTime: number) {
axlModel.position.set(
originAxlModelPosition.x,
originAxlModelPosition.y + Math.sin(elapsedTime),
originAxlModelPosition.z,
)
axlModel.rotation.y = Math.sin(elapsedTime * 0.3) * 0.2 + (Math.PI * 100) / 180
mixer.update(deltaTime)
}
}
let updateGLTF = (_deltaTime: number, _elapsedTime: number) => {}
load().then((updateFn) => {
if (updateFn) updateGLTF = updateFn
})"""
new = """ let updateGLTF = (_deltaTime: number, _elapsedTime: number) => {}"""
assert old in t, "找不到美西螈加载块"
t = t.replace(old, new, 1)
open(p, "w", encoding="utf-8", newline="\n").write(t)
print("done")

View File

@ -0,0 +1,42 @@
import os, re
ROOT = r"D:\Project\Starlight_Lancher"
DIRS = [
"apps/app-frontend/src",
"packages/ui/src",
"packages/app-lib/src",
"apps/app/src",
]
KEYWORDS = re.compile(
r"Copyright|All Rights|Powered by|attribution|Attribution|"
r"modified version|based on|版权所有|版权归|署名|致谢|鸣谢|"
r"credit|Credit|acknowledg|Acknowledg",
re.I,
)
hits = []
for d in DIRS:
base = os.path.join(ROOT, d)
if not os.path.isdir(base):
continue
for dp, dn, fn in os.walk(base):
dn[:] = [x for x in dn if x not in ("node_modules", "dist", "target", ".git")]
for f in fn:
if not f.endswith((".ts", ".vue", ".rs", ".js", ".json")):
continue
p = os.path.join(dp, f)
if "locales" in p and not p.endswith("en-US/index.json") and not p.endswith("zh-CN/index.json"):
continue
try:
lines = open(p, encoding="utf-8").read().split("\n")
except Exception:
continue
for i, line in enumerate(lines, 1):
if "Starlight" in line and KEYWORDS.search(line):
hits.append((os.path.relpath(p, ROOT), i, line.strip()))
print(f"含版权/归属性质且提到 Starlight 的行:{len(hits)}\n")
for h in hits:
print(f"{h[0]}:{h[1]}")
print(f" {h[2][:160]}")

View File

@ -0,0 +1,5 @@
l = open("packages/app-lib/src/launcher/mod.rs", encoding="utf-8").read().split("\n")
for i, line in enumerate(l):
if "init_loading" in line or "emit_loading" in line or "MinecraftDownload" in line or "InstanceUpdate" in line:
print(f"{i+1}: {line.strip()}")

View File

@ -0,0 +1,24 @@
import os, glob
ROOT = r"D:\Project\Starlight_Lancher"
LOCALES = os.path.join(ROOT, "apps", "app-frontend", "src", "locales")
KEYS = [
"app.settings.about.copyright",
"app.settings.about.attribution",
]
for path in sorted(glob.glob(os.path.join(LOCALES, "*", "index.json"))):
lang = os.path.basename(os.path.dirname(path))
try:
lines = open(path, encoding="utf-8").read().split("\n")
except Exception:
continue
for i, line in enumerate(lines):
for key in KEYS:
if f'"{key}"' in line:
# 取下一行的 message
msg = lines[i + 1].strip() if i + 1 < len(lines) else ""
print(f"{lang} | {key}")
print(f" {msg[:150]}")

View File

@ -0,0 +1,9 @@
t = open(r"D:\Project\Starlight_Lancher\apps\app-frontend\src\components\ui\AboutEasterEgg.vue", encoding="utf-8").read()
for i, line in enumerate(t.split("\n")):
if any(k in line for k in ("import blueStar", "import greenStar", "import pinkStar", "import redStar", "import yellowStar", "import superStar",
"const colors =", "const ballImages =", "starlight-merge-best-score",
"stars fell down", "Nether star", "sky is falling", "drop a star")):
print(f"{i+1}: {line.strip()}")
print("\n残留 axolotl-balls:", "axolotl-balls" in t)
print("残留 axolotl 文案:", "axolotl" in t.lower())

View File

@ -0,0 +1,7 @@
a = open(r"D:\Project\Starlight_Lancher\apps\app-frontend\src\components\ui\settings\AboutSettings.vue", encoding="utf-8").read()
b = open(r"D:\Project\Starlight_Lancher\apps\app-frontend\src\pages\Settings.vue", encoding="utf-8").read()
print("AboutSettings 含 Axolotl 版权:", "Copyright \u00a9 Axolotl All Rights Reserved." in a)
print("AboutSettings 还含 Starlight 版权:", "Copyright \u00a9 Starlight All Rights Reserved." in a)
print("Settings 含 Powered by Axolotl:", "Powered by Axolotl" in b)
print("Settings 还含 Powered by Starlight:", "Powered by Starlight" in b)

View File

@ -0,0 +1,29 @@
p = r"D:\Project\Starlight_Lancher\apps\app-frontend\src\pages\Index.vue"
lines = open(p, encoding="utf-8").read().split("\n")
# 1) import在 HomePlayInsights import 行后加一行
imp_i = None
for i, ln in enumerate(lines):
if "import HomePlayInsights from '@/components/home/HomePlayInsights.vue'" in ln:
imp_i = i
break
assert imp_i is not None, "找不到 HomePlayInsights import"
lines.insert(imp_i, "import HomeLaunchProgress from '@/components/home/HomeLaunchProgress.vue'")
# 2) 模板:在 <HomePlayInsights /> 前插入 <HomeLaunchProgress />
tpl_i = None
for i, ln in enumerate(lines):
if ln.strip() == "<HomePlayInsights />":
tpl_i = i
break
assert tpl_i is not None, "找不到 <HomePlayInsights />"
lines.insert(tpl_i, "\t\t\t<HomeLaunchProgress />")
open(p, "w", encoding="utf-8", newline="\n").write("\n".join(lines))
print("done")
for i in range(imp_i - 1, imp_i + 2):
print(f"{i+1}: {lines[i]}")
print("...")
for i in range(tpl_i - 2, tpl_i + 3):
print(f"{i+1}: {lines[i]}")