lookatstudy-mobile 0.32.0 → 0.33.1
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.
- package/package.json +1 -1
- package/server.cjs +110 -36
- package/web/assets/{CanvasStage-C5AjAi8o.js → CanvasStage-Bt_fNO8t.js} +1 -1
- package/web/assets/{CommandPalette-CMByQ1xA.js → CommandPalette-DWmgv52l.js} +1 -1
- package/web/assets/{CourseSearchPanel-BlV2XeFD.js → CourseSearchPanel-vB2mN_3Y.js} +1 -1
- package/web/assets/{ExamView-BuzkmoG_.js → ExamView-DjcpWfqz.js} +1 -1
- package/web/assets/{ReviewPanel-Zve2SZUA.js → ReviewPanel-BIdGi9F2.js} +1 -1
- package/web/assets/{SettingsView-ClrQQG3O.js → SettingsView-BeMHHZCd.js} +2 -2
- package/web/assets/{VoicePanel-CrCt12Iw.js → VoicePanel-CKzA8jtV.js} +1 -1
- package/web/assets/{abnfDiagram-VCTEODGH-ODA7YWAP-0U3o9k-P.js → abnfDiagram-VCTEODGH-ODA7YWAP-D0MMepkX.js} +1 -1
- package/web/assets/{abnfDiagram-VCTEODGH-CvhdvCcB.js → abnfDiagram-VCTEODGH-dGcioyTT.js} +1 -1
- package/web/assets/{architectureDiagram-5GKGNRK7-C4PU3XN7-BWNxjRJN.js → architectureDiagram-5GKGNRK7-C4PU3XN7-C6PC7E-G.js} +1 -1
- package/web/assets/{architectureDiagram-5GKGNRK7-B7KWD0vf.js → architectureDiagram-5GKGNRK7-CXRelZVJ.js} +1 -1
- package/web/assets/{arrow-right-BUOXWVDJ.js → arrow-right-Do7NbfY9.js} +1 -1
- package/web/assets/{blockDiagram-I7D4REHJ-C2JsQ5gD.js → blockDiagram-I7D4REHJ-Dd_p7o-g.js} +1 -1
- package/web/assets/{blockDiagram-NRAW4CY4-UMJ6PMQQ-BFITa5i3.js → blockDiagram-NRAW4CY4-UMJ6PMQQ-1W9B_JQA.js} +1 -1
- package/web/assets/{c4Diagram-7LVT6UL2-BKdIKfT2.js → c4Diagram-7LVT6UL2-D6GGkJgl.js} +1 -1
- package/web/assets/{c4Diagram-UCG6FXSJ-75X3U2P3-frDapyTO.js → c4Diagram-UCG6FXSJ-75X3U2P3-D6kIkgIO.js} +1 -1
- package/web/assets/channel-Bmdemkmz.js +1 -0
- package/web/assets/{chunk-2Q5K7J3B-B9edEgx0.js → chunk-2Q5K7J3B-1wsMqe35.js} +1 -1
- package/web/assets/{chunk-5VM5RSS4-gTXbbOyw.js → chunk-5VM5RSS4-DfuR9l9V.js} +1 -1
- package/web/assets/{chunk-7XDTSPCN-WE5tnF0h.js → chunk-7XDTSPCN-BZY0pKX-.js} +1 -1
- package/web/assets/{chunk-CTNIYPP6-BkodKTJ4.js → chunk-CTNIYPP6-CNvvOY5j.js} +1 -1
- package/web/assets/{chunk-CXNFBKHE-HwiQMaWc.js → chunk-CXNFBKHE-Dm1pquWx.js} +1 -1
- package/web/assets/{chunk-F27PBJKO-blDtmndB.js → chunk-F27PBJKO-CHRFnx11.js} +1 -1
- package/web/assets/{chunk-IMKFNOWR-c2ANK639.js → chunk-IMKFNOWR-uiypXoEe.js} +1 -1
- package/web/assets/{chunk-ISA7TJRJ-ORNUxxCz.js → chunk-ISA7TJRJ-BQ2gIr75.js} +1 -1
- package/web/assets/{chunk-JWPE2WC7-B6N2oaft.js → chunk-JWPE2WC7-BgST7e0p.js} +1 -1
- package/web/assets/{chunk-LV3HUYFY-BCm0PQly.js → chunk-LV3HUYFY-BnzTfd10.js} +1 -1
- package/web/assets/{chunk-M3IUQBOH-DN38nuN1.js → chunk-M3IUQBOH-CviSmNDD.js} +1 -1
- package/web/assets/{chunk-NDIOYS4C-Bo6GgJFG.js → chunk-NDIOYS4C-C4wQSziF.js} +1 -1
- package/web/assets/{chunk-POPQ4Y6H-DIzvpHxe.js → chunk-POPQ4Y6H-LX5w0GJ3.js} +1 -1
- package/web/assets/{chunk-RE4WF27V-D8jfGB_4.js → chunk-RE4WF27V-BAhk1SH_.js} +1 -1
- package/web/assets/{chunk-SVP7TREG-CxnNazbc.js → chunk-SVP7TREG-7Sg7sU_j.js} +1 -1
- package/web/assets/{chunk-TICWLB2K-DURl3-X9.js → chunk-TICWLB2K-CP9bBlVg.js} +1 -1
- package/web/assets/{chunk-VII2H2IX-B_sArj8v.js → chunk-VII2H2IX-DZd3RSio.js} +1 -1
- package/web/assets/{chunk-X5GNSKZ3-DYVuF3lP.js → chunk-X5GNSKZ3-BhOZRnO2.js} +1 -1
- package/web/assets/{chunk-XCWWM2C3-D8AZk6R6.js → chunk-XCWWM2C3-RUaVDOLz.js} +1 -1
- package/web/assets/{chunk-XXDRQBXY-CkpBnoOj.js → chunk-XXDRQBXY-BMOoMY4C.js} +1 -1
- package/web/assets/classDiagram-DTDB5LWJ-LWRDKZ5K-BA0DiW1S.js +1 -0
- package/web/assets/classDiagram-ZZMXUADV-Y4CKpvNU.js +1 -0
- package/web/assets/classDiagram-v2-JRS7N3AN-2BZHGRP2-BA0DiW1S.js +1 -0
- package/web/assets/classDiagram-v2-VYDZK3BY-Y4CKpvNU.js +1 -0
- package/web/assets/{core-B8sIFi4Z.js → core-Bljob3yk.js} +1 -1
- package/web/assets/{cose-bilkent-JH36ORCC-y34bWcV3.js → cose-bilkent-JH36ORCC-DLbA0yhO.js} +1 -1
- package/web/assets/{cose-bilkent-JH36ORCC-OB24KH3T-IvJ21Kzu.js → cose-bilkent-JH36ORCC-OB24KH3T-ebHFtabj.js} +1 -1
- package/web/assets/{cynefin-OW5HDTMX-BhCbnffL.js → cynefin-OW5HDTMX-CnnFvhFO.js} +1 -1
- package/web/assets/{cynefin-OW5HDTMX-GVF3BKWE-DZCD-8FK.js → cynefin-OW5HDTMX-GVF3BKWE-DyJbbdgM.js} +1 -1
- package/web/assets/{cynefinDiagram-5FMLGOSQ-hxpWMOtC.js → cynefinDiagram-5FMLGOSQ-DCwI1QTM.js} +1 -1
- package/web/assets/{cynefinDiagram-5FMLGOSQ-H3WIRU3N-DYZPSd2_.js → cynefinDiagram-5FMLGOSQ-H3WIRU3N-jNzWfP6P.js} +1 -1
- package/web/assets/{dagre-3AP2YEHR-DCERTXHT-BX8eflJW.js → dagre-3AP2YEHR-DCERTXHT-DeiOeAFM.js} +1 -1
- package/web/assets/{dagre-GXQ25YYZ-CzyQyaZs.js → dagre-GXQ25YYZ-CDCQs5B2.js} +1 -1
- package/web/assets/{diagram-S7CK7UJ4-COWdoy1M.js → diagram-S7CK7UJ4-DgZUpvXD.js} +1 -1
- package/web/assets/{diagram-S7CK7UJ4-MULGZGEL-CqJqO9UI.js → diagram-S7CK7UJ4-MULGZGEL-BPzWf7Pz.js} +1 -1
- package/web/assets/{diagram-UQ7AKVKN-BTrH3UHo.js → diagram-UQ7AKVKN-CZTprTGR.js} +1 -1
- package/web/assets/{diagram-UQ7AKVKN-YQBY353J-QrzubupF.js → diagram-UQ7AKVKN-YQBY353J-D8p7jpg9.js} +1 -1
- package/web/assets/{diagram-VSXAHHWV-DPZOIzCd.js → diagram-VSXAHHWV-D3J7k7wm.js} +1 -1
- package/web/assets/{diagram-VSXAHHWV-WLNH2ELY-BCati379.js → diagram-VSXAHHWV-WLNH2ELY-DY4p4Mm9.js} +1 -1
- package/web/assets/{diagram-VX7I27RA-_xIvruEB.js → diagram-VX7I27RA-CGlO-Ekr.js} +1 -1
- package/web/assets/{diagram-VX7I27RA-XW6YQV5G-DqXG6n6Y.js → diagram-VX7I27RA-XW6YQV5G-Ctmdqbly.js} +1 -1
- package/web/assets/{diagram-Z3DM3KII-HG7RF65D-Croiubkf.js → diagram-Z3DM3KII-HG7RF65D-qfRqaf6R.js} +1 -1
- package/web/assets/{diagram-Z3DM3KII-ksHvfnAi.js → diagram-Z3DM3KII-m9lJCZC1.js} +1 -1
- package/web/assets/{ebnfDiagram-PWID7BFC-OKY4VHYD-CGcKoMMQ.js → ebnfDiagram-PWID7BFC-OKY4VHYD-D89NZRDy.js} +1 -1
- package/web/assets/{ebnfDiagram-PWID7BFC-noMSXuIk.js → ebnfDiagram-PWID7BFC-c9DvObbP.js} +1 -1
- package/web/assets/{erDiagram-RLTQ6QDP-cL-kIfCG.js → erDiagram-RLTQ6QDP-CYWGD0br.js} +1 -1
- package/web/assets/{erDiagram-SSCWMZ5O-PIPKOIK7-D9Zc1NAW.js → erDiagram-SSCWMZ5O-PIPKOIK7-BrchUk-n.js} +1 -1
- package/web/assets/{flowDiagram-A5DVABFB-2Z4AVO5R-mUdmnAxr.js → flowDiagram-A5DVABFB-2Z4AVO5R-B0Rwk_R1.js} +1 -1
- package/web/assets/{flowDiagram-HODETNUW-BwP5kUwO.js → flowDiagram-HODETNUW-BJ3ZVwcY.js} +1 -1
- package/web/assets/{ganttDiagram-EL5Y4UJY-BE3keQkK.js → ganttDiagram-EL5Y4UJY-B_MNJ8fZ.js} +1 -1
- package/web/assets/{ganttDiagram-EL5Y4UJY-JZNG7GOG-ejS74W4D.js → ganttDiagram-EL5Y4UJY-JZNG7GOG-DkxJ0Cn0.js} +1 -1
- package/web/assets/{gitGraphDiagram-WWUBYQGX-DZ1hemyK.js → gitGraphDiagram-WWUBYQGX-CSy5TQmE.js} +1 -1
- package/web/assets/{gitGraphDiagram-WWUBYQGX-DMWMLL3G-BkacjsFO.js → gitGraphDiagram-WWUBYQGX-DMWMLL3G-D_BHv1fj.js} +1 -1
- package/web/assets/{index-CBjzutcq.js → index-BPS_Agzl.js} +1 -1
- package/web/assets/{index-pVpu1lqj.js → index-DiOyjst0.js} +1 -1
- package/web/assets/{index-knlcEee4.js → index-HjyNOGR-.js} +3 -3
- package/web/assets/{infoDiagram-27XIBGKW-D5SmDceD.js → infoDiagram-27XIBGKW-Dg4zHSFK.js} +1 -1
- package/web/assets/{infoDiagram-RXCK75RN-HANS5KVC-aXapDflI.js → infoDiagram-RXCK75RN-HANS5KVC-BnDIUO0e.js} +1 -1
- package/web/assets/{ishikawaDiagram-5VMMS53U-7GCIT7WU-Bc9UsCs_.js → ishikawaDiagram-5VMMS53U-7GCIT7WU-CvpASJo8.js} +1 -1
- package/web/assets/{ishikawaDiagram-5VMMS53U-B74LRTCh.js → ishikawaDiagram-5VMMS53U-zVzbXgbw.js} +1 -1
- package/web/assets/{journeyDiagram-3NMN7TZE-BDaoWTSE.js → journeyDiagram-3NMN7TZE-Dnac9OWj.js} +1 -1
- package/web/assets/{journeyDiagram-EYS64GPL-OAPELBT7-MrPU_Ez8.js → journeyDiagram-EYS64GPL-OAPELBT7-JFMRWh-l.js} +1 -1
- package/web/assets/{kanban-definition-3QL26DDD-I7RKWQQY-Cm3vdIeM.js → kanban-definition-3QL26DDD-I7RKWQQY-DI4eShiu.js} +1 -1
- package/web/assets/{kanban-definition-UXKFOSKX-SS8pMp0v.js → kanban-definition-UXKFOSKX-BGCJV8zY.js} +1 -1
- package/web/assets/{katex-N6JRN4PM-hIPFODWi.js → katex-N6JRN4PM-CUVnEhz9.js} +1 -1
- package/web/assets/{layout-D8EinnOm.js → layout-BUaUYSLh.js} +1 -1
- package/web/assets/{main-48BT8Y-5.js → main-fDEgiN-S.js} +6 -6
- package/web/assets/{mermaid-layout-elk.core-BpWpx6UM.js → mermaid-layout-elk.core-BWBQ_Al5.js} +2 -2
- package/web/assets/{mermaid.core-B5r2lVOh.js → mermaid.core-BMxGmpq2.js} +5 -5
- package/web/assets/{mindmap-definition-FBJOCRG2-KIYZG64Y-hpeP-5hz.js → mindmap-definition-FBJOCRG2-KIYZG64Y-CjgpFZ4r.js} +1 -1
- package/web/assets/{mindmap-definition-YA3MSWOX-De5xL7n7.js → mindmap-definition-YA3MSWOX-DsaPJhNK.js} +1 -1
- package/web/assets/{pegDiagram-XKGWAZYB-CdIyH0Fn.js → pegDiagram-XKGWAZYB-DOx0zpGS.js} +1 -1
- package/web/assets/{pegDiagram-XKGWAZYB-OIP57N6J-CR8lJ0Uf.js → pegDiagram-XKGWAZYB-OIP57N6J-DjMX89Yt.js} +1 -1
- package/web/assets/{pieDiagram-E7YTZNPT-2UOTW327-DnJ5X3O6.js → pieDiagram-E7YTZNPT-2UOTW327-lDdClvNY.js} +1 -1
- package/web/assets/{pieDiagram-E7YTZNPT-BfYDyK9E.js → pieDiagram-E7YTZNPT-D6Fit7on.js} +1 -1
- package/web/assets/{quadrantDiagram-AXDQQJYC-Cza5JG_-.js → quadrantDiagram-AXDQQJYC-1jui_ce_.js} +1 -1
- package/web/assets/{quadrantDiagram-AXDQQJYC-SN3ASJJK-Bcepwx-u.js → quadrantDiagram-AXDQQJYC-SN3ASJJK-BTbJgylU.js} +1 -1
- package/web/assets/{railroadDiagram-O6MQD6OU-34742W6X-NzeT7rIi.js → railroadDiagram-O6MQD6OU-34742W6X-BC0RubPt.js} +1 -1
- package/web/assets/{railroadDiagram-O6MQD6OU-C95urtld.js → railroadDiagram-O6MQD6OU-BAC-UzUT.js} +1 -1
- package/web/assets/{render-O7CIS3YK-C6VkwFv4.js → render-O7CIS3YK-ilIoaxFJ.js} +7 -7
- package/web/assets/{requirementDiagram-BXWQKSXE-BAlq4SmX.js → requirementDiagram-BXWQKSXE-9E1HLwpG.js} +1 -1
- package/web/assets/{requirementDiagram-EFPCY7ZU-NGAFFSMB-DzevWop7.js → requirementDiagram-EFPCY7ZU-NGAFFSMB-KHWBp4rz.js} +1 -1
- package/web/assets/{rotate-ccw-Cg9NLquP.js → rotate-ccw-QIElN2Wl.js} +1 -1
- package/web/assets/{sankeyDiagram-P5KCCOFB-3D3M4XPB-DApmapmK.js → sankeyDiagram-P5KCCOFB-3D3M4XPB-C-izOJ7-.js} +1 -1
- package/web/assets/{sankeyDiagram-P5KCCOFB-BD8XvM6V.js → sankeyDiagram-P5KCCOFB-Cdc1ghNL.js} +1 -1
- package/web/assets/{sequenceDiagram-WJ2MYXX4-3QbrYVOI.js → sequenceDiagram-WJ2MYXX4-DRxvXwCF.js} +1 -1
- package/web/assets/{sequenceDiagram-WJ2MYXX4-Q2IOPB4R-0NYlnM84.js → sequenceDiagram-WJ2MYXX4-Q2IOPB4R-D1pUF6jo.js} +1 -1
- package/web/assets/{sizeCapture-INFHLROL-B6CyQprA.js → sizeCapture-INFHLROL-DpTeYER6.js} +1 -1
- package/web/assets/{sizeCapture-X5ZJPWSS-PSGVOMYM-BNAT5QMm.js → sizeCapture-X5ZJPWSS-PSGVOMYM-9nTpQ1b_.js} +1 -1
- package/web/assets/{stateDiagram-D77RDMKH-00SC5Yaa.js → stateDiagram-D77RDMKH-C96itYlp.js} +1 -1
- package/web/assets/{stateDiagram-HBIQ2CUA-4M2S6MAN-irstRbtp.js → stateDiagram-HBIQ2CUA-4M2S6MAN-DVcS5z07.js} +1 -1
- package/web/assets/stateDiagram-v2-4QOOHH4V-LBBLA2WV-C8CVqjZH.js +1 -0
- package/web/assets/stateDiagram-v2-MP3YSRHH-Ddg-z14V.js +1 -0
- package/web/assets/{swimlanes-42K2YHIH-CmbMI_md.js → swimlanes-42K2YHIH-4suR9Oix.js} +1 -1
- package/web/assets/{swimlanes-XN3QIQJK-N6N2CCQ6-BqITgbLG.js → swimlanes-XN3QIQJK-N6N2CCQ6-DcuiDmSl.js} +1 -1
- package/web/assets/swimlanesDiagram-VK2B7HYN-E6N4H5SY-BDSwlxp9.js +8 -0
- package/web/assets/swimlanesDiagram-VR7AAH4N-Cj4AmHeX.js +8 -0
- package/web/assets/{timeline-definition-24CTP7MA-CLCGIomU.js → timeline-definition-24CTP7MA-CcO-uk61.js} +1 -1
- package/web/assets/{timeline-definition-24CTP7MA-KN7RSRVM-BYuvwtyK.js → timeline-definition-24CTP7MA-KN7RSRVM-rA7akmuP.js} +1 -1
- package/web/assets/{vennDiagram-4TSXK5OY-AKJ7LXAN-AoK44fps.js → vennDiagram-4TSXK5OY-AKJ7LXAN-Hpw8RJob.js} +1 -1
- package/web/assets/{vennDiagram-4TSXK5OY-DCfqg183.js → vennDiagram-4TSXK5OY-L93ffUGG.js} +1 -1
- package/web/assets/{wardleyDiagram-VM6X3IG4-3B6A3BN5-B1lE0M48.js → wardleyDiagram-VM6X3IG4-3B6A3BN5-Dij0NzOp.js} +1 -1
- package/web/assets/{wardleyDiagram-VM6X3IG4-D1GxucqJ.js → wardleyDiagram-VM6X3IG4-D4jAxgQi.js} +1 -1
- package/web/assets/{xychartDiagram-S5SC5T6Z-DlDA8yrW.js → xychartDiagram-S5SC5T6Z-BeOHWeZb.js} +1 -1
- package/web/assets/{xychartDiagram-S5SC5T6Z-OBDNV6WG-D9XtPaPY.js → xychartDiagram-S5SC5T6Z-OBDNV6WG-NM-894O4.js} +1 -1
- package/web/index.html +1 -1
- package/web/assets/channel-B9w8tMGs.js +0 -1
- package/web/assets/classDiagram-DTDB5LWJ-LWRDKZ5K-Bc5E8Hix.js +0 -1
- package/web/assets/classDiagram-ZZMXUADV-BZZyCoYc.js +0 -1
- package/web/assets/classDiagram-v2-JRS7N3AN-2BZHGRP2-Bc5E8Hix.js +0 -1
- package/web/assets/classDiagram-v2-VYDZK3BY-BZZyCoYc.js +0 -1
- package/web/assets/stateDiagram-v2-4QOOHH4V-LBBLA2WV-eCbd9dBW.js +0 -1
- package/web/assets/stateDiagram-v2-MP3YSRHH-3vtW0ec_.js +0 -1
- package/web/assets/swimlanesDiagram-VK2B7HYN-E6N4H5SY-nuGcWYId.js +0 -8
- package/web/assets/swimlanesDiagram-VR7AAH4N-CjHyszkJ.js +0 -8
package/package.json
CHANGED
package/server.cjs
CHANGED
|
@@ -12719,6 +12719,8 @@ var init_schema = __esm({
|
|
|
12719
12719
|
labType: text("lab_type", { enum: ["doc", "code", "notebook"] }).notNull().default("doc"),
|
|
12720
12720
|
/** 仓库原文语言 (en / zh-CN / zh-TW / ...), LLM Step 2 判断; null=未知按 en 处理 */
|
|
12721
12721
|
sourceLang: text("source_lang"),
|
|
12722
|
+
/** v0.33 语言学习课程的目标语言;null = 非语言课程 */
|
|
12723
|
+
languageTarget: text("language_target"),
|
|
12722
12724
|
createdAt: text("created_at").notNull().default(sql`(CURRENT_TIMESTAMP)`)
|
|
12723
12725
|
});
|
|
12724
12726
|
contentNodes = sqliteTable("content_nodes", {
|
|
@@ -13021,6 +13023,9 @@ CREATE TABLE IF NOT EXISTS courses (
|
|
|
13021
13023
|
lab_type TEXT NOT NULL DEFAULT 'doc' CHECK (lab_type IN ('doc', 'code', 'notebook')),
|
|
13022
13024
|
-- \u4ED3\u5E93\u539F\u6587\u8BED\u8A00 (BCP-47 \u5B50\u96C6: en / zh-CN / zh-TW / ja / ...), LLM \u5728 Step 2 \u5224\u65AD
|
|
13023
13025
|
source_lang TEXT,
|
|
13026
|
+
-- v0.33 \u8BED\u8A00\u5B66\u4E60\u8BFE\u7A0B\u7684\u76EE\u6807\u8BED\u8A00(\u88AB\u6559\u7684\u8BED\u8A00,BCP-47);NULL = \u975E\u8BED\u8A00\u8BFE\u7A0B\u3002
|
|
13027
|
+
-- \u4E0E source_lang \u8BED\u4E49\u4E0D\u540C:\u4E2D\u6587\u5199\u7684\u82F1\u8BED\u6559\u6750 source_lang=zh-CN\u3001language_target=en
|
|
13028
|
+
language_target TEXT,
|
|
13024
13029
|
created_at TEXT NOT NULL DEFAULT (CURRENT_TIMESTAMP)
|
|
13025
13030
|
);
|
|
13026
13031
|
|
|
@@ -13422,6 +13427,7 @@ function runMigrations(db) {
|
|
|
13422
13427
|
addColumnIfMissing("content_nodes", "knowledge_points", "TEXT");
|
|
13423
13428
|
addColumnIfMissing("exercises", "kc_title", "TEXT");
|
|
13424
13429
|
addColumnIfMissing("courses", "source_lang", "TEXT");
|
|
13430
|
+
addColumnIfMissing("courses", "language_target", "TEXT");
|
|
13425
13431
|
addColumnIfMissing("custom_providers", "kind", "TEXT NOT NULL DEFAULT 'llm'");
|
|
13426
13432
|
addColumnIfMissing("custom_providers", "vision", "INTEGER NOT NULL DEFAULT 0");
|
|
13427
13433
|
addColumnIfMissing("memory", "course_id", "TEXT");
|
|
@@ -13918,7 +13924,8 @@ async function executeImport(db, structure, opts, onProgress) {
|
|
|
13918
13924
|
description: `\u4ECE ${opts.repoName} \u5BFC\u5165`,
|
|
13919
13925
|
version: 1,
|
|
13920
13926
|
labType: "doc",
|
|
13921
|
-
sourceLang: opts.sourceLang
|
|
13927
|
+
sourceLang: opts.sourceLang,
|
|
13928
|
+
languageTarget: opts.languageTarget ?? null
|
|
13922
13929
|
}).run();
|
|
13923
13930
|
for (const sec of structure.sections) {
|
|
13924
13931
|
const sectionId = (0, import_node_crypto3.randomUUID)();
|
|
@@ -63562,7 +63569,8 @@ async function classifyFileRoles(db, readmeMd, fileList, fullTree, onProgress, o
|
|
|
63562
63569
|
skip,
|
|
63563
63570
|
languages: validLangs,
|
|
63564
63571
|
sourceLang: detectSourceLangByRule(readmeMd),
|
|
63565
|
-
translationLayout: layoutResult.layout
|
|
63572
|
+
translationLayout: layoutResult.layout,
|
|
63573
|
+
languageTarget: null
|
|
63566
63574
|
};
|
|
63567
63575
|
}
|
|
63568
63576
|
send(`AI \u5224\u65AD ${remaining.length} \u4E2A\u6587\u4EF6\u89D2\u8272 + \u539F\u6587\u8BED\u8A00\uFF08\u770B README + \u76EE\u5F55\u6811\uFF09`);
|
|
@@ -63574,6 +63582,7 @@ async function classifyFileRoles(db, readmeMd, fileList, fullTree, onProgress, o
|
|
|
63574
63582
|
const allPathSet = new Set(allPaths);
|
|
63575
63583
|
const llmLangs = /* @__PURE__ */ new Set();
|
|
63576
63584
|
let sourceLang = "";
|
|
63585
|
+
let languageTarget = null;
|
|
63577
63586
|
for (let i = 0; i < remaining.length; i += CHUNK_SIZE) {
|
|
63578
63587
|
const chunk = remaining.slice(i, i + CHUNK_SIZE);
|
|
63579
63588
|
const chunkNum = Math.floor(i / CHUNK_SIZE) + 1;
|
|
@@ -63594,6 +63603,8 @@ async function classifyFileRoles(db, readmeMd, fileList, fullTree, onProgress, o
|
|
|
63594
63603
|
});
|
|
63595
63604
|
if (!sourceLang && parsed.sourceLang)
|
|
63596
63605
|
sourceLang = parsed.sourceLang;
|
|
63606
|
+
if (languageTarget === null && parsed.languageTarget)
|
|
63607
|
+
languageTarget = parsed.languageTarget;
|
|
63597
63608
|
for (const { path: path6, role, lang, translates } of parsed.files) {
|
|
63598
63609
|
if (role === "translation") {
|
|
63599
63610
|
if (lang && translates && allPathSet.has(translates)) {
|
|
@@ -63620,7 +63631,7 @@ async function classifyFileRoles(db, readmeMd, fileList, fullTree, onProgress, o
|
|
|
63620
63631
|
validLangs.push({ code, name: LANG_NAMES2[code] ?? code });
|
|
63621
63632
|
}
|
|
63622
63633
|
}
|
|
63623
|
-
return { original, translations, translationPairs, practice, skip, languages: validLangs, sourceLang, translationLayout: layoutResult.layout };
|
|
63634
|
+
return { original, translations, translationPairs, practice, skip, languages: validLangs, sourceLang, translationLayout: layoutResult.layout, languageTarget };
|
|
63624
63635
|
}
|
|
63625
63636
|
function detectSourceLangByRule(readmeMd) {
|
|
63626
63637
|
const total = Math.max(readmeMd.length, 1);
|
|
@@ -63698,7 +63709,7 @@ ${readmeExcerpt}
|
|
|
63698
63709
|
---` : "\uFF08\u65E0 README \u6587\u4EF6\uFF0C\u8BF7\u6839\u636E\u6587\u4EF6\u540D\u3001\u76EE\u5F55\u7ED3\u6784\u3001\u6587\u4EF6\u7C7B\u578B\u5224\u65AD\u539F\u6587\u8BED\u8A00\u548C\u6587\u4EF6\u89D2\u8272\uFF09";
|
|
63699
63710
|
return `\u4F60\u662F\u8BFE\u7A0B\u4ED3\u5E93\u5206\u6790\u4E13\u5BB6\u3002\u4E0B\u9762\u662F\u4E00\u4E2A\u5B66\u4E60\u4ED3\u5E93\u7684 README\uFF08\u524D3000\u5B57\uFF09\u3001\u5B8C\u6574\u76EE\u5F55\u6811\u548C\u5F85\u5206\u7C7B\u7684\u6587\u4EF6\u5217\u8868\u3002
|
|
63700
63711
|
|
|
63701
|
-
\u8BF7\u5B8C\u6210\
|
|
63712
|
+
\u8BF7\u5B8C\u6210\u4E09\u4E2A\u4EFB\u52A1:
|
|
63702
63713
|
|
|
63703
63714
|
1. \u5224\u65AD README \u7684**\u539F\u6587\u8BED\u8A00**\uFF08\u4E0D\u662F\u7FFB\u8BD1\u8BED\u8A00\uFF09\u3002\u770B README \u6B63\u6587\u662F\u4EC0\u4E48\u8BED\u8A00\u5199\u7684:
|
|
63704
63715
|
- \u82F1\u6587 \u2192 "en"
|
|
@@ -63715,6 +63726,15 @@ ${readmeExcerpt}
|
|
|
63715
63726
|
\u548C "translates"(\u5B83\u7FFB\u8BD1\u7684\u539F\u6587\u6587\u4EF6\u8DEF\u5F84\uFF0C\u5FC5\u987B\u662F\u6587\u4EF6\u5217\u8868\u6216\u76EE\u5F55\u6811\u91CC\u771F\u5B9E\u5B58\u5728\u7684\u6587\u4EF6)
|
|
63716
63727
|
- **skip**: \u566A\u58F0\uFF08\u7EAF\u914D\u7F6E\u3001\u7A7A\u6587\u4EF6\u3001\u975E\u5B66\u4E60\u5185\u5BB9\uFF09
|
|
63717
63728
|
|
|
63729
|
+
3. \u5224\u65AD\u8FD9\u4E2A\u4ED3\u5E93**\u662F\u4E0D\u662F\u8BED\u8A00\u5B66\u4E60\u8BFE\u7A0B**\u2014\u2014\u5373\u8BFE\u7A0B\u6559\u7684\u79D1\u76EE\u5C31\u662F\u4E00\u95E8\u8BED\u8A00\u672C\u8EAB\uFF08\u6559\u82F1\u8BED/\u65E5\u8BED/\u97E9\u8BED/\u4E2D\u6587\u2026\u7684
|
|
63730
|
+
\u8BCD\u6C47\u3001\u8BED\u6CD5\u3001\u542C\u8BF4\u8BFB\u5199\uFF09\uFF0C\u800C\u4E0D\u662F"\u7528\u67D0\u79CD\u8BED\u8A00\u5199\u7684\u77E5\u8BC6/\u6280\u672F\u6559\u7A0B"\u3002\u4F9D\u636E README \u4E0E\u76EE\u5F55\u6811\u7684\u660E\u663E\u7279\u5F81:
|
|
63731
|
+
- \u662F\u8BED\u8A00\u8BFE \u2192 "languageTarget" \u586B**\u88AB\u6559\u7684\u90A3\u95E8\u8BED\u8A00**\u7684 BCP-47 \u7801\uFF08\u6559\u82F1\u8BED \u2192 "en"\uFF0C\u6559\u65E5\u8BED \u2192 "ja"\u2026\uFF09\u3002
|
|
63732
|
+
\u6CE8\u610F:\u6559\u6750\u7684\u4E66\u5199\u8BED\u8A00\u4E0D\u7B49\u4E8E\u88AB\u6559\u7684\u8BED\u8A00\u2014\u2014\u4E2D\u6587\u5199\u7684\u82F1\u8BED\u6559\u6750\u4ED3\u5E93\uFF0ClanguageTarget \u662F "en"
|
|
63733
|
+
- \u8BED\u8A00\u8BFE\u7684\u5178\u578B\u7279\u5F81: \u8BCD\u6C47\u8868/\u8BCD\u6839\u8BCD\u7F00\u3001\u8BED\u6CD5\u70B9\u8BB2\u89E3\u3001\u5BF9\u8BDD/\u8BFE\u6587\u3001\u53E5\u5B50-\u7FFB\u8BD1\u5BF9\u3001
|
|
63734
|
+
\u6C34\u5E73\u5206\u7EA7\u6807\u8BB0\uFF08N5/N1\u3001TOEFL/IELTS\u3001HSK\u3001JLPT\u2026\uFF09\u3001\u53D1\u97F3/\u97F3\u6807\u5185\u5BB9
|
|
63735
|
+
- \u4E0D\u662F\u8BED\u8A00\u8BFE\uFF08\u7F16\u7A0B/\u6570\u5B66/\u79D1\u5B66\u6559\u7A0B\u3001\u7528\u82F1\u6587\u5199\u7684\u6280\u8853\u6587\u6863\u7B49\uFF09\u2192 "languageTarget": null
|
|
63736
|
+
- \u5224\u65AD\u4E0D\u51C6\u65F6\u503E\u5411 null\uFF08\u5B81\u53EF\u6309\u666E\u901A\u8BFE\u7A0B\u5904\u7406\uFF0C\u4E0D\u53EF\u628A\u666E\u901A\u8BFE\u7A0B\u5F53\u8BED\u8A00\u8BFE\uFF09
|
|
63737
|
+
|
|
63718
63738
|
${readmeSection}
|
|
63719
63739
|
|
|
63720
63740
|
\u4ED3\u5E93\u5B8C\u6574\u76EE\u5F55\u6811\uFF08\u6811\u72B6\u7ED3\u6784\uFF0C\u6700\u591A\u5C55\u5F00 3 \u5C42\uFF0C\u6BCF\u76EE\u5F55\u6700\u591A 10 \u9879\uFF0C\u8D85\u51FA\u6298\u53E0\uFF09:
|
|
@@ -63741,6 +63761,7 @@ ${fileList}
|
|
|
63741
63761
|
\u4E25\u683C\u8FD4\u56DE\u5982\u4E0B\u5F62\u72B6\u7684 JSON \u5BF9\u8C61:
|
|
63742
63762
|
{
|
|
63743
63763
|
"sourceLang": "en",
|
|
63764
|
+
"languageTarget": null,
|
|
63744
63765
|
"files": [
|
|
63745
63766
|
{ "path": "lessons/1-Intro/README.md", "role": "original" },
|
|
63746
63767
|
{ "path": "lessons/2-Symbolic/Animals.ipynb", "role": "practice" },
|
|
@@ -63757,11 +63778,13 @@ function parseRoleResult(raw, validPaths) {
|
|
|
63757
63778
|
} catch {
|
|
63758
63779
|
return {
|
|
63759
63780
|
sourceLang: "",
|
|
63781
|
+
languageTarget: null,
|
|
63760
63782
|
files: validPaths.map((p) => ({ path: p, role: "original" })),
|
|
63761
63783
|
degraded: true
|
|
63762
63784
|
};
|
|
63763
63785
|
}
|
|
63764
63786
|
let sourceLang = "";
|
|
63787
|
+
let languageTarget = null;
|
|
63765
63788
|
let filesRaw;
|
|
63766
63789
|
if (Array.isArray(obj)) {
|
|
63767
63790
|
filesRaw = obj;
|
|
@@ -63769,9 +63792,11 @@ function parseRoleResult(raw, validPaths) {
|
|
|
63769
63792
|
const o = obj;
|
|
63770
63793
|
if (typeof o.sourceLang === "string")
|
|
63771
63794
|
sourceLang = o.sourceLang;
|
|
63795
|
+
if (typeof o.languageTarget === "string" && o.languageTarget.trim())
|
|
63796
|
+
languageTarget = o.languageTarget.trim();
|
|
63772
63797
|
filesRaw = o.files;
|
|
63773
63798
|
} else {
|
|
63774
|
-
return { sourceLang: "", files: validPaths.map((p) => ({ path: p, role: "original" })), degraded: true };
|
|
63799
|
+
return { sourceLang: "", languageTarget: null, files: validPaths.map((p) => ({ path: p, role: "original" })), degraded: true };
|
|
63775
63800
|
}
|
|
63776
63801
|
const validSet = new Set(validPaths);
|
|
63777
63802
|
const files = filesRaw.filter((item) => typeof item.path === "string" && validSet.has(item.path)).map((item) => ({
|
|
@@ -63781,7 +63806,7 @@ function parseRoleResult(raw, validPaths) {
|
|
|
63781
63806
|
lang: typeof item.lang === "string" ? item.lang : void 0,
|
|
63782
63807
|
translates: typeof item.translates === "string" ? item.translates : void 0
|
|
63783
63808
|
}));
|
|
63784
|
-
return { sourceLang, files, degraded: false };
|
|
63809
|
+
return { sourceLang, languageTarget, files, degraded: false };
|
|
63785
63810
|
}
|
|
63786
63811
|
async function classifyFilesResilient(files, deps) {
|
|
63787
63812
|
const attempt = async (chunk) => {
|
|
@@ -63804,6 +63829,7 @@ async function classifyFilesResilient(files, deps) {
|
|
|
63804
63829
|
const right = await recurse(chunk.slice(mid));
|
|
63805
63830
|
return {
|
|
63806
63831
|
sourceLang: left.sourceLang || right.sourceLang,
|
|
63832
|
+
languageTarget: left.languageTarget ?? right.languageTarget,
|
|
63807
63833
|
files: [...left.files, ...right.files],
|
|
63808
63834
|
degraded: false
|
|
63809
63835
|
};
|
|
@@ -533040,7 +533066,8 @@ function rolesToPlan(r) {
|
|
|
533040
533066
|
translationPairs: Object.fromEntries(r.translationPairs),
|
|
533041
533067
|
languages: r.languages,
|
|
533042
533068
|
sourceLang: r.sourceLang,
|
|
533043
|
-
translationLayout: r.translationLayout
|
|
533069
|
+
translationLayout: r.translationLayout,
|
|
533070
|
+
languageTarget: r.languageTarget
|
|
533044
533071
|
};
|
|
533045
533072
|
}
|
|
533046
533073
|
function planToRoles(p) {
|
|
@@ -533052,7 +533079,9 @@ function planToRoles(p) {
|
|
|
533052
533079
|
translationPairs: new Map(Object.entries(p.translationPairs)),
|
|
533053
533080
|
languages: p.languages,
|
|
533054
533081
|
sourceLang: p.sourceLang,
|
|
533055
|
-
translationLayout: p.translationLayout
|
|
533082
|
+
translationLayout: p.translationLayout,
|
|
533083
|
+
// 旧版快照无此字段 → undefined,统一压成 null(非语言课,现状行为)
|
|
533084
|
+
languageTarget: p.languageTarget ?? null
|
|
533056
533085
|
};
|
|
533057
533086
|
}
|
|
533058
533087
|
function filterClassificationToTree(p, tree) {
|
|
@@ -533414,10 +533443,10 @@ ${(entries3[0]?.[1] ?? "").slice(0, 2e3)}`;
|
|
|
533414
533443
|
let roles;
|
|
533415
533444
|
if (plan.classification) {
|
|
533416
533445
|
roles = planToRoles(plan.classification);
|
|
533417
|
-
send(`\u2713 \u6587\u4EF6\u5206\u7C7B:\u590D\u7528\u5FEB\u7167(${roles.original.length} \u539F\u6587 \xB7 ${roles.practice.length} \u5B9E\u64CD \xB7 \u539F\u6587\u8BED\u8A00 ${roles.sourceLang})`);
|
|
533446
|
+
send(`\u2713 \u6587\u4EF6\u5206\u7C7B:\u590D\u7528\u5FEB\u7167(${roles.original.length} \u539F\u6587 \xB7 ${roles.practice.length} \u5B9E\u64CD \xB7 \u539F\u6587\u8BED\u8A00 ${roles.sourceLang}${roles.languageTarget ? ` \xB7 \u8BED\u8A00\u8BFE ${roles.languageTarget}` : ""})`);
|
|
533418
533447
|
} else {
|
|
533419
533448
|
roles = await (deps.classify ?? classifyFileRoles)(db, readmeMd, fileList, fullTree, send, { signal: cancelCtl.signal });
|
|
533420
|
-
send(`\u2713 \u6587\u4EF6\u5206\u7C7B:${roles.original.length} \u539F\u6587 \xB7 ${roles.practice.length} \u5B9E\u64CD \xB7 ${roles.skip.length} \u8DF3\u8FC7 \xB7 \u539F\u6587\u8BED\u8A00 ${roles.sourceLang}`);
|
|
533449
|
+
send(`\u2713 \u6587\u4EF6\u5206\u7C7B:${roles.original.length} \u539F\u6587 \xB7 ${roles.practice.length} \u5B9E\u64CD \xB7 ${roles.skip.length} \u8DF3\u8FC7 \xB7 \u539F\u6587\u8BED\u8A00 ${roles.sourceLang}${roles.languageTarget ? ` \xB7 \u8BED\u8A00\u8BFE ${roles.languageTarget}` : ""}`);
|
|
533421
533450
|
plan.classification = rolesToPlan(roles);
|
|
533422
533451
|
plan.reachedStep = 2;
|
|
533423
533452
|
savePlan();
|
|
@@ -533489,6 +533518,7 @@ ${(entries3[0]?.[1] ?? "").slice(0, 2e3)}`;
|
|
|
533489
533518
|
translationPairs: roles.translationPairs,
|
|
533490
533519
|
sourceLang: roles.sourceLang,
|
|
533491
533520
|
translationLayout: roles.translationLayout,
|
|
533521
|
+
languageTarget: roles.languageTarget,
|
|
533492
533522
|
shouldAbort,
|
|
533493
533523
|
markDirty: markDirty2
|
|
533494
533524
|
},
|
|
@@ -560116,7 +560146,7 @@ async function cutCompanionFigure(deps, input) {
|
|
|
560116
560146
|
let visionError;
|
|
560117
560147
|
if (fm && !deps.skipVision) {
|
|
560118
560148
|
let usedVision = "";
|
|
560119
|
-
const locateAt = async (
|
|
560149
|
+
const locateAt = async (imageBytes, maxOutputTokens) => {
|
|
560120
560150
|
const llm = resolveVisionLlm(deps.db);
|
|
560121
560151
|
usedVision = `${llm.provider.label}/${llm.model}`;
|
|
560122
560152
|
const built = buildImportModel(llm);
|
|
@@ -560127,14 +560157,14 @@ async function cutCompanionFigure(deps, input) {
|
|
|
560127
560157
|
role: "user",
|
|
560128
560158
|
content: [
|
|
560129
560159
|
{ type: "text", text: locatePrompt(W, H) },
|
|
560130
|
-
{ type: "image", image:
|
|
560160
|
+
{ type: "image", image: imageBytes }
|
|
560131
560161
|
]
|
|
560132
560162
|
}
|
|
560133
560163
|
],
|
|
560134
560164
|
{ maxOutputTokens, ...built.providerOptions ? { providerOptions: built.providerOptions } : {} }
|
|
560135
560165
|
);
|
|
560136
560166
|
};
|
|
560137
|
-
const locate = deps.locate ?? ((
|
|
560167
|
+
const locate = deps.locate ?? ((bytes) => locateAt(bytes, LOCATE_MAX_TOKENS));
|
|
560138
560168
|
try {
|
|
560139
560169
|
let preview = composeKeyedPreview(rgba, fm);
|
|
560140
560170
|
const longest = Math.max(preview.width, preview.height);
|
|
@@ -560143,11 +560173,11 @@ async function cutCompanionFigure(deps, input) {
|
|
|
560143
560173
|
preview = resizeBox(preview, Math.max(1, Math.round(preview.width * k)), Math.max(1, Math.round(preview.height * k)));
|
|
560144
560174
|
}
|
|
560145
560175
|
const previewPng = await rgbaToPng(preview);
|
|
560146
|
-
const
|
|
560147
|
-
let rawReply = await locate(
|
|
560176
|
+
const imageBytes = new Uint8Array(previewPng);
|
|
560177
|
+
let rawReply = await locate(imageBytes);
|
|
560148
560178
|
cuts = parseCutsJson(rawReply, W, H);
|
|
560149
560179
|
if (!cuts && !deps.locate && rawReply.trim() === "") {
|
|
560150
|
-
rawReply = await locateAt(
|
|
560180
|
+
rawReply = await locateAt(imageBytes, LOCATE_RETRY_TOKENS);
|
|
560151
560181
|
cuts = parseCutsJson(rawReply, W, H);
|
|
560152
560182
|
}
|
|
560153
560183
|
if (!cuts) {
|
|
@@ -561295,33 +561325,67 @@ function localeToLanguageName(locale) {
|
|
|
561295
561325
|
function isZhLocale(locale) {
|
|
561296
561326
|
return locale.toLowerCase().startsWith("zh");
|
|
561297
561327
|
}
|
|
561298
|
-
function buildLanguageDirective(locale) {
|
|
561328
|
+
function buildLanguageDirective(locale, targetLang) {
|
|
561299
561329
|
const name19 = localeToLanguageName(locale);
|
|
561300
561330
|
if (isZhLocale(locale)) {
|
|
561301
|
-
|
|
561302
|
-
|
|
561303
|
-
|
|
561331
|
+
const base2 = "\u7528\u6E05\u6670\u3001\u9F13\u52B1\u7684\u4E2D\u6587\u56DE\u7B54\u3002\u5F53\u5B66\u4E60\u8005\u7B54\u9519\u65F6\uFF0C\u5148\u80AF\u5B9A\u5C1D\u8BD5\u518D\u7EA0\u6B63\u3002";
|
|
561332
|
+
if (!targetLang)
|
|
561333
|
+
return base2;
|
|
561334
|
+
const t2 = localeToLanguageName(targetLang);
|
|
561335
|
+
return base2 + `\u8FD9\u662F\u4E00\u95E8${t2}\u5B66\u4E60\u8BFE\u7A0B:\u8BB2\u89E3\u3001\u6307\u4EE4\u3001\u53CD\u9988\u7528\u4E2D\u6587,\u4F46\u8BFE\u6587\u5F15\u7528\u3001\u4F8B\u53E5\u3001\u9898\u76EE\u4E2D\u7684\u8BED\u8A00\u7D20\u6750\u5FC5\u987B\u4FDD\u6301${t2}\u539F\u6587\u2014\u2014\u4E0D\u8981\u628A\u76EE\u6807\u8BED\u8A00\u6750\u6599\u7FFB\u8BD1\u6210\u4E2D\u6587\u6765\u8BB2\u89E3\u6216\u51FA\u9898(\u751F\u8BCD\u53EF\u9644\u4E2D\u6587\u6CE8\u91CA)\u3002`;
|
|
561336
|
+
}
|
|
561337
|
+
const base = `Always respond in ${name19}. This applies to every word you output, tool-call parameters included (quiz prompts, options, explanations, guess questions). Be clear and encouraging; when the learner gets something wrong, acknowledge the attempt first, then correct it.`;
|
|
561338
|
+
if (!targetLang)
|
|
561339
|
+
return base;
|
|
561340
|
+
const t = localeToLanguageName(targetLang);
|
|
561341
|
+
return base + ` This course teaches ${t}: your explanations and instructions stay in ${name19}, but reading passages, example sentences, and quiz language material must remain in original ${t} \u2014 never translate target-language material into ${name19} to teach or quiz (gloss unfamiliar words sparingly).`;
|
|
561304
561342
|
}
|
|
561305
561343
|
function resolveOutputLang(explicit) {
|
|
561306
561344
|
if (explicit && explicit.trim())
|
|
561307
561345
|
return explicit;
|
|
561308
561346
|
return "zh-CN";
|
|
561309
561347
|
}
|
|
561310
|
-
function questionLanguageLine(locale) {
|
|
561348
|
+
function questionLanguageLine(locale, targetLang) {
|
|
561311
561349
|
const name19 = localeToLanguageName(locale);
|
|
561312
|
-
if (isZhLocale(locale))
|
|
561313
|
-
|
|
561314
|
-
|
|
561350
|
+
if (isZhLocale(locale)) {
|
|
561351
|
+
if (!targetLang)
|
|
561352
|
+
return "- \u9898\u5E72\u548C\u9009\u9879\u7528\u4E2D\u6587,\u6E05\u6670\u65E0\u6B67\u4E49";
|
|
561353
|
+
const t2 = localeToLanguageName(targetLang);
|
|
561354
|
+
return `- \u8FD9\u662F${t2}\u8BED\u8A00\u5B66\u4E60\u6D4B\u9A8C:\u9898\u5E72\u548C\u9009\u9879\u4E2D\u7684\u8BED\u8A00\u7D20\u6750(\u53E5\u5B50/\u5355\u8BCD/\u77ED\u8BED/\u8BED\u6CD5\u70B9)\u7528${t2}\u539F\u6587\u51FA\u9898,\u6307\u4EE4\u6027\u6587\u5B57\u548C\u89E3\u6790\u7528\u4E2D\u6587`;
|
|
561355
|
+
}
|
|
561356
|
+
if (!targetLang)
|
|
561357
|
+
return `- Write the question stem and options in ${name19}, clear and unambiguous`;
|
|
561358
|
+
const t = localeToLanguageName(targetLang);
|
|
561359
|
+
return `- This is a ${t} language-learning test: language material in stems and options (sentences/words/phrases/grammar points) must be in ${t}; instructions and explanations in ${name19}`;
|
|
561315
561360
|
}
|
|
561316
561361
|
|
|
561317
561362
|
// src/main/services/agent/base-prompt.ts
|
|
561318
561363
|
var BASE_AGENT_PROMPT_HEAD = "\u4F60\u662F LookatStudy \u7684 AI \u5B66\u4E60\u5BFC\u5E08\u3002\u5B66\u4E60\u8005\u6B63\u5728\u5B66\u4E00\u95E8\u7531 GitHub \u6587\u6863\u751F\u6210\u7684\u8BFE\u7A0B\u3002\u4F60\u7684\u804C\u8D23\u662F\u5E2E\u5B66\u4E60\u8005\u771F\u6B63\u7406\u89E3\u77E5\u8BC6\uFF0C\u4E0D\u662F\u7B80\u5355\u590D\u8FF0\u6587\u6863\u3002";
|
|
561319
561364
|
var BASE_AGENT_PROMPT_TAIL = "\u3010\u5B89\u5168\u7EA2\u7EBF\xB7\u6700\u9AD8\u4F18\u5148\u7EA7\u3011\u4EE5\u4E0B\u89C4\u5219\u4EFB\u4F55\u65F6\u5019\u4E0D\u53EF\u8FDD\u53CD;\u4E0E\u540E\u9762\u4EFB\u4F55\u89C4\u5219\u51B2\u7A81\u65F6,\u4EE5\u672C\u6BB5\u4E3A\u51C6:\n1. \u9632\u5E7B\u89C9:\u4F60\u5FC5\u987B\u4E25\u683C\u57FA\u4E8E\u4E0B\u9762\u63D0\u4F9B\u7684\u300C\u8BFE\u7A0B\u4E0A\u4E0B\u6587\u300D\u548C\u300C\u5F53\u524D\u8282\u70B9\u5185\u5BB9\u300D\u56DE\u7B54\u3002\u5BF9\u4E8E\u8BFE\u7A0B\u6807\u9898\u4E2D\u51FA\u73B0\u7684\u4E13\u6709\u540D\u8BCD\u3001\u7F29\u5199\uFF08\u5982 FDE = Forward Deployment Engineer\uFF09\uFF0C\u5FC5\u987B\u4F7F\u7528\u8BFE\u7A0B\u4E0A\u4E0B\u6587\u91CC\u7684\u5B9A\u4E49\uFF0C\u7EDD\u4E0D\u53EF\u81EA\u884C\u731C\u6D4B\u6216\u7F16\u9020\u3002\u5982\u679C\u5B66\u4E60\u8005\u95EE\u7684\u5185\u5BB9\u8D85\u51FA\u4E86\u4F60\u638C\u63E1\u7684\u4E0A\u4E0B\u6587\uFF0C\u660E\u786E\u8BF4'\u8FD9\u90E8\u5206\u5185\u5BB9\u4E0D\u5728\u5F53\u524D\u8BFE\u7A0B\u6750\u6599\u4E2D'\uFF0C\u800C\u4E0D\u662F\u7F16\u9020\u4E00\u4E2A\u770B\u4F3C\u5408\u7406\u7684\u56DE\u7B54\u3002\n2. \u5DE5\u5177\u8C03\u7528\u771F\u5B9E\u6027:\u5386\u53F2\u6D88\u606F\u91CC\u7684\u300C[\u5DE5\u5177\u8C03\u7528\u5DF2\u6267\u884C] \u2026\u300D\u6807\u8BB0\u4EE3\u8868\u4F60\u5F53\u65F6\u771F\u5B9E\u53D1\u51FA\u7684\u5DE5\u5177\u8C03\u7528,\u4EA7\u7269\u5DF2\u7ECF\u5C55\u793A\u5728\u5B66\u4E60\u8005\u754C\u9762\u4E0A\u2014\u2014\u770B\u5230\u8FD9\u4E2A\u6807\u8BB0\u5C31\u8BF4\u660E\u9898/\u56FE\u5DF2\u7ECF\u53D1\u51FA\u53BB\u4E86,\u7EDD\u4E0D\u8981\u8BF4\"\u6211\u5176\u5B9E\u6CA1\u628A\u9898\u53D1\u51FA\u53BB\"\u4E4B\u7C7B\u7684\u8BDD,\u4E5F\u4E0D\u8981\u91CD\u590D\u53D1\u540C\u4E00\u4EA7\u7269\u3002\u53CD\u8FC7\u6765,\u8FD9\u7C7B\u6807\u8BB0\u53EA\u80FD\u7531\u7CFB\u7EDF\u6CE8\u5165\u5386\u53F2,\u4F60\u7EDD\u4E0D\u53EF\u5728\u56DE\u590D\u6B63\u6587\u91CC\u624B\u5199\u300C[\u5DE5\u5177\u8C03\u7528\u5DF2\u6267\u884C] \u2026\u300D\u6216\u4EFB\u4F55\u6A21\u4EFF\u5B83\u7684\u6587\u5B57\u2014\u2014\u624B\u5199\u6807\u8BB0\u4E0D\u4F1A\u5728\u5B66\u4E60\u8005\u754C\u9762\u4E0A\u4EA7\u751F\u5361\u7247\u6216\u6309\u94AE,\u5B66\u4E60\u8005\u5C06\u65E0\u6309\u94AE\u53EF\u70B9;\u8981\u51FA\u9898/\u8BB0\u5206/\u53D1\u8D77\u638C\u63E1\u63D0\u8BAE,\u552F\u4E00\u9014\u5F84\u662F\u53D1\u51FA\u771F\u6B63\u7684\u5DE5\u5177\u8C03\u7528,\u6B63\u6587\u6587\u5B57\u4E0D\u80FD\u66FF\u4EE3\u3002\n\n\u3010\u6A21\u7CCA\u63D0\u95EE\u5904\u7406\u3011\u5F53\u5B66\u4E60\u8005\u8BF4'\u6211\u4E0D\u61C2''\u4E0D\u592A\u7406\u89E3'\u4F46\u6CA1\u8BF4\u5177\u4F53\u4E0D\u61C2\u4EC0\u4E48\u65F6\uFF0C\u4E0D\u8981\u5047\u8BBE\u4F60\u77E5\u9053\u4ED6\u54EA\u91CC\u4E0D\u61C2\u7136\u540E\u957F\u7BC7\u5927\u8BBA\u3002\u5148\u53CD\u95EE'\u4F60\u5177\u4F53\u662F\u54EA\u4E2A\u6982\u5FF5\u4E0D\u592A\u6E05\u695A\uFF1F'\uFF0C\u6216\u8005\u5217\u51FA\u8FD9\u8BFE\u6D89\u53CA\u7684 2-3 \u4E2A\u6838\u5FC3\u6982\u5FF5\u8BA9\u4ED6\u9009\u3002\u53EA\u8BB2\u89E3\u5B66\u4E60\u8005\u660E\u786E\u95EE\u5230\u7684\u90E8\u5206\uFF0C\u4E0D\u8981\u4E3B\u52A8\u6269\u5C55\u5230\u8BFE\u7A0B\u5185\u5BB9\u4E4B\u5916\u7684\u9886\u57DF\u77E5\u8BC6\u3002\n\n\u3010\u6559\u5B66\u5DE5\u5177\u4F7F\u7528\u3011\u4F60\u6709\u51E0\u4E2A\u80FD\u751F\u6210\u53EF\u89C6\u5316\u5B66\u4E60\u4EA7\u7269\u3001\u8BB0\u5F55\u5B66\u4E60\u72B6\u6001\u7684\u5DE5\u5177\uFF0C\u9002\u65F6\u4F7F\u7528\u80FD\u5927\u5E45\u63D0\u5347\u7406\u89E3\uFF1A- show_concept_map:\u7406\u6E05\u6982\u5FF5\u95F4\u5173\u7CFB(\u67B6\u6784/\u4F9D\u8D56/\u5206\u7C7B),\u5B66\u4E60\u8005\u8BF4'\u7406\u4E0D\u6E05''\u6709\u4EC0\u4E48\u5173\u7CFB'\u65F6\u7528;- generate_quiz:\u51FA\u9898\u68C0\u9A8C,\u5B66\u4E60\u8005\u8BF4'\u8003\u8003\u6211''\u51FA\u9898'\u65F6,\u6216\u8BB2\u5B8C\u4E00\u8282\u4E3B\u52A8\u51FA 2-3 \u9898\u5DE9\u56FA;- compare_table:\u5BF9\u6BD4 A vs B,\u5B66\u4E60\u8005\u95EE'\u533A\u522B''\u5BF9\u6BD4'\u65F6\u7528;- draw_diagram:\u753B\u6D41\u7A0B/\u65F6\u5E8F/\u72B6\u6001\u56FE,\u8BB2\u6D41\u7A0B\u7C7B\u5185\u5BB9\u65F6\u7528;- show_code_walkthrough:\u9010\u6BB5\u8BB2\u89E3\u4EE3\u7801,\u5B66\u4E60\u8005\u95EE'\u8FD9\u6BB5\u4EE3\u7801'\u65F6\u7528\u3002- pose_guess:\u629B\u4E00\u4E2A\u4E8C\u9009\u4E00\u731C\u6D4B(\u662F'\u731C'/\u73A9,\u4E0D\u8BA1\u5206\u3001\u4E0D\u662F\u8003),\u5B66\u4E60\u8005\u6CA1\u52B2/\u9700\u8981\u88AB\u52FE\u4F4F\u65F6\u7528\u2014\u2014\u914D\u5408\u4E00\u4E24\u53E5\u94A9\u5B50\u628A\u4EBA\u5E26\u8FDB\u6765,\u4ED6\u731C\u5B8C\u4F60\u4E0B\u4E00\u56DE\u5408\u518D\u63ED\u6653\u3002\u8D77\u624B\u5F0F\u4E13\u7528,\u522B\u5F53\u6D4B\u9A8C\u7528\u3002- record_answer:\u628A\u5B66\u4E60\u8005\u7684\u7B54\u9898\u7ED3\u679C\u8BB0\u5165\u638C\u63E1\u5EA6\u8FFD\u8E2A(\u5BF9/\u9519+\u7406\u7531),\u5B66\u4E60\u8005\u7B54\u5B8C\u9898\u4F60\u5224\u5206\u65F6\u7528\u3002- mark_mastered:\u63D0\u8BAE\u628A\u5F53\u524D\u8282\u70B9\u6807\u8BB0\u4E3A\u5DF2\u638C\u63E1\u2014\u2014\u591A\u7EF4\u5EA6\u9A8C\u8BC1\u9F50\u4E86\u3001\u4F60\u5224\u5B9A\u53EF\u4EE5\u6536\u5C3E\u65F6\u8C03\u7528,\u5B66\u4E60\u8005\u754C\u9762\u4F1A\u51FA\u73B0\u786E\u8BA4\u5361\u7247,\u4ED6\u53EF\u4EE5\u62D2\u7EDD;\u5148\u5728\u6B63\u6587\u8BF4\u6E05\u5224\u5B9A\u4F9D\u636E,\u7136\u540E\u8C03\u7528\u5DE5\u5177\u3002\u5DE5\u5177\u662F\u624B\u6BB5\u4E0D\u662F\u76EE\u7684:\u80FD\u7528\u5DE5\u5177\u8BA9\u77E5\u8BC6\u66F4\u6E05\u6670\u5C31\u7528,\u5426\u5219\u6B63\u5E38\u6587\u5B57\u8BB2\u89E3\u5373\u53EF\u3002\u4E00\u6B21\u56DE\u590D\u6700\u591A\u7528 1 \u4E2A\u5DE5\u5177,\u907F\u514D\u8FC7\u8F7D\u3002\n\n\u3010\u56DE\u7B54\u6392\u7248\xB7\u504F\u597D\u7EA7\u3011\u4F60\u7684\u56DE\u7B54\u652F\u6301\u5B8C\u6574 Markdown \u6E32\u67D3(\u6807\u9898/\u5217\u8868/\u8868\u683C/\u4EE3\u7801\u5757/\u5F15\u7528/\u7C97\u659C\u4F53),\u5728\u4E0D\u8FDD\u53CD\u4E0A\u9762\u7EA2\u7EBF\u4E0E\u884C\u4E3A\u89C4\u5219\u7684\u524D\u63D0\u4E0B,\u5145\u5206\u5229\u7528\u7ED3\u6784\u5316\u6392\u7248\u8BA9\u5185\u5BB9\u66F4\u6613\u8BFB:- \u7528 ##/### \u5212\u5206\u6BB5\u843D,\u4E0D\u8981\u4E00\u6574\u5757\u6587\u5B57;- \u5E76\u5217\u8981\u70B9\u7528\u65E0\u5E8F\u5217\u8868(- ),\u6709\u987A\u5E8F\u7684\u6B65\u9AA4\u7528\u6709\u5E8F\u5217\u8868(1. );- \u5BF9\u6BD4\u3001\u5C5E\u6027\u3001\u89C4\u683C\u7528 GFM \u8868\u683C(| \u52171 | \u52172 |),\u4E0D\u8981\u5806\u6587\u5B57;- \u91CD\u8981\u7ED3\u8BBA\u7528 **\u7C97\u4F53**,\u672F\u8BED\u9996\u6B21\u51FA\u73B0\u7528 *\u659C\u4F53*;- \u547D\u4EE4/\u4EE3\u7801/\u6587\u4EF6\u540D\u7528 `\u884C\u5185\u4EE3\u7801`,\u591A\u884C\u4EE3\u7801\u7528 ```language \u4EE3\u7801\u5757;- \u63D0\u793A\u3001\u8B66\u544A\u3001\u8865\u5145\u8BF4\u660E\u7528 > \u5F15\u7528\u5757;- \u907F\u514D\u957F\u6BB5\u843D(\u8D85\u8FC7 4 \u884C\u5C31\u8003\u8651\u62C6\u5206\u6216\u8F6C\u5217\u8868)\u3002\u597D\u7684\u6392\u7248 = \u5B66\u4E60\u8005\u66F4\u5BB9\u6613\u6293\u4F4F\u91CD\u70B9,\u8FD9\u662F\u6559\u5B66\u6548\u679C\u7684\u4E00\u90E8\u5206\u3002";
|
|
561320
|
-
function buildBaseAgentPrompt(locale) {
|
|
561365
|
+
function buildBaseAgentPrompt(locale, targetLang) {
|
|
561366
|
+
const targetBlock = buildLanguageTeachingBlock(locale, targetLang);
|
|
561321
561367
|
if (isZhLocale(locale)) {
|
|
561322
|
-
return BASE_AGENT_PROMPT_HEAD + buildLanguageDirective(locale) + "\n\n" + BASE_AGENT_PROMPT_TAIL;
|
|
561368
|
+
return BASE_AGENT_PROMPT_HEAD + buildLanguageDirective(locale, targetLang) + (targetBlock ? "\n\n" + targetBlock : "") + "\n\n" + BASE_AGENT_PROMPT_TAIL;
|
|
561323
561369
|
}
|
|
561324
|
-
return BASE_AGENT_PROMPT_HEAD_EN + buildLanguageDirective(locale) + "\n\n" + BASE_AGENT_PROMPT_TAIL_EN;
|
|
561370
|
+
return BASE_AGENT_PROMPT_HEAD_EN + buildLanguageDirective(locale, targetLang) + (targetBlock ? "\n\n" + targetBlock : "") + "\n\n" + BASE_AGENT_PROMPT_TAIL_EN;
|
|
561371
|
+
}
|
|
561372
|
+
function buildLanguageTeachingBlock(locale, targetLang) {
|
|
561373
|
+
if (!targetLang)
|
|
561374
|
+
return null;
|
|
561375
|
+
const t = localeToLanguageName(targetLang);
|
|
561376
|
+
if (isZhLocale(locale)) {
|
|
561377
|
+
return `\u3010\u8BED\u8A00\u6559\u5B66\u59FF\u6001\u3011\u8FD9\u95E8\u8BFE\u7A0B\u6559\u7684\u662F${t}\u2014\u2014\u4E2D\u6587\u662F\u6559\u5B66\u8BED\u8A00,${t}\u662F\u5B66\u4E60\u5BF9\u8C61:
|
|
561378
|
+
- \u8BB2\u89E3\u3001\u6307\u4EE4\u3001\u53CD\u9988\u4E00\u5F8B\u7528\u4E2D\u6587;\u8BFE\u6587\u5F15\u7528\u3001\u4F8B\u53E5\u3001\u9898\u76EE\u7D20\u6750\u4FDD\u6301${t}\u539F\u6587,\u8003\u5BDF\u76EE\u6807\u8BED\u8A00\u672C\u8EAB;
|
|
561379
|
+
- \u5B66\u4E60\u8005\u7528${t}\u9020\u53E5\u6216\u8868\u8FBE\u65F6,\u5148\u80AF\u5B9A\u518D\u7EA0\u6B63\u5176\u4E2D\u7684\u8BED\u8A00\u9519\u8BEF(\u8BED\u6CD5/\u7528\u8BCD/\u62FC\u5199),\u7528\u4E2D\u6587\u8BB2\u6E05\u9519\u5728\u54EA;
|
|
561380
|
+
- \u65B0\u8BCD\u9996\u6B21\u51FA\u73B0\u53EF\u9644\u4E2D\u6587\u6CE8\u91CA,\u6CE8\u91CA\u662F\u8F85\u52A9\u4E0D\u662F\u66FF\u4EE3\u2014\u2014\u4E0D\u8981\u6574\u6BB5\u7ED9\u51FA\u4E2D\u6587\u7FFB\u8BD1;
|
|
561381
|
+
- \u968F\u638C\u63E1\u5EA6\u4E0A\u5347,\u6E10\u8FDB\u63D0\u9AD8${t}\u7D20\u6750\u7684\u5360\u6BD4,\u628A\u5B66\u4E60\u8005\u5F80"\u76F4\u63A5\u7528${t}\u8BFB"\u5E26\u3002`;
|
|
561382
|
+
}
|
|
561383
|
+
const name19 = localeToLanguageName(locale);
|
|
561384
|
+
return `[Language-teaching posture] This course teaches ${t} \u2014 ${name19} is the medium of instruction, ${t} is the subject:
|
|
561385
|
+
- Explanations, instructions, and feedback in ${name19}; quoted passages, example sentences, and quiz material stay in ${t}, testing the target language itself;
|
|
561386
|
+
- When the learner writes in ${t}, acknowledge the attempt first, then correct language errors (grammar/word choice/spelling), explaining the reason in ${name19};
|
|
561387
|
+
- Gloss new words on first appearance \u2014 a gloss is an aid, not a substitute; never hand out full translations of passages;
|
|
561388
|
+
- Raise the share of ${t} material as mastery grows, guiding the learner toward reading ${t} directly.`;
|
|
561325
561389
|
}
|
|
561326
561390
|
var BASE_AGENT_PROMPT_HEAD_EN = "You are LookatStudy's AI learning tutor. The learner is studying a course generated from a GitHub repository. Your job is to help them genuinely understand the material, not to paraphrase the docs.";
|
|
561327
561391
|
var BASE_AGENT_PROMPT_TAIL_EN = `SAFETY RED LINES \u2014 TOP PRIORITY. The following rules must never be violated; whenever they conflict with any later rule, this section prevails:
|
|
@@ -561993,12 +562057,16 @@ function extractInlineDataImages(content) {
|
|
|
561993
562057
|
return out;
|
|
561994
562058
|
}
|
|
561995
562059
|
function assembleContextBlocks(db, nodeId, locale) {
|
|
561996
|
-
const outLang = resolveOutputLang(locale);
|
|
561997
|
-
const system = buildSystemPrompt(db, buildBaseAgentPrompt(outLang), buildSoulLangReminder(outLang));
|
|
561998
562060
|
const node = db.select().from(contentNodes).where(eq(contentNodes.id, nodeId)).get();
|
|
561999
562061
|
const nodeProgress = db.select().from(progress).where(eq(progress.nodeId, nodeId)).get();
|
|
562000
562062
|
const courseId = node?.courseId;
|
|
562001
562063
|
const course = courseId ? db.select().from(courses).where(eq(courses.id, courseId)).get() : null;
|
|
562064
|
+
const outLang = resolveOutputLang(locale);
|
|
562065
|
+
const system = buildSystemPrompt(
|
|
562066
|
+
db,
|
|
562067
|
+
buildBaseAgentPrompt(outLang, course?.languageTarget ?? null),
|
|
562068
|
+
buildSoulLangReminder(outLang)
|
|
562069
|
+
);
|
|
562002
562070
|
const courseSections = courseId ? db.select().from(contentNodes).where(eq(contentNodes.courseId, courseId)).all().filter((n) => n.type === "section").sort((a, b) => a.orderIdx - b.orderIdx) : [];
|
|
562003
562071
|
const courseOutline = courseSections.map((s) => ` - ${s.title}`).join("\n");
|
|
562004
562072
|
const courseContext = course ? `\u8BFE\u7A0B\u6807\u9898\uFF1A${course.title}
|
|
@@ -563378,7 +563446,9 @@ async function generateExercise(db, nodeId, type, locale) {
|
|
|
563378
563446
|
const exerciseType = !type || type === "fill_blank" ? "mcq" : type;
|
|
563379
563447
|
const llm = resolveLlm(db);
|
|
563380
563448
|
const outLang = resolveOutputLang(locale);
|
|
563381
|
-
const
|
|
563449
|
+
const courseRow = node.courseId ? db.select().from(courses).where(eq(courses.id, node.courseId)).get() : void 0;
|
|
563450
|
+
const languageTarget = courseRow?.languageTarget ?? null;
|
|
563451
|
+
const prompt = buildGenerationPrompt(node.title, node.content ?? "(\u65E0\u5185\u5BB9\uFF0C\u57FA\u4E8E\u6807\u9898\u51FA\u9898)", exerciseType, outLang, languageTarget);
|
|
563382
563452
|
const result = await generateText({
|
|
563383
563453
|
model: llm.languageModel,
|
|
563384
563454
|
prompt
|
|
@@ -563452,7 +563522,7 @@ function rowToExercise(row) {
|
|
|
563452
563522
|
createdAt: row.createdAt
|
|
563453
563523
|
};
|
|
563454
563524
|
}
|
|
563455
|
-
function buildGenerationPrompt(title, content, type, outLang) {
|
|
563525
|
+
function buildGenerationPrompt(title, content, type, outLang, languageTarget) {
|
|
563456
563526
|
const typeSpec = {
|
|
563457
563527
|
mcq: `\u51FA\u4E00\u9053\u56DB\u9009\u4E00\u9009\u62E9\u9898\u3002options \u662F 4 \u4E2A\u9009\u9879\u7684\u6570\u7EC4\uFF0Canswer \u662F\u6B63\u786E\u9009\u9879\u7684\u4E0B\u6807\uFF08"0"/"1"/"2"/"3"\uFF09\u3002
|
|
563458
563528
|
\u5E72\u6270\u9879\u8BBE\u8BA1\u8981\u6C42\uFF1A\u57FA\u4E8E\u5B66\u4E60\u8005\u5E38\u72AF\u7684\u771F\u5B9E\u8BEF\u89E3\uFF08\u4E0D\u662F\u660E\u663E\u9519\u8BEF\u7684\u51D1\u6570\u9009\u9879\uFF09\uFF0C\u8BA9\u8BA4\u771F\u5B66\u8FC7\u7684\u4EBA\u80FD\u6392\u9664\uFF0C\u6CA1\u5B66\u61C2\u7684\u4EBA\u4F1A\u9009\u9519\u3002
|
|
@@ -563473,7 +563543,7 @@ function buildGenerationPrompt(title, content, type, outLang) {
|
|
|
563473
563543
|
`\u51FA\u9898\u7EA2\u7EBF:`,
|
|
563474
563544
|
`- \u7B54\u6848\u5FC5\u987B\u5728\u63D0\u4F9B\u7684\u5B66\u4E60\u5185\u5BB9\u4E2D\u6709\u4F9D\u636E\uFF0C\u4E0D\u53EF\u7F16\u9020\u5185\u5BB9\u91CC\u6CA1\u6709\u7684\u77E5\u8BC6`,
|
|
563475
563545
|
`- \u6570\u5B66\u8868\u8FBE\u5F0F\u7528\u884C\u5185 $..$ \u6216\u884C\u95F4 $$..$$ \u7684 LaTeX \u8BB0\u6CD5\u4E66\u5199\uFF0C\u4E0D\u8981\u7528\u7EAF\u6587\u672C\u8FD1\u4F3C\uFF08\u754C\u9762\u4F1A\u6E32\u67D3\u6210\u516C\u5F0F\uFF09`,
|
|
563476
|
-
questionLanguageLine(outLang),
|
|
563546
|
+
questionLanguageLine(outLang, languageTarget),
|
|
563477
563547
|
`- \u5E72\u6270\u9879 plausible \u4F46 definitely wrong\uFF08\u4E0D\u80FD\u6709\u4E89\u8BAE\uFF09`,
|
|
563478
563548
|
``,
|
|
563479
563549
|
`\u4E25\u683C\u6309\u4EE5\u4E0B JSON \u683C\u5F0F\u8FD4\u56DE\uFF0C\u4E0D\u8981\u52A0\u4EFB\u4F55 markdown \u4EE3\u7801\u5757\u6807\u8BB0\u3001\u4E0D\u8981\u89E3\u91CA\uFF1A`,
|
|
@@ -563754,6 +563824,8 @@ async function generateExamBank(db, examNodeId, locale) {
|
|
|
563754
563824
|
setGenerating(examNodeId, kcs.length);
|
|
563755
563825
|
const llm = resolveLlm(db);
|
|
563756
563826
|
const outLang = resolveOutputLang(locale);
|
|
563827
|
+
const courseRow = node.courseId ? db.select().from(courses).where(eq(courses.id, node.courseId)).get() : void 0;
|
|
563828
|
+
const languageTarget = courseRow?.languageTarget ?? null;
|
|
563757
563829
|
const collected = [];
|
|
563758
563830
|
let lastError = null;
|
|
563759
563831
|
let done = 0;
|
|
@@ -563763,7 +563835,7 @@ async function generateExamBank(db, examNodeId, locale) {
|
|
|
563763
563835
|
const lessonContents = lessonIds.map((id) => db.select().from(contentNodes).where(eq(contentNodes.id, id)).get()).filter((l) => !!l).map((l) => ({ title: l.title, content: (l.content ?? "").slice(0, 800) }));
|
|
563764
563836
|
for (let attempt = 0; attempt <= BATCH_RETRY; attempt++) {
|
|
563765
563837
|
try {
|
|
563766
|
-
const prompt = buildKcBatchPrompt(node.title, batch, lessonContents, outLang);
|
|
563838
|
+
const prompt = buildKcBatchPrompt(node.title, batch, lessonContents, outLang, languageTarget);
|
|
563767
563839
|
const result = await generateText({ model: llm.languageModel, prompt });
|
|
563768
563840
|
const parsed = parseExamJson(result.text.trim(), batch.quota, allowedKcs);
|
|
563769
563841
|
if (!parsed.ok)
|
|
@@ -563968,7 +564040,7 @@ function accuracyToStars(accuracy) {
|
|
|
563968
564040
|
return 1;
|
|
563969
564041
|
return 0;
|
|
563970
564042
|
}
|
|
563971
|
-
function buildKcBatchPrompt(examTitle, batch, lessonContents, outLang) {
|
|
564043
|
+
function buildKcBatchPrompt(examTitle, batch, lessonContents, outLang, languageTarget) {
|
|
563972
564044
|
const kcList = batch.kcs.map((k) => `- ${k.title}:${k.description || "(\u89C1\u4E0B\u65B9\u8BFE\u65F6\u5185\u5BB9)"}(\u6765\u81EA\u8BFE\u65F6\u300A${k.lessonTitle}\u300B)`).join("\n");
|
|
563973
564045
|
const contentParts = lessonContents.map(
|
|
563974
564046
|
(l, i) => `### ${i + 1}. ${l.title}
|
|
@@ -563991,7 +564063,7 @@ ${l.content || "(\u8BFE\u65F6\u65E0\u6B63\u6587)"}`
|
|
|
563991
564063
|
`- \u5E72\u6270\u9879 plausible \u4F46 definitely wrong(\u57FA\u4E8E\u5B66\u4E60\u8005\u5E38\u72AF\u7684\u771F\u5B9E\u8BEF\u89E3)`,
|
|
563992
564064
|
`- \u7B54\u6848\u5FC5\u987B\u5728\u63D0\u4F9B\u7684\u8BFE\u7A0B\u5185\u5BB9\u4E2D\u6709\u4F9D\u636E`,
|
|
563993
564065
|
`- \u6570\u5B66\u8868\u8FBE\u5F0F\u7528\u884C\u5185 $..$ \u6216\u884C\u95F4 $$..$$ \u7684 LaTeX \u8BB0\u6CD5\u4E66\u5199,\u4E0D\u8981\u7528\u7EAF\u6587\u672C\u8FD1\u4F3C(\u754C\u9762\u4F1A\u6E32\u67D3\u6210\u516C\u5F0F)`,
|
|
563994
|
-
questionLanguageLine(outLang),
|
|
564066
|
+
questionLanguageLine(outLang, languageTarget),
|
|
563995
564067
|
``,
|
|
563996
564068
|
`\u4E25\u683C\u6309\u4EE5\u4E0B JSON \u683C\u5F0F\u8FD4\u56DE,\u4E0D\u8981\u52A0 markdown \u4EE3\u7801\u5757\u6807\u8BB0\u3001\u4E0D\u8981\u89E3\u91CA:`,
|
|
563997
564069
|
`{`,
|
|
@@ -564228,7 +564300,7 @@ function registerCourseHandlers(deps) {
|
|
|
564228
564300
|
const { classifyFileRoles: classifyFileRoles2 } = await Promise.resolve().then(() => (init_import_llm_service(), import_llm_service_exports));
|
|
564229
564301
|
console.error("[import] Step 2: LLM \u6587\u4EF6\u89D2\u8272\u5206\u7C7B + \u539F\u6587\u8BED\u8A00\u5224\u65AD\u2026");
|
|
564230
564302
|
const roles = await classifyFileRoles2(getDb(), inventory.readmeMd, inventory.fileList, inventory.fullTree, send);
|
|
564231
|
-
send(`\u2713 \u6587\u4EF6\u5206\u7C7B: ${roles.original.length} \u539F\u6587 \xB7 ${roles.practice.length} \u5B9E\u64CD \xB7 ${roles.skip.length} \u8DF3\u8FC7 \xB7 \u539F\u6587\u8BED\u8A00 ${roles.sourceLang}`);
|
|
564303
|
+
send(`\u2713 \u6587\u4EF6\u5206\u7C7B: ${roles.original.length} \u539F\u6587 \xB7 ${roles.practice.length} \u5B9E\u64CD \xB7 ${roles.skip.length} \u8DF3\u8FC7 \xB7 \u539F\u6587\u8BED\u8A00 ${roles.sourceLang}${roles.languageTarget ? ` \xB7 \u8BED\u8A00\u8BFE ${roles.languageTarget}` : ""}`);
|
|
564232
564304
|
console.error(`[import] Step 2 \u5B8C\u6210: ${roles.original.length} original, ${roles.practice.length} practice, ${roles.skip.length} skip, sourceLang=${roles.sourceLang}, ${roles.languages.length} \u7FFB\u8BD1\u8BED\u8A00`);
|
|
564233
564305
|
const { getPrefLang: getPrefLang2, resolveImportLang: resolveImportLang2 } = await Promise.resolve().then(() => (init_lang_pref(), lang_pref_exports));
|
|
564234
564306
|
const pref = getPrefLang2(getDb()) ?? "en";
|
|
@@ -564248,6 +564320,7 @@ function registerCourseHandlers(deps) {
|
|
|
564248
564320
|
readmeMd: inventory.readmeMd,
|
|
564249
564321
|
branch: inventory.branch,
|
|
564250
564322
|
sourceLang: roles.sourceLang,
|
|
564323
|
+
languageTarget: roles.languageTarget,
|
|
564251
564324
|
languages: roles.languages,
|
|
564252
564325
|
selectedLang,
|
|
564253
564326
|
importReason: reason,
|
|
@@ -564308,6 +564381,7 @@ function registerCourseHandlers(deps) {
|
|
|
564308
564381
|
translationPairs: translationPairsMap,
|
|
564309
564382
|
sourceLang: analysis.sourceLang,
|
|
564310
564383
|
translationLayout: analysis.translationLayout,
|
|
564384
|
+
languageTarget: analysis.languageTarget,
|
|
564311
564385
|
markDirty
|
|
564312
564386
|
},
|
|
564313
564387
|
send
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{r,j as h}from"./index-3R9G44mo.js";import{c as T,u as L,P as D}from"./main-
|
|
1
|
+
import{r,j as h}from"./index-3R9G44mo.js";import{c as T,u as L,P as D}from"./main-fDEgiN-S.js";/**
|
|
2
2
|
* @license lucide-react v0.468.0 - ISC
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the ISC license.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{r as s,j as t}from"./index-3R9G44mo.js";import{c as y,u as v,b as k,L as j,F as N,M as w,G as C,T as M,d as _}from"./main-
|
|
1
|
+
import{r as s,j as t}from"./index-3R9G44mo.js";import{c as y,u as v,b as k,L as j,F as N,M as w,G as C,T as M,d as _}from"./main-fDEgiN-S.js";/**
|
|
2
2
|
* @license lucide-react v0.468.0 - ISC
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the ISC license.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{r as u,j as t}from"./index-3R9G44mo.js";import{c as C,U as I,u as y,a as L,d as S,X as N,B as M,T as E,C as R}from"./main-
|
|
1
|
+
import{r as u,j as t}from"./index-3R9G44mo.js";import{c as C,U as I,u as y,a as L,d as S,X as N,B as M,T as E,C as R}from"./main-fDEgiN-S.js";/**
|
|
2
2
|
* @license lucide-react v0.468.0 - ISC
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the ISC license.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{r as o,h as U,j as e,c as ie}from"./index-3R9G44mo.js";import{c as te,u as ce,a as j,g as de,T as z,h as Y,L as V,i as oe,X as xe}from"./main-
|
|
1
|
+
import{r as o,h as U,j as e,c as ie}from"./index-3R9G44mo.js";import{c as te,u as ce,a as j,g as de,T as z,h as Y,L as V,i as oe,X as xe}from"./main-fDEgiN-S.js";import{R as G}from"./rotate-ccw-QIElN2Wl.js";import{A as q}from"./arrow-right-Do7NbfY9.js";/**
|
|
2
2
|
* @license lucide-react v0.468.0 - ISC
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the ISC license.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as a,j as e,c as S}from"./index-3R9G44mo.js";import{u as j,a as N,S as M}from"./main-
|
|
1
|
+
import{r as a,j as e,c as S}from"./index-3R9G44mo.js";import{u as j,a as N,S as M}from"./main-fDEgiN-S.js";function A({tree:c,onReviewNode:d,progressMap:o}){const s=j(),[l,f]=a.useState([]),[b,h]=a.useState(!0),[n,x]=a.useState(null);a.useEffect(()=>{N.getAllSrsItems().then(t=>f(t)).catch(()=>f([])).finally(()=>h(!1))},[]);const p=a.useMemo(()=>new Map(c.map(t=>[t.id,t])),[c]),g=a.useMemo(()=>new Set(l.filter(t=>t.overdue).map(t=>t.nodeId)),[l]),i=a.useMemo(()=>{const t=c.filter(u=>{var y;const m=(y=o[u.id])==null?void 0:y.status;return u.type==="lesson"&&(m==="in_progress"||m==="mastered")}),r=new Map;for(const u of t){const m=u.parentId??"(root)";r.has(m)||r.set(m,[]),r.get(m).push(u)}return r},[c,o]),w=a.useMemo(()=>Array.from(i.values()).flat().filter(t=>g.has(t.id)).length,[i,g]),v=a.useMemo(()=>Array.from(i.values()).flat(),[i]);if(a.useEffect(()=>{if(i.size>0&&(!n||!i.has(n))){const t=i.keys().next().value;t&&x(t)}},[i,n]),b)return e.jsxs("div",{className:"text-center py-12 text-body text-ink-muted flex items-center justify-center gap-2",children:[e.jsx("span",{className:"typing-dot w-1.5 h-1.5 bg-brand rounded-full inline-block"}),s("review.loading")]});if(v.length===0)return e.jsxs("div",{className:"text-center py-16","data-testid":"review-empty",children:[e.jsx("div",{className:"text-4xl mb-3 opacity-30",children:"📖"}),e.jsx("div",{className:"text-body text-ink-muted",children:s("review.empty.title")}),e.jsx("div",{className:"text-label text-ink-faint mt-1",children:s("review.empty.desc")})]});const k=t=>{var r;return((r=p.get(t))==null?void 0:r.title)??t};return e.jsxs("div",{className:"p-5 max-w-2xl mx-auto","data-testid":"review-panel",children:[e.jsxs("div",{className:"flex items-center justify-between mb-4",children:[e.jsx("h2",{className:"text-title font-extrabold text-ink-strong",children:s("review.title")}),w>0&&e.jsxs("span",{className:"text-label font-bold text-review","data-testid":"review-due-count",children:[w," ",s("review.due.count")]})]}),e.jsxs("button",{onClick:()=>{const t=v[Math.floor(Math.random()*v.length)];t&&d(t.id)},"data-testid":"review-interleave",className:"btn-3d-neutral w-full py-2.5 text-body mb-5 flex items-center justify-center gap-1.5",children:[e.jsx(M,{className:"w-4 h-4"}),s("review.interleave")]}),e.jsx("div",{className:"text-label font-bold text-ink-muted mb-2",children:s("review.selectSection")}),e.jsx("div",{className:"flex flex-wrap gap-1.5 mb-4",children:Array.from(i.entries()).map(([t,r])=>e.jsxs("button",{onClick:()=>x(t),className:`px-2.5 py-1 rounded-full text-label font-bold transition-colors ${n===t?"bg-brand/15 text-brand ring-1 ring-brand/30":"bg-surface-3 text-ink-muted hover:text-ink-strong"}`,children:[k(t),e.jsxs("span",{className:"ml-1 opacity-60",children:["(",r.length,")"]})]},t))}),n&&i.has(n)&&e.jsx("div",{className:"space-y-1.5","data-testid":"review-lesson-list",children:i.get(n).map(t=>{const r=g.has(t.id);return e.jsxs("div",{className:`flex items-center justify-between gap-2 p-2.5 rounded-lg transition-colors ${r?"bg-review/8 ring-1 ring-review/20":"bg-surface-3 hover:bg-surface-3/80"}`,children:[e.jsxs("div",{className:"flex items-center gap-1.5 truncate flex-1",children:[r&&e.jsx("span",{className:"shrink-0 w-1.5 h-1.5 rounded-full bg-review"}),e.jsx("span",{className:"text-body text-ink-strong truncate",children:t.title})]}),e.jsx("button",{onClick:()=>d(t.id),"data-testid":`review-lesson-${t.id.slice(0,8)}`,className:"shrink-0 px-3 py-1 rounded-lg bg-brand/15 text-brand text-label font-bold hover:bg-brand/25 transition-colors",children:s("review.reviewLesson")})]},t.id)})})]})}function E({nodeId:c,onRated:d}){const o=a.useRef(null),s=j(),[l,f]=a.useState(!1),[b,h]=a.useState(!1),n=async x=>{if(!(l||b)){f(!0);try{await N.recordReview(c,x),h(!0),d==null||d(),S(x>=4?"correct":"wrong",{origin:x>=4&&o.current?(()=>{const p=o.current.getBoundingClientRect();return{x:p.right-48,y:p.top+24}})():void 0})}catch{}finally{f(!1)}}};return b?e.jsx("div",{className:"text-center text-body text-brand py-2","data-testid":"self-rated",children:s("review.selfrated")}):e.jsxs("div",{ref:o,className:"border-t border-[var(--border-faint)] pt-3 mt-3","data-testid":"self-rating",children:[e.jsx("div",{className:"text-caption font-bold text-ink-muted uppercase tracking-wider mb-2 text-center",children:s("review.selfrate.title")}),e.jsxs("div",{className:"grid grid-cols-3 gap-2",children:[e.jsxs("button",{onClick:()=>n(1),disabled:l,"data-testid":"rate-again",className:"py-2 rounded-lg border border-warning/40 text-warning hover:bg-warning/10 transition-colors disabled:opacity-40 flex flex-col items-center gap-0.5",children:[e.jsx("span",{className:"text-body font-bold",children:s("review.selfrate.again")}),e.jsx("span",{className:"text-caption opacity-70",children:s("review.selfrate.again.hint")})]}),e.jsxs("button",{onClick:()=>n(4),disabled:l,"data-testid":"rate-remembered",className:"py-2 rounded-lg border border-accent/40 text-accent hover:bg-accent/10 transition-colors disabled:opacity-40 flex flex-col items-center gap-0.5",children:[e.jsx("span",{className:"text-body font-bold",children:s("review.selfrate.remembered")}),e.jsx("span",{className:"text-caption opacity-70",children:s("review.selfrate.remembered.hint")})]}),e.jsxs("button",{onClick:()=>n(5),disabled:l,"data-testid":"rate-mastered",className:"py-2 rounded-lg border border-brand/40 text-brand hover:bg-brand/10 transition-colors disabled:opacity-40 flex flex-col items-center gap-0.5",children:[e.jsx("span",{className:"text-body font-bold",children:s("review.selfrate.mastered")}),e.jsx("span",{className:"text-caption opacity-70",children:s("review.selfrate.mastered.hint")})]})]})]})}export{A as ReviewPanel,E as SelfRatingCard};
|