lookatstudy-mobile 0.34.0 → 0.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/install-termux.sh +169 -25
  2. package/package.json +1 -1
  3. package/server.cjs +1796 -5809
  4. package/web/assets/{CanvasStage-CpZeMgwT.js → CanvasStage-C72HaOiw.js} +1 -1
  5. package/web/assets/{CommandPalette-BlRArZBr.js → CommandPalette-cB6g-mzh.js} +1 -1
  6. package/web/assets/{CourseSearchPanel-C7KIDdON.js → CourseSearchPanel-C4Kh0qi3.js} +1 -1
  7. package/web/assets/{ExamView-8XI8Nw1H.js → ExamView-dpph-olw.js} +1 -1
  8. package/web/assets/{ReviewPanel-CTzPBfgZ.js → ReviewPanel-DynCNoAR.js} +1 -1
  9. package/web/assets/SettingsView-CislD6Xj.js +20 -0
  10. package/web/assets/{VoicePanel-BhC8Hu-3.js → VoicePanel-BgdvFoyX.js} +1 -1
  11. package/web/assets/{abnfDiagram-VCTEODGH-CTwnb_LX.js → abnfDiagram-VCTEODGH-CMtB3HOl.js} +1 -1
  12. package/web/assets/{abnfDiagram-VCTEODGH-ODA7YWAP-C1dBUZ7b.js → abnfDiagram-VCTEODGH-ODA7YWAP-BvhKfjaY.js} +1 -1
  13. package/web/assets/api-web-Dwfo27My.js +1 -0
  14. package/web/assets/{architectureDiagram-5GKGNRK7-C4PU3XN7-ChothZwf.js → architectureDiagram-5GKGNRK7-C4PU3XN7-CSMXYZiY.js} +1 -1
  15. package/web/assets/{architectureDiagram-5GKGNRK7-DOTrgigK.js → architectureDiagram-5GKGNRK7-CHnFIH9b.js} +1 -1
  16. package/web/assets/{arrow-right-BQgpe45F.js → arrow-right-TM3y6L-Y.js} +1 -1
  17. package/web/assets/{blockDiagram-I7D4REHJ-DyYXGdPV.js → blockDiagram-I7D4REHJ-VdzqHZvv.js} +1 -1
  18. package/web/assets/{blockDiagram-NRAW4CY4-UMJ6PMQQ-DOFYspw-.js → blockDiagram-NRAW4CY4-UMJ6PMQQ-D7kvNED-.js} +1 -1
  19. package/web/assets/{c4Diagram-7LVT6UL2-0AsslgW1.js → c4Diagram-7LVT6UL2-CgznJmUi.js} +1 -1
  20. package/web/assets/{c4Diagram-UCG6FXSJ-75X3U2P3-B96ZJV69.js → c4Diagram-UCG6FXSJ-75X3U2P3-B9xFuSPH.js} +1 -1
  21. package/web/assets/channel-Df7Ybeyp.js +1 -0
  22. package/web/assets/{chunk-2Q5K7J3B-YcEvx37Y.js → chunk-2Q5K7J3B-CAns8y8j.js} +1 -1
  23. package/web/assets/{chunk-5VM5RSS4-BFI2AG6f.js → chunk-5VM5RSS4-C2nhzqYi.js} +1 -1
  24. package/web/assets/{chunk-7XDTSPCN-BjlU6ynD.js → chunk-7XDTSPCN-Du4JZkXG.js} +1 -1
  25. package/web/assets/{chunk-CTNIYPP6-LyWaVAeX.js → chunk-CTNIYPP6-B7lfk-YJ.js} +1 -1
  26. package/web/assets/{chunk-CXNFBKHE-9OIQHuxw.js → chunk-CXNFBKHE-BC4UXdAm.js} +1 -1
  27. package/web/assets/{chunk-F27PBJKO-C1TvQsHt.js → chunk-F27PBJKO-LQpoAT6J.js} +1 -1
  28. package/web/assets/{chunk-IMKFNOWR-B7G8Z_Ng.js → chunk-IMKFNOWR-Ctz3Z-uw.js} +1 -1
  29. package/web/assets/{chunk-ISA7TJRJ-BL4KA7xt.js → chunk-ISA7TJRJ-CxiQRkRL.js} +1 -1
  30. package/web/assets/{chunk-JWPE2WC7-CH4FMDWX.js → chunk-JWPE2WC7-yepkRAwM.js} +1 -1
  31. package/web/assets/{chunk-LV3HUYFY-Ch9MhjBs.js → chunk-LV3HUYFY-CIjD4C-e.js} +1 -1
  32. package/web/assets/{chunk-M3IUQBOH-BcqMygxc.js → chunk-M3IUQBOH-B_b1jC1d.js} +1 -1
  33. package/web/assets/{chunk-NDIOYS4C-DE45Abjh.js → chunk-NDIOYS4C-Dnzhfg9r.js} +1 -1
  34. package/web/assets/{chunk-POPQ4Y6H-D-SBQItu.js → chunk-POPQ4Y6H-fHcz0X8j.js} +1 -1
  35. package/web/assets/{chunk-RE4WF27V-C1subsXL.js → chunk-RE4WF27V-CxX4BnEj.js} +1 -1
  36. package/web/assets/{chunk-SVP7TREG-cLlInCCA.js → chunk-SVP7TREG-BnltLrM-.js} +1 -1
  37. package/web/assets/{chunk-TICWLB2K-DxNEFrwG.js → chunk-TICWLB2K-D12wpFc6.js} +1 -1
  38. package/web/assets/{chunk-VII2H2IX-ymoFJHrt.js → chunk-VII2H2IX-Twcv7Nfl.js} +1 -1
  39. package/web/assets/{chunk-X5GNSKZ3-Cce97LK8.js → chunk-X5GNSKZ3-BL1R0B0m.js} +1 -1
  40. package/web/assets/{chunk-XCWWM2C3-DDsEpbkx.js → chunk-XCWWM2C3-Tg4IR0n8.js} +1 -1
  41. package/web/assets/{chunk-XXDRQBXY-BlitsaYT.js → chunk-XXDRQBXY-DJoPR1YB.js} +1 -1
  42. package/web/assets/classDiagram-DTDB5LWJ-LWRDKZ5K-B6JY0Amc.js +1 -0
  43. package/web/assets/classDiagram-ZZMXUADV-CW-v2a7Q.js +1 -0
  44. package/web/assets/classDiagram-v2-JRS7N3AN-2BZHGRP2-B6JY0Amc.js +1 -0
  45. package/web/assets/classDiagram-v2-VYDZK3BY-CW-v2a7Q.js +1 -0
  46. package/web/assets/{core-Cz8RF5Ss.js → core-CInUNwIn.js} +1 -1
  47. package/web/assets/{cose-bilkent-JH36ORCC-Z1v0LKQQ.js → cose-bilkent-JH36ORCC-Bs86DA0h.js} +1 -1
  48. package/web/assets/{cose-bilkent-JH36ORCC-OB24KH3T-CHMfylG8.js → cose-bilkent-JH36ORCC-OB24KH3T-Dq3mWE76.js} +1 -1
  49. package/web/assets/{cynefin-OW5HDTMX-B8B-PAKZ.js → cynefin-OW5HDTMX-D6ZQlGMQ.js} +1 -1
  50. package/web/assets/{cynefin-OW5HDTMX-GVF3BKWE-B8BGNTw-.js → cynefin-OW5HDTMX-GVF3BKWE-Dnl8JJFM.js} +1 -1
  51. package/web/assets/{cynefinDiagram-5FMLGOSQ-Dx-FYIVc.js → cynefinDiagram-5FMLGOSQ-CCC-03k6.js} +1 -1
  52. package/web/assets/{cynefinDiagram-5FMLGOSQ-H3WIRU3N-BV3TRRUk.js → cynefinDiagram-5FMLGOSQ-H3WIRU3N-Dt1RAnjs.js} +1 -1
  53. package/web/assets/{dagre-3AP2YEHR-DCERTXHT-US6ZPid6.js → dagre-3AP2YEHR-DCERTXHT-B0t8ElJV.js} +1 -1
  54. package/web/assets/{dagre-GXQ25YYZ-DvgJpvrR.js → dagre-GXQ25YYZ-CgU7yrVy.js} +1 -1
  55. package/web/assets/{diagram-S7CK7UJ4-IA_lpeJb.js → diagram-S7CK7UJ4-DTR2y6UT.js} +1 -1
  56. package/web/assets/{diagram-S7CK7UJ4-MULGZGEL-9X64zpVt.js → diagram-S7CK7UJ4-MULGZGEL-DwX9tj_E.js} +1 -1
  57. package/web/assets/{diagram-UQ7AKVKN-BI77k4Eg.js → diagram-UQ7AKVKN-D2CVN862.js} +1 -1
  58. package/web/assets/{diagram-UQ7AKVKN-YQBY353J-BSFLZLn0.js → diagram-UQ7AKVKN-YQBY353J-LgNf4bhC.js} +1 -1
  59. package/web/assets/{diagram-VSXAHHWV-qtk4pu7a.js → diagram-VSXAHHWV-CYQLixjs.js} +1 -1
  60. package/web/assets/{diagram-VSXAHHWV-WLNH2ELY-BVJvpn3p.js → diagram-VSXAHHWV-WLNH2ELY-C2Plr2sR.js} +1 -1
  61. package/web/assets/{diagram-VX7I27RA-D1OUoA-L.js → diagram-VX7I27RA-BJQtFm-G.js} +1 -1
  62. package/web/assets/{diagram-VX7I27RA-XW6YQV5G-c8XHmzOy.js → diagram-VX7I27RA-XW6YQV5G-BkfQh4Mv.js} +1 -1
  63. package/web/assets/{diagram-Z3DM3KII-HG7RF65D-Cw_b8aFx.js → diagram-Z3DM3KII-HG7RF65D-BsWePKJ6.js} +1 -1
  64. package/web/assets/{diagram-Z3DM3KII-A7_8IFdk.js → diagram-Z3DM3KII-arIh_Ydh.js} +1 -1
  65. package/web/assets/{ebnfDiagram-PWID7BFC-OKY4VHYD-DpM76Qo-.js → ebnfDiagram-PWID7BFC-OKY4VHYD-CSDN-ES2.js} +1 -1
  66. package/web/assets/{ebnfDiagram-PWID7BFC-s9w7t4t-.js → ebnfDiagram-PWID7BFC-efb9oxoq.js} +1 -1
  67. package/web/assets/{erDiagram-RLTQ6QDP-BqZVVONb.js → erDiagram-RLTQ6QDP-BM3e1Eha.js} +1 -1
  68. package/web/assets/{erDiagram-SSCWMZ5O-PIPKOIK7-mvBqIA0l.js → erDiagram-SSCWMZ5O-PIPKOIK7-BkRkgiEn.js} +1 -1
  69. package/web/assets/{flowDiagram-A5DVABFB-2Z4AVO5R-Y6pTj-b3.js → flowDiagram-A5DVABFB-2Z4AVO5R-CNrxNr1X.js} +1 -1
  70. package/web/assets/{flowDiagram-HODETNUW-Bd7fJP9N.js → flowDiagram-HODETNUW-tIdWY3-u.js} +1 -1
  71. package/web/assets/{ganttDiagram-EL5Y4UJY-Bu47S_cy.js → ganttDiagram-EL5Y4UJY-CcbO-KDD.js} +1 -1
  72. package/web/assets/{ganttDiagram-EL5Y4UJY-JZNG7GOG-rP8k0IHr.js → ganttDiagram-EL5Y4UJY-JZNG7GOG-BZZl4cGf.js} +1 -1
  73. package/web/assets/{gitGraphDiagram-WWUBYQGX-DMWMLL3G-B2RIK0rX.js → gitGraphDiagram-WWUBYQGX-DMWMLL3G-BXh7EB8a.js} +1 -1
  74. package/web/assets/{gitGraphDiagram-WWUBYQGX-6TDOHnoT.js → gitGraphDiagram-WWUBYQGX-mUHSo0oW.js} +1 -1
  75. package/web/assets/{index-5Ng_lap_.js → index-B9KSAI26.js} +1 -1
  76. package/web/assets/{index-DqlC7JoQ.js → index-BzdORPhC.js} +1 -1
  77. package/web/assets/{index-DqnX7_zV.js → index-CtcKtEMF.js} +3 -3
  78. package/web/assets/{infoDiagram-27XIBGKW-CNG9rO3x.js → infoDiagram-27XIBGKW-6zuJOkud.js} +1 -1
  79. package/web/assets/{infoDiagram-RXCK75RN-HANS5KVC-BLQAuzZo.js → infoDiagram-RXCK75RN-HANS5KVC-DgNsPCDB.js} +1 -1
  80. package/web/assets/{ishikawaDiagram-5VMMS53U-7GCIT7WU-cUZxo0qy.js → ishikawaDiagram-5VMMS53U-7GCIT7WU-C1aCi21z.js} +1 -1
  81. package/web/assets/{ishikawaDiagram-5VMMS53U-O4xGKyXs.js → ishikawaDiagram-5VMMS53U-CfBlX7z8.js} +1 -1
  82. package/web/assets/{journeyDiagram-3NMN7TZE-CXvTN2XW.js → journeyDiagram-3NMN7TZE-CsVwn0uz.js} +1 -1
  83. package/web/assets/{journeyDiagram-EYS64GPL-OAPELBT7-DkfyTyBt.js → journeyDiagram-EYS64GPL-OAPELBT7-GmOyjRKs.js} +1 -1
  84. package/web/assets/{kanban-definition-3QL26DDD-I7RKWQQY-CgdUNDmE.js → kanban-definition-3QL26DDD-I7RKWQQY-DYO1LivP.js} +1 -1
  85. package/web/assets/{kanban-definition-UXKFOSKX-CXs9ROJB.js → kanban-definition-UXKFOSKX-BGaJOKpZ.js} +1 -1
  86. package/web/assets/{katex-N6JRN4PM-BjAz2jJx.js → katex-N6JRN4PM-CCO3fQlh.js} +1 -1
  87. package/web/assets/{layout-Cp9w3JfL.js → layout-DxwbGgTi.js} +1 -1
  88. package/web/assets/{main-YTxb38yo.js → main-CSS_lFDP.js} +95 -95
  89. package/web/assets/{mermaid-layout-elk.core-DUbLagu1.js → mermaid-layout-elk.core-D9OOh4D_.js} +2 -2
  90. package/web/assets/{mermaid.core-Czgmmu3m.js → mermaid.core-BEhN9Nie.js} +5 -5
  91. package/web/assets/{mindmap-definition-FBJOCRG2-KIYZG64Y-DTs3kC-2.js → mindmap-definition-FBJOCRG2-KIYZG64Y-ZPfoLqVV.js} +1 -1
  92. package/web/assets/{mindmap-definition-YA3MSWOX-8ZLAntKe.js → mindmap-definition-YA3MSWOX-D-0q8D0x.js} +1 -1
  93. package/web/assets/{pegDiagram-XKGWAZYB-Ddy9tOBo.js → pegDiagram-XKGWAZYB-BV_8eJIL.js} +1 -1
  94. package/web/assets/{pegDiagram-XKGWAZYB-OIP57N6J-CX1pDkyu.js → pegDiagram-XKGWAZYB-OIP57N6J-DqRF2GbT.js} +1 -1
  95. package/web/assets/{pieDiagram-E7YTZNPT-2UOTW327-CJbVQ7j0.js → pieDiagram-E7YTZNPT-2UOTW327-Cr4fAU8o.js} +1 -1
  96. package/web/assets/{pieDiagram-E7YTZNPT-bhM8Af8W.js → pieDiagram-E7YTZNPT-nB-_CLeT.js} +1 -1
  97. package/web/assets/{quadrantDiagram-AXDQQJYC-BPNVp-tm.js → quadrantDiagram-AXDQQJYC-BNAlPSkj.js} +1 -1
  98. package/web/assets/{quadrantDiagram-AXDQQJYC-SN3ASJJK-JrUYqoH6.js → quadrantDiagram-AXDQQJYC-SN3ASJJK-Cf5G05Po.js} +1 -1
  99. package/web/assets/{railroadDiagram-O6MQD6OU-34742W6X-CNPl0cRd.js → railroadDiagram-O6MQD6OU-34742W6X-CFH7Z2dw.js} +1 -1
  100. package/web/assets/{railroadDiagram-O6MQD6OU-Djl7WlNw.js → railroadDiagram-O6MQD6OU-FF9J40BN.js} +1 -1
  101. package/web/assets/{render-O7CIS3YK-SgZRy1rl.js → render-O7CIS3YK-CFk-qve4.js} +7 -7
  102. package/web/assets/{requirementDiagram-BXWQKSXE-DvRiMfSd.js → requirementDiagram-BXWQKSXE-BMV8VnvW.js} +1 -1
  103. package/web/assets/{requirementDiagram-EFPCY7ZU-NGAFFSMB-CetNYmyR.js → requirementDiagram-EFPCY7ZU-NGAFFSMB-Dgr3205x.js} +1 -1
  104. package/web/assets/{rotate-ccw-CmUerE0X.js → rotate-ccw-DZxkibR5.js} +1 -1
  105. package/web/assets/{sankeyDiagram-P5KCCOFB-3D3M4XPB-DTBggk4D.js → sankeyDiagram-P5KCCOFB-3D3M4XPB-CN03neoX.js} +1 -1
  106. package/web/assets/{sankeyDiagram-P5KCCOFB-Chf73i3x.js → sankeyDiagram-P5KCCOFB-yPJIv_Ow.js} +1 -1
  107. package/web/assets/{sequenceDiagram-WJ2MYXX4-DH8S7n6r.js → sequenceDiagram-WJ2MYXX4-BaWlvXkm.js} +1 -1
  108. package/web/assets/{sequenceDiagram-WJ2MYXX4-Q2IOPB4R-BaqhVeKy.js → sequenceDiagram-WJ2MYXX4-Q2IOPB4R-GEXmhp2H.js} +1 -1
  109. package/web/assets/{sizeCapture-INFHLROL-DoWkN5kB.js → sizeCapture-INFHLROL-DvbiIbXq.js} +1 -1
  110. package/web/assets/{sizeCapture-X5ZJPWSS-PSGVOMYM-D1w8fJEe.js → sizeCapture-X5ZJPWSS-PSGVOMYM-CLmCvgE-.js} +1 -1
  111. package/web/assets/{stateDiagram-D77RDMKH-C2Bmxn5D.js → stateDiagram-D77RDMKH-v3JAq8H4.js} +1 -1
  112. package/web/assets/{stateDiagram-HBIQ2CUA-4M2S6MAN-DmtO2hG4.js → stateDiagram-HBIQ2CUA-4M2S6MAN-Bk6n0C4h.js} +1 -1
  113. package/web/assets/stateDiagram-v2-4QOOHH4V-LBBLA2WV-CSGIWpWK.js +1 -0
  114. package/web/assets/stateDiagram-v2-MP3YSRHH-Bw5j-FVB.js +1 -0
  115. package/web/assets/{swimlanes-42K2YHIH-CtCB4ffC.js → swimlanes-42K2YHIH-DHz1p7KG.js} +1 -1
  116. package/web/assets/{swimlanes-XN3QIQJK-N6N2CCQ6-C7V48a-4.js → swimlanes-XN3QIQJK-N6N2CCQ6-HxeNMmfZ.js} +1 -1
  117. package/web/assets/swimlanesDiagram-VK2B7HYN-E6N4H5SY-BcGn1eC6.js +8 -0
  118. package/web/assets/swimlanesDiagram-VR7AAH4N-BORev-1v.js +8 -0
  119. package/web/assets/{timeline-definition-24CTP7MA-CGdYfJkN.js → timeline-definition-24CTP7MA-Dn1hyaRN.js} +1 -1
  120. package/web/assets/{timeline-definition-24CTP7MA-KN7RSRVM-CPntDgZR.js → timeline-definition-24CTP7MA-KN7RSRVM-DVlLI_ni.js} +1 -1
  121. package/web/assets/{vennDiagram-4TSXK5OY-AKJ7LXAN-CGIdSvWW.js → vennDiagram-4TSXK5OY-AKJ7LXAN-W1H4YhrJ.js} +1 -1
  122. package/web/assets/{vennDiagram-4TSXK5OY-BpWlDJtb.js → vennDiagram-4TSXK5OY-Dxezw6vn.js} +1 -1
  123. package/web/assets/{wardleyDiagram-VM6X3IG4-3B6A3BN5-DXGnwCFx.js → wardleyDiagram-VM6X3IG4-3B6A3BN5-y4QqLWww.js} +1 -1
  124. package/web/assets/{wardleyDiagram-VM6X3IG4-DqcLqOTM.js → wardleyDiagram-VM6X3IG4-BJzvdena.js} +1 -1
  125. package/web/assets/{xychartDiagram-S5SC5T6Z-VQI7mnX_.js → xychartDiagram-S5SC5T6Z-DePRnwoY.js} +1 -1
  126. package/web/assets/{xychartDiagram-S5SC5T6Z-OBDNV6WG-DIO8oTNs.js → xychartDiagram-S5SC5T6Z-OBDNV6WG-CCeTp4-Y.js} +1 -1
  127. package/web/fouc.js +18 -0
  128. package/web/index.html +5 -21
  129. package/web/pet.html +1 -1
  130. package/web/assets/SettingsView-CoGqUXEr.js +0 -15
  131. package/web/assets/api-web-BZuBkYKX.js +0 -1
  132. package/web/assets/channel-CB0qYQAs.js +0 -1
  133. package/web/assets/classDiagram-DTDB5LWJ-LWRDKZ5K-gE6ypOty.js +0 -1
  134. package/web/assets/classDiagram-ZZMXUADV-6pfB_2-g.js +0 -1
  135. package/web/assets/classDiagram-v2-JRS7N3AN-2BZHGRP2-gE6ypOty.js +0 -1
  136. package/web/assets/classDiagram-v2-VYDZK3BY-6pfB_2-g.js +0 -1
  137. package/web/assets/stateDiagram-v2-4QOOHH4V-LBBLA2WV-BRoIf1d6.js +0 -1
  138. package/web/assets/stateDiagram-v2-MP3YSRHH-BVoQ569f.js +0 -1
  139. package/web/assets/swimlanesDiagram-VK2B7HYN-E6N4H5SY-B6zzyUKq.js +0 -8
  140. package/web/assets/swimlanesDiagram-VR7AAH4N-XUZui9QT.js +0 -8
package/install-termux.sh CHANGED
@@ -13,8 +13,9 @@ set -euo pipefail
13
13
  PORT="${LOOKATSTUDY_PORT:-17890}"
14
14
  APP_DIR="$HOME/lookatstudy"
15
15
  DATA_DIR="$HOME/.lookatstudy"
16
- GH_ASSET="https://github.com/Kaiji-Z/LookatStudy/releases/latest/download/lookatstudy-mobile.zip"
17
- GH_VOICE="https://github.com/Kaiji-Z/LookatStudy/releases/latest/download/lookatstudy-termux-voice.tar.gz"
16
+ GH_RELEASE="https://github.com/Kaiji-Z/LookatStudy/releases/latest/download"
17
+ GH_ASSET="${GH_RELEASE}/lookatstudy-mobile.zip"
18
+ GH_VOICE="${GH_RELEASE}/lookatstudy-termux-voice.tar.gz"
18
19
  # 直连优先,失败再走代理前缀(镜像只做回退:KaijiBot 教训是镜像同步延迟会坏事,大头收益在 apt 的 TUNA)
19
20
  DL_PREFIXES=("" "https://gh-proxy.com/" "https://ghproxy.net/" "https://ghfast.top/")
20
21
 
@@ -37,7 +38,9 @@ npm_tarball() {
37
38
  return 0
38
39
  fi
39
40
  fi
40
- printf '%s' "$json" | sed -n 's/.*"tarball":"\([^"]*\)".*/\1/p'
41
+ printf '%s\n%s\n' \
42
+ "$(printf '%s' "$json" | sed -n 's/.*"tarball":"\([^"]*\)".*/\1/p')" \
43
+ "$(printf '%s' "$json" | sed -n 's/.*"integrity":"\([^"]*\)".*/\1/p')"
41
44
  }
42
45
 
43
46
  # npm 官方源(权威、零同步滞后):镜像未命中/滞后时的第一兜底,先于 GitHub 链。
@@ -45,7 +48,9 @@ npm_tarball() {
45
48
  npm_tarball_official() {
46
49
  local json
47
50
  json=$(curl -sfL --connect-timeout 8 --max-time 20 "https://registry.npmjs.org/$1/latest" || true)
48
- printf '%s' "$json" | sed -n 's/.*"tarball":"\([^"]*\)".*/\1/p'
51
+ printf '%s\n%s\n' \
52
+ "$(printf '%s' "$json" | sed -n 's/.*"tarball":"\([^"]*\)".*/\1/p')" \
53
+ "$(printf '%s' "$json" | sed -n 's/.*"integrity":"\([^"]*\)".*/\1/p')"
49
54
  }
50
55
 
51
56
  # GitHub 最新 release 版本号(仅数字串;空=探测失败)。releases/latest/download 会 302 到
@@ -65,6 +70,64 @@ ver_ge() {
65
70
  [ -n "$a" ] && [ -n "$b" ] && [ "$a" -ge "$b" 2>/dev/null ]
66
71
  }
67
72
 
73
+
74
+ # ── 下载完整性(2026-09-13 审计后续)─────────────────────────
75
+ # npm tgz 走 registry dist.integrity(sha512,与 tarball 同一次元数据带回);
76
+ # GitHub 直连/代理资产走同 Release 的 <asset>.sha256 sidecar。校验不过/拿不到
77
+ # 期望值一律拒装;唯一逃逸口 LOOKATSTUDY_SKIP_VERIFY=1(用户显式自担风险)。
78
+
79
+ # 文件 sha256(小写 hex;失败空输出)
80
+ sha256_of() { sha256sum < "$1" 2>/dev/null | awk '{print $1}' || true; }
81
+
82
+ # npm tgz 强校验:期望值=registry 元数据 dist.integrity("sha512-<base64>")
83
+ verify_npm_tgz() { # <file> <integrity>
84
+ if [ "${LOOKATSTUDY_SKIP_VERIFY:-0}" = "1" ]; then
85
+ info "LOOKATSTUDY_SKIP_VERIFY=1:跳过 npm 包完整性校验(用户显式自担风险)"
86
+ return 0
87
+ fi
88
+ [ -n "$2" ] || { warn "registry 元数据缺 integrity,视为不可信"; return 1; }
89
+ local got
90
+ got=$(node -e "const c=require('crypto'),f=require('fs');process.stdout.write('sha512-'+c.createHash('sha512').update(f.readFileSync(process.argv[1])).digest('base64'))" "$1" 2>/dev/null || true)
91
+ if [ -n "$got" ] && [ "$got" = "$2" ]; then return 0; fi
92
+ warn "npm 包 sha512 完整性校验失败(下载损坏或镜像被篡改),换下一源"
93
+ return 1
94
+ }
95
+
96
+ # GitHub Release 资产的期望 sha256(同 Release 的 .sha256 sidecar;直连优先代理兜底)
97
+ # 契约:恒 exit 0,空输出=校验源不可达。
98
+ gh_asset_sha256() { # <asset-filename>
99
+ local p raw sum
100
+ for p in "${DL_PREFIXES[@]}"; do
101
+ raw=$(curl -sfL --connect-timeout 8 --max-time 20 "${p}${GH_RELEASE}/$1.sha256" 2>/dev/null || true)
102
+ sum=$(printf '%s' "$raw" | tr -d ' \r\n' | tr 'A-F' 'a-f')
103
+ case "$sum" in
104
+ ''|*[!0-9a-f]*) ;; # 空或非 hex:这一跳拿不到/坏,试下一跳
105
+ *) printf '%s' "$sum"; return 0 ;;
106
+ esac
107
+ done
108
+ return 0
109
+ }
110
+
111
+ # GH 资产强校验:期望值拿不到或对不上都拒装(防代理篡改/下载损坏)
112
+ verify_gh_download() { # <file> <asset-filename> <label>
113
+ if [ "${LOOKATSTUDY_SKIP_VERIFY:-0}" = "1" ]; then
114
+ info "LOOKATSTUDY_SKIP_VERIFY=1:跳过 $3 完整性校验(用户显式自担风险)"
115
+ return 0
116
+ fi
117
+ local exp got
118
+ exp=$(gh_asset_sha256 "$2")
119
+ if [ -z "$exp" ]; then
120
+ warn "$3 的 sha256 校验源不可达(.sha256 sidecar 拿不到),拒装防篡改;稍后重跑,或自担风险:LOOKATSTUDY_SKIP_VERIFY=1 bash 本脚本"
121
+ return 1
122
+ fi
123
+ got=$(sha256_of "$1")
124
+ if [ "$got" != "$exp" ]; then
125
+ warn "$3 sha256 不匹配(期望 ${exp:0:12}…实际 ${got:0:12}…),已拒装(下载损坏或被篡改)"
126
+ return 1
127
+ fi
128
+ ok "$3 sha256 校验通过"
129
+ }
130
+
68
131
  info() { printf '\033[1m[*]\033[0m %s\n' "$*"; }
69
132
  ok() { printf '\033[38;2;0;229;204m[✓]\033[0m %s\n' "$*"; }
70
133
  warn() { printf '\033[33m[!]\033[0m %s\n' "$*"; }
@@ -119,22 +182,31 @@ download_bundle() {
119
182
  cd "$APP_DIR"
120
183
  info "下载便携包(约 5MB)..."
121
184
  # 主源:npm 镜像(npmmirror 同步 npm 发布,国内直连快;tgz 需剥 package/ 前缀)
122
- local tb
123
- tb=$(npm_tarball lookatstudy-mobile)
124
- if [ -n "$tb" ] && curl -fL --connect-timeout 10 --retry 2 -o mobile.tgz "$tb"; then
185
+ # meta 两行 = tarball URL + dist.integrity(sha512);下载后强校验,不过=换下一源
186
+ local meta tb itg
187
+ meta=$(npm_tarball lookatstudy-mobile)
188
+ tb=$(printf '%s' "$meta" | sed -n 1p)
189
+ itg=$(printf '%s' "$meta" | sed -n 2p)
190
+ if [ -n "$tb" ] && curl -fL --connect-timeout 10 --retry 2 -o mobile.tgz "$tb" \
191
+ && verify_npm_tgz mobile.tgz "$itg"; then
125
192
  tar -xzf mobile.tgz --strip-components=1
126
193
  rm -f mobile.tgz
127
- ok "便携包就位(npm 镜像): $APP_DIR"
194
+ ok "便携包就位(npm 镜像,sha512 校验通过): $APP_DIR"
128
195
  return 0
129
196
  fi
197
+ rm -f mobile.tgz
130
198
  # 次选:npm 官方源(权威零滞后,接住镜像同步窗口)
131
- tb=$(npm_tarball_official lookatstudy-mobile)
132
- if [ -n "$tb" ] && curl -fL --connect-timeout 10 --retry 2 -o mobile.tgz "$tb"; then
199
+ meta=$(npm_tarball_official lookatstudy-mobile)
200
+ tb=$(printf '%s' "$meta" | sed -n 1p)
201
+ itg=$(printf '%s' "$meta" | sed -n 2p)
202
+ if [ -n "$tb" ] && curl -fL --connect-timeout 10 --retry 2 -o mobile.tgz "$tb" \
203
+ && verify_npm_tgz mobile.tgz "$itg"; then
133
204
  tar -xzf mobile.tgz --strip-components=1
134
205
  rm -f mobile.tgz
135
- ok "便携包就位(npm 官方源): $APP_DIR"
206
+ ok "便携包就位(npm 官方源,sha512 校验通过): $APP_DIR"
136
207
  return 0
137
208
  fi
209
+ rm -f mobile.tgz
138
210
  info "npm 源未命中,回退 GitHub 直连+代理链..."
139
211
  local dl_ok=0 p=""
140
212
  for p in "${DL_PREFIXES[@]}"; do
@@ -148,6 +220,7 @@ download_bundle() {
148
220
  echo "下载失败。请手动下载 $GH_ASSET 放到 $APP_DIR/ls.zip 后重跑本脚本。"
149
221
  exit 1
150
222
  fi
223
+ verify_gh_download ls.zip lookatstudy-mobile.zip "便携包" || { rm -f ls.zip; exit 1; }
151
224
  unzip -o ls.zip
152
225
  rm -f ls.zip
153
226
  ok "便携包就位: $APP_DIR"
@@ -157,24 +230,32 @@ install_voice() {
157
230
  mkdir -p "$APP_DIR/node_modules"
158
231
  info "下载 Termux 语音引擎包(约 12MB)..."
159
232
  # 主源:npm 镜像(tgz 剥 package/ 前缀,解出 sherpa-onnx-node/ 目录)
160
- local tb
161
- tb=$(npm_tarball lookatstudy-termux-voice)
162
- if [ -n "$tb" ] && curl -fL --connect-timeout 10 --retry 2 -o voice.tgz "$tb"; then
233
+ local meta tb itg
234
+ meta=$(npm_tarball lookatstudy-termux-voice)
235
+ tb=$(printf '%s' "$meta" | sed -n 1p)
236
+ itg=$(printf '%s' "$meta" | sed -n 2p)
237
+ if [ -n "$tb" ] && curl -fL --connect-timeout 10 --retry 2 -o voice.tgz "$tb" \
238
+ && verify_npm_tgz voice.tgz "$itg"; then
163
239
  tar -xzf voice.tgz --strip-components=1 -C "$APP_DIR/node_modules"
164
240
  rm -f voice.tgz
165
- ok "语音引擎就位(npm 镜像): $APP_DIR/node_modules/sherpa-onnx-node"
241
+ ok "语音引擎就位(npm 镜像,sha512 校验通过): $APP_DIR/node_modules/sherpa-onnx-node"
166
242
  info "语音模型在应用内按需下载(设置 → 语音能力):朗读本地档约 430MB;听写建议云档(Groq/Azure),本地 Whisper 约 360MB~1GB"
167
243
  info "装完模型重启服务生效: bash ~/lookatstudy/start.sh"
168
244
  return 0
169
245
  fi
246
+ rm -f voice.tgz
170
247
  # 次选:npm 官方源(权威零滞后,接住镜像同步窗口)
171
- tb=$(npm_tarball_official lookatstudy-termux-voice)
172
- if [ -n "$tb" ] && curl -fL --connect-timeout 10 --retry 2 -o voice.tgz "$tb"; then
248
+ meta=$(npm_tarball_official lookatstudy-termux-voice)
249
+ tb=$(printf '%s' "$meta" | sed -n 1p)
250
+ itg=$(printf '%s' "$meta" | sed -n 2p)
251
+ if [ -n "$tb" ] && curl -fL --connect-timeout 10 --retry 2 -o voice.tgz "$tb" \
252
+ && verify_npm_tgz voice.tgz "$itg"; then
173
253
  tar -xzf voice.tgz --strip-components=1 -C "$APP_DIR/node_modules"
174
254
  rm -f voice.tgz
175
- ok "语音引擎就位(npm 官方源): $APP_DIR/node_modules/sherpa-onnx-node"
255
+ ok "语音引擎就位(npm 官方源,sha512 校验通过): $APP_DIR/node_modules/sherpa-onnx-node"
176
256
  return 0
177
257
  fi
258
+ rm -f voice.tgz
178
259
  info "npm 源未命中,回退 GitHub 直连+代理链..."
179
260
  local dl_ok=0 p=""
180
261
  for p in "${DL_PREFIXES[@]}"; do
@@ -189,6 +270,7 @@ install_voice() {
189
270
  rm -f voice.tar.gz
190
271
  return 1
191
272
  fi
273
+ verify_gh_download voice.tar.gz lookatstudy-termux-voice.tar.gz "语音引擎包" || { rm -f voice.tar.gz; return 1; }
192
274
  tar -xzf voice.tar.gz -C "$APP_DIR/node_modules"
193
275
  rm -f voice.tar.gz
194
276
  ok "语音引擎就位: $APP_DIR/node_modules/sherpa-onnx-node"
@@ -287,10 +369,59 @@ EOS
287
369
  {
288
370
  printf '#!/data/data/com.termux/files/usr/bin/bash\n'
289
371
  printf 'set -euo pipefail\n'
290
- printf 'APP_DIR=%q PORT=%q GH_ASSET=%q\n\n' "$APP_DIR" "$PORT" "$GH_ASSET"
372
+ printf 'APP_DIR=%q PORT=%q GH_ASSET=%q GH_RELEASE=%q\n' "$APP_DIR" "$PORT" "$GH_ASSET" "$GH_RELEASE"
373
+ printf 'DL_PREFIXES=("" "https://gh-proxy.com/" "https://ghproxy.net/" "https://ghfast.top/")\n\n'
291
374
  cat <<'UPDATE_EOF'
292
375
  # 更新便携包并重启(下载链与 install-termux.sh 同源:npmmirror+滞后守卫 → npm 官方 → GitHub 直连 → gh 代理)
293
376
  info() { printf '\033[1m[*]\033[0m %s\n' "$*"; }
377
+ warn() { printf '\033[33m[!]\033[0m %s\n' "$*"; }
378
+ ok() { printf '\033[38;2;0,229,204m[✓]\033[0m %s\n' "$*"; }
379
+
380
+ # ↓↓ 完整性校验四件(与 install-termux.sh 逐字同源)↓↓
381
+ sha256_of() { sha256sum < "$1" 2>/dev/null | awk '{print $1}' || true; }
382
+ verify_npm_tgz() { # <file> <integrity>
383
+ if [ "${LOOKATSTUDY_SKIP_VERIFY:-0}" = "1" ]; then
384
+ info "LOOKATSTUDY_SKIP_VERIFY=1:跳过 npm 包完整性校验(用户显式自担风险)"
385
+ return 0
386
+ fi
387
+ [ -n "$2" ] || { warn "registry 元数据缺 integrity,视为不可信"; return 1; }
388
+ local got
389
+ got=$(node -e "const c=require('crypto'),f=require('fs');process.stdout.write('sha512-'+c.createHash('sha512').update(f.readFileSync(process.argv[1])).digest('base64'))" "$1" 2>/dev/null || true)
390
+ if [ -n "$got" ] && [ "$got" = "$2" ]; then return 0; fi
391
+ warn "npm 包 sha512 完整性校验失败(下载损坏或镜像被篡改),换下一源"
392
+ return 1
393
+ }
394
+ gh_asset_sha256() { # <asset-filename>
395
+ local p raw sum
396
+ for p in "${DL_PREFIXES[@]}"; do
397
+ raw=$(curl -sfL --connect-timeout 8 --max-time 20 "${p}${GH_RELEASE}/$1.sha256" 2>/dev/null || true)
398
+ sum=$(printf '%s' "$raw" | tr -d ' \r\n' | tr 'A-F' 'a-f')
399
+ case "$sum" in
400
+ ''|*[!0-9a-f]*) ;;
401
+ *) printf '%s' "$sum"; return 0 ;;
402
+ esac
403
+ done
404
+ return 0
405
+ }
406
+ verify_gh_download() { # <file> <asset-filename> <label>
407
+ if [ "${LOOKATSTUDY_SKIP_VERIFY:-0}" = "1" ]; then
408
+ info "LOOKATSTUDY_SKIP_VERIFY=1:跳过 $3 完整性校验(用户显式自担风险)"
409
+ return 0
410
+ fi
411
+ local exp got
412
+ exp=$(gh_asset_sha256 "$2")
413
+ if [ -z "$exp" ]; then
414
+ warn "$3 的 sha256 校验源不可达(.sha256 sidecar 拿不到),拒装防篡改;稍后重跑,或自担风险:LOOKATSTUDY_SKIP_VERIFY=1 bash 本脚本"
415
+ return 1
416
+ fi
417
+ got=$(sha256_of "$1")
418
+ if [ "$got" != "$exp" ]; then
419
+ warn "$3 sha256 不匹配(期望 ${exp:0:12}…实际 ${got:0:12}…),已拒装(下载损坏或被篡改)"
420
+ return 1
421
+ fi
422
+ ok "$3 sha256 校验通过"
423
+ }
424
+ # ↑↑ 完整性校验四件结束 ↑↑
294
425
 
295
426
  # ↓↓ 与 install-termux.sh 逐字同源的四个函数(契约:恒 exit 0,空输出=未命中)↓↓
296
427
  npm_tarball() {
@@ -305,12 +436,16 @@ npm_tarball() {
305
436
  return 0
306
437
  fi
307
438
  fi
308
- printf '%s' "$json" | sed -n 's/.*"tarball":"\([^"]*\)".*/\1/p'
439
+ printf '%s\n%s\n' \
440
+ "$(printf '%s' "$json" | sed -n 's/.*"tarball":"\([^"]*\)".*/\1/p')" \
441
+ "$(printf '%s' "$json" | sed -n 's/.*"integrity":"\([^"]*\)".*/\1/p')"
309
442
  }
310
443
  npm_tarball_official() {
311
444
  local json
312
445
  json=$(curl -sfL --connect-timeout 8 --max-time 20 "https://registry.npmjs.org/$1/latest" || true)
313
- printf '%s' "$json" | sed -n 's/.*"tarball":"\([^"]*\)".*/\1/p'
446
+ printf '%s\n%s\n' \
447
+ "$(printf '%s' "$json" | sed -n 's/.*"tarball":"\([^"]*\)".*/\1/p')" \
448
+ "$(printf '%s' "$json" | sed -n 's/.*"integrity":"\([^"]*\)".*/\1/p')"
314
449
  }
315
450
  gh_latest_version() {
316
451
  local final
@@ -332,14 +467,22 @@ pkill -f "server.cjs --port $PORT" 2>/dev/null || true
332
467
  sleep 1
333
468
  echo "==> 下载最新便携包..."
334
469
  src=""
335
- tb=$(npm_tarball lookatstudy-mobile)
336
- if [ -n "$tb" ] && curl -fL --connect-timeout 10 --retry 2 -o mobile.tgz "$tb"; then
470
+ meta=$(npm_tarball lookatstudy-mobile)
471
+ tb=$(printf '%s' "$meta" | sed -n 1p)
472
+ itg=$(printf '%s' "$meta" | sed -n 2p)
473
+ if [ -n "$tb" ] && curl -fL --connect-timeout 10 --retry 2 -o mobile.tgz "$tb" \
474
+ && verify_npm_tgz mobile.tgz "$itg"; then
337
475
  src="npm 镜像"
338
476
  else
339
- tb=$(npm_tarball_official lookatstudy-mobile)
340
- if [ -n "$tb" ] && curl -fL --connect-timeout 10 --retry 2 -o mobile.tgz "$tb"; then
477
+ rm -f mobile.tgz
478
+ meta=$(npm_tarball_official lookatstudy-mobile)
479
+ tb=$(printf '%s' "$meta" | sed -n 1p)
480
+ itg=$(printf '%s' "$meta" | sed -n 2p)
481
+ if [ -n "$tb" ] && curl -fL --connect-timeout 10 --retry 2 -o mobile.tgz "$tb" \
482
+ && verify_npm_tgz mobile.tgz "$itg"; then
341
483
  src="npm 官方源"
342
484
  fi
485
+ rm -f mobile.tgz
343
486
  fi
344
487
  if [ -n "$src" ]; then
345
488
  tar -xzf mobile.tgz --strip-components=1 && rm -f mobile.tgz
@@ -351,6 +494,7 @@ else
351
494
  echo " ${p:-GitHub 直连}失败,换下一个源..."
352
495
  done
353
496
  [ "$dl_ok" = 1 ] || { echo "下载失败,保持原版本。"; exit 1; }
497
+ verify_gh_download ls.zip lookatstudy-mobile.zip "便携包" || { rm -f ls.zip; echo "校验失败,保持原版本。"; exit 1; }
354
498
  unzip -o ls.zip && rm -f ls.zip
355
499
  fi
356
500
  echo "==> 重启..."
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lookatstudy-mobile",
3
- "version": "0.34.0",
3
+ "version": "0.35.0",
4
4
  "description": "LookatStudy Termux portable bundle (server.cjs + web frontend)",
5
5
  "repository": {
6
6
  "type": "git",