papilot 0.8.7 → 0.8.9
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/dist/assets/78-EUC-H-Uf9aYsyl.bcmap +0 -0
- package/dist/assets/78-EUC-V-ChJxef36.bcmap +0 -0
- package/dist/assets/78-H-CSvm1opZ.bcmap +0 -0
- package/dist/assets/78-RKSJ-H-34XPL1-s.bcmap +0 -0
- package/dist/assets/78-RKSJ-V-CyaguwXf.bcmap +0 -0
- package/dist/assets/78-V-CJGcNX1t.bcmap +0 -0
- package/dist/assets/78ms-RKSJ-H-BuwLY695.bcmap +0 -0
- package/dist/assets/78ms-RKSJ-V-4hl06FMz.bcmap +0 -0
- package/dist/assets/83pv-RKSJ-H-DNQtuaxs.bcmap +0 -0
- package/dist/assets/90ms-RKSJ-H-Bhsfh4Ys.bcmap +0 -0
- package/dist/assets/90ms-RKSJ-V-BbstlayK.bcmap +0 -0
- package/dist/assets/90msp-RKSJ-H-GTsoumJQ.bcmap +0 -0
- package/dist/assets/90msp-RKSJ-V-BWep_0YK.bcmap +0 -0
- package/dist/assets/90pv-RKSJ-H-DOcWKG7K.bcmap +0 -0
- package/dist/assets/90pv-RKSJ-V-BuoQaCHG.bcmap +0 -0
- package/dist/assets/Add-H-1H-FQZVX.bcmap +0 -0
- package/dist/assets/Add-RKSJ-H-BpPRzomz.bcmap +0 -0
- package/dist/assets/Add-RKSJ-V-C7YDsPzV.bcmap +0 -0
- package/dist/assets/Add-V-CzFvKnCs.bcmap +0 -0
- package/dist/assets/Adobe-CNS1-0-CfVBOElR.bcmap +0 -0
- package/dist/assets/Adobe-CNS1-1-Cs9TI-Yv.bcmap +0 -0
- package/dist/assets/Adobe-CNS1-2-D3SWzMKj.bcmap +0 -0
- package/dist/assets/Adobe-CNS1-3-BNxX-hNa.bcmap +0 -0
- package/dist/assets/Adobe-CNS1-4-uApBOf1n.bcmap +0 -0
- package/dist/assets/Adobe-CNS1-5-BWUfkzOP.bcmap +0 -0
- package/dist/assets/Adobe-CNS1-6-DfjBny4_.bcmap +0 -0
- package/dist/assets/Adobe-CNS1-UCS2-fO900upv.bcmap +0 -0
- package/dist/assets/Adobe-GB1-0-DCLN2jmY.bcmap +0 -0
- package/dist/assets/Adobe-GB1-1-C_oGAEc2.bcmap +0 -0
- package/dist/assets/Adobe-GB1-2-BT5g0uLo.bcmap +0 -0
- package/dist/assets/Adobe-GB1-3-BWe_tZZF.bcmap +0 -0
- package/dist/assets/Adobe-GB1-4-CYF1KDey.bcmap +0 -0
- package/dist/assets/Adobe-GB1-5-B89PBerI.bcmap +0 -0
- package/dist/assets/Adobe-GB1-UCS2-BA083yIo.bcmap +0 -0
- package/dist/assets/Adobe-Japan1-0-7ueYY-cc.bcmap +0 -0
- package/dist/assets/Adobe-Japan1-1-DymoVrld.bcmap +0 -0
- package/dist/assets/Adobe-Japan1-2-WYxwCoVU.bcmap +0 -0
- package/dist/assets/Adobe-Japan1-3-DU7EG9_k.bcmap +0 -0
- package/dist/assets/Adobe-Japan1-4-CPqEnWe7.bcmap +0 -0
- package/dist/assets/Adobe-Japan1-5-CyFviWdc.bcmap +0 -0
- package/dist/assets/Adobe-Japan1-6-8p9YIft8.bcmap +0 -0
- package/dist/assets/Adobe-Japan1-UCS2-BL-4M2vS.bcmap +0 -0
- package/dist/assets/Adobe-Korea1-0-A0Ew6GJu.bcmap +0 -0
- package/dist/assets/Adobe-Korea1-1-DFSgDZ4w.bcmap +0 -0
- package/dist/assets/Adobe-Korea1-2-LZ89XEQ6.bcmap +0 -0
- package/dist/assets/Adobe-Korea1-UCS2-BEQZuOdo.bcmap +0 -0
- package/dist/assets/B5-H-Cw4_8on0.bcmap +0 -0
- package/dist/assets/B5-V-3ShXAXP7.bcmap +0 -0
- package/dist/assets/B5pc-H-BLVOWhvh.bcmap +0 -0
- package/dist/assets/B5pc-V-1X0fY8mT.bcmap +0 -0
- package/dist/assets/CNS-EUC-H-C1HifF4e.bcmap +0 -0
- package/dist/assets/CNS-EUC-V-DYWNmJNX.bcmap +0 -0
- package/dist/assets/CNS1-H-DAbVz1sI.bcmap +0 -0
- package/dist/assets/CNS1-V-BUvSsmpl.bcmap +0 -0
- package/dist/assets/CNS2-H-COHfjmw4.bcmap +0 -0
- package/dist/assets/CNS2-V-DgHqi_gL.bcmap +3 -0
- package/dist/assets/ETHK-B5-H-1MLb5v9x.bcmap +0 -0
- package/dist/assets/ETHK-B5-V-DmzBBgI2.bcmap +0 -0
- package/dist/assets/ETen-B5-H-BUa4x82H.bcmap +0 -0
- package/dist/assets/ETen-B5-V-CyIout1L.bcmap +0 -0
- package/dist/assets/ETenms-B5-H-DbS1LlRr.bcmap +3 -0
- package/dist/assets/ETenms-B5-V-DsL3AkeM.bcmap +0 -0
- package/dist/assets/EUC-H-D0J4HUB1.bcmap +0 -0
- package/dist/assets/EUC-V-BQ9CW2K_.bcmap +0 -0
- package/dist/assets/Ext-H-BSOXjlXj.bcmap +0 -0
- package/dist/assets/Ext-RKSJ-H-DGTwwnmY.bcmap +0 -0
- package/dist/assets/Ext-RKSJ-V-gySMsgy2.bcmap +0 -0
- package/dist/assets/Ext-V-ByyLJMVu.bcmap +0 -0
- package/dist/assets/GB-EUC-H-OgfW-rVY.bcmap +0 -0
- package/dist/assets/GB-EUC-V-DUJE8ZfE.bcmap +0 -0
- package/dist/assets/GB-H-CjAQlF1j.bcmap +4 -0
- package/dist/assets/GB-V-DQcXdwec.bcmap +0 -0
- package/dist/assets/GBK-EUC-H-CAORqCkf.bcmap +0 -0
- package/dist/assets/GBK-EUC-V-bmo5E_xa.bcmap +0 -0
- package/dist/assets/GBK2K-H-Cv55KPk8.bcmap +0 -0
- package/dist/assets/GBK2K-V-VEqjiYVJ.bcmap +0 -0
- package/dist/assets/GBKp-EUC-H-CElrnV5g.bcmap +0 -0
- package/dist/assets/GBKp-EUC-V-FKuJrjD8.bcmap +0 -0
- package/dist/assets/GBT-EUC-H-BTsQIL1M.bcmap +0 -0
- package/dist/assets/GBT-EUC-V-CCaONgYB.bcmap +0 -0
- package/dist/assets/GBT-H-DL9h-WkS.bcmap +0 -0
- package/dist/assets/GBT-V-DVsZ47Yj.bcmap +0 -0
- package/dist/assets/GBTpc-EUC-H-DnIh5WJ3.bcmap +0 -0
- package/dist/assets/GBTpc-EUC-V-D_dpKF5g.bcmap +0 -0
- package/dist/assets/GBpc-EUC-H-4mXfmDQd.bcmap +0 -0
- package/dist/assets/GBpc-EUC-V-BNBtOgzw.bcmap +0 -0
- package/dist/assets/H-BJYV6_el.bcmap +0 -0
- package/dist/assets/HKdla-B5-H-Bg4tZIUs.bcmap +0 -0
- package/dist/assets/HKdla-B5-V-CHovDFKh.bcmap +0 -0
- package/dist/assets/HKdlb-B5-H-Bf7o2mkD.bcmap +0 -0
- package/dist/assets/HKdlb-B5-V-CDfiFKye.bcmap +0 -0
- package/dist/assets/HKgccs-B5-H-BSg_4Hb2.bcmap +0 -0
- package/dist/assets/HKgccs-B5-V-Bb0FD7uo.bcmap +0 -0
- package/dist/assets/HKm314-B5-H-CUeI2x46.bcmap +0 -0
- package/dist/assets/HKm314-B5-V-BrK5Z_J-.bcmap +0 -0
- package/dist/assets/HKm471-B5-H-B_3IWC_G.bcmap +0 -0
- package/dist/assets/HKm471-B5-V-B7Ks-vkf.bcmap +0 -0
- package/dist/assets/HKscs-B5-H-CXbBbq6g.bcmap +0 -0
- package/dist/assets/HKscs-B5-V-CrljkNDo.bcmap +0 -0
- package/dist/assets/Hankaku-SMDjWZXJ.bcmap +0 -0
- package/dist/assets/Hiragana-Dy6yEHKG.bcmap +0 -0
- package/dist/assets/KSC-EUC-H-CYoqfsaA.bcmap +0 -0
- package/dist/assets/KSC-EUC-V-C8-Y2jkl.bcmap +0 -0
- package/dist/assets/KSC-H-CUtFgdXl.bcmap +0 -0
- package/dist/assets/KSC-Johab-H-CGtV3MPT.bcmap +0 -0
- package/dist/assets/KSC-Johab-V-CWzqnmVV.bcmap +0 -0
- package/dist/assets/KSC-V-CDXb0xyy.bcmap +0 -0
- package/dist/assets/KSCms-UHC-H-i6eA63f9.bcmap +0 -0
- package/dist/assets/KSCms-UHC-HW-H-lQjjyo98.bcmap +0 -0
- package/dist/assets/KSCms-UHC-HW-V-DKemUgQi.bcmap +0 -0
- package/dist/assets/KSCms-UHC-V-DaEePsjg.bcmap +0 -0
- package/dist/assets/KSCpc-EUC-H-BxNLD7US.bcmap +0 -0
- package/dist/assets/KSCpc-EUC-V-Bd6pD5ID.bcmap +0 -0
- package/dist/assets/Katakana-DueN_6wN.bcmap +0 -0
- package/dist/assets/NWP-H-BSh0nlh5.bcmap +0 -0
- package/dist/assets/NWP-V-DvbaGBQ0.bcmap +0 -0
- package/dist/assets/RKSJ-H-CotmKIAY.bcmap +0 -0
- package/dist/assets/RKSJ-V-CVQ5ujKs.bcmap +0 -0
- package/dist/assets/Roman-CGyUGrKi.bcmap +0 -0
- package/dist/assets/UniCNS-UCS2-H-DWzv8UhH.bcmap +0 -0
- package/dist/assets/UniCNS-UCS2-V-CbsnO1ia.bcmap +0 -0
- package/dist/assets/UniCNS-UTF16-H-Bw0JKgCq.bcmap +0 -0
- package/dist/assets/UniCNS-UTF16-V-DoPO_42D.bcmap +0 -0
- package/dist/assets/UniCNS-UTF32-H-IE_GrH2i.bcmap +0 -0
- package/dist/assets/UniCNS-UTF32-V-DpuHKqNE.bcmap +0 -0
- package/dist/assets/UniCNS-UTF8-H-BbLcFh6Q.bcmap +0 -0
- package/dist/assets/UniCNS-UTF8-V-BD4cmnNX.bcmap +0 -0
- package/dist/assets/UniGB-UCS2-H-D7xzRvZN.bcmap +0 -0
- package/dist/assets/UniGB-UCS2-V-COOBrDBb.bcmap +0 -0
- package/dist/assets/UniGB-UTF16-H-DKjbEwc0.bcmap +0 -0
- package/dist/assets/UniGB-UTF16-V-E1RoE6KW.bcmap +0 -0
- package/dist/assets/UniGB-UTF32-H-Behfouj5.bcmap +0 -0
- package/dist/assets/UniGB-UTF32-V-BFm4myiz.bcmap +0 -0
- package/dist/assets/UniGB-UTF8-H-EROft1ro.bcmap +0 -0
- package/dist/assets/UniGB-UTF8-V-C8SGUjFI.bcmap +0 -0
- package/dist/assets/UniJIS-UCS2-H-2DbZ0MaQ.bcmap +0 -0
- package/dist/assets/UniJIS-UCS2-HW-H-BfJ3t__Z.bcmap +0 -0
- package/dist/assets/UniJIS-UCS2-HW-V-BZyQaeYZ.bcmap +0 -0
- package/dist/assets/UniJIS-UCS2-V-Cr3xs5m4.bcmap +0 -0
- package/dist/assets/UniJIS-UTF16-H-BlDDPdCK.bcmap +0 -0
- package/dist/assets/UniJIS-UTF16-V-DfK_Aqy1.bcmap +0 -0
- package/dist/assets/UniJIS-UTF32-H-BIQPWkjv.bcmap +0 -0
- package/dist/assets/UniJIS-UTF32-V-0rh54ED3.bcmap +0 -0
- package/dist/assets/UniJIS-UTF8-H-CGusholx.bcmap +0 -0
- package/dist/assets/UniJIS-UTF8-V-BGWFHinB.bcmap +0 -0
- package/dist/assets/UniJIS2004-UTF16-H-KXixH-HI.bcmap +0 -0
- package/dist/assets/UniJIS2004-UTF16-V-BSHgoaRy.bcmap +0 -0
- package/dist/assets/UniJIS2004-UTF32-H-CXrD-9YE.bcmap +0 -0
- package/dist/assets/UniJIS2004-UTF32-V-CH3k_ouF.bcmap +0 -0
- package/dist/assets/UniJIS2004-UTF8-H-BkOctZjn.bcmap +0 -0
- package/dist/assets/UniJIS2004-UTF8-V-D8AJhywu.bcmap +0 -0
- package/dist/assets/UniJISPro-UCS2-HW-V-DLNkNO1x.bcmap +0 -0
- package/dist/assets/UniJISPro-UCS2-V-BpvX7JBA.bcmap +0 -0
- package/dist/assets/UniJISPro-UTF8-V-Db5K6XXE.bcmap +0 -0
- package/dist/assets/UniJISX0213-UTF32-H-BavAhZjb.bcmap +0 -0
- package/dist/assets/UniJISX0213-UTF32-V-Cz7JBDr9.bcmap +0 -0
- package/dist/assets/UniJISX02132004-UTF32-H-DAH-AgUq.bcmap +0 -0
- package/dist/assets/UniJISX02132004-UTF32-V-BqqDIAe5.bcmap +0 -0
- package/dist/assets/UniKS-UCS2-H-YeUc7k1L.bcmap +0 -0
- package/dist/assets/UniKS-UCS2-V-ahlksFWQ.bcmap +0 -0
- package/dist/assets/UniKS-UTF16-H-B0dvB-Td.bcmap +0 -0
- package/dist/assets/UniKS-UTF16-V-EgvK-6IG.bcmap +0 -0
- package/dist/assets/UniKS-UTF32-H-olR_LtmR.bcmap +0 -0
- package/dist/assets/UniKS-UTF32-V-CnOTbPsn.bcmap +0 -0
- package/dist/assets/UniKS-UTF8-H-CbpebD-y.bcmap +0 -0
- package/dist/assets/UniKS-UTF8-V-BIFFPWWm.bcmap +0 -0
- package/dist/assets/V-BJkwLeFB.bcmap +0 -0
- package/dist/assets/WP-Symbol-C3zzKJnO.bcmap +0 -0
- package/dist/assets/index-5M0Hwxth.css +1 -0
- package/dist/assets/index-n1pYuKIY.js +271 -0
- package/dist/assets/{math-CBzEH7Y2.js → math-yn5UTwU4.js} +1 -1
- package/dist/assets/pdf-view-hEwEpB78.js +598 -0
- package/dist/index.html +2 -2
- package/engine/dist/web/assets/78-EUC-H-Uf9aYsyl.bcmap +0 -0
- package/engine/dist/web/assets/78-EUC-V-ChJxef36.bcmap +0 -0
- package/engine/dist/web/assets/78-H-CSvm1opZ.bcmap +0 -0
- package/engine/dist/web/assets/78-RKSJ-H-34XPL1-s.bcmap +0 -0
- package/engine/dist/web/assets/78-RKSJ-V-CyaguwXf.bcmap +0 -0
- package/engine/dist/web/assets/78-V-CJGcNX1t.bcmap +0 -0
- package/engine/dist/web/assets/78ms-RKSJ-H-BuwLY695.bcmap +0 -0
- package/engine/dist/web/assets/78ms-RKSJ-V-4hl06FMz.bcmap +0 -0
- package/engine/dist/web/assets/83pv-RKSJ-H-DNQtuaxs.bcmap +0 -0
- package/engine/dist/web/assets/90ms-RKSJ-H-Bhsfh4Ys.bcmap +0 -0
- package/engine/dist/web/assets/90ms-RKSJ-V-BbstlayK.bcmap +0 -0
- package/engine/dist/web/assets/90msp-RKSJ-H-GTsoumJQ.bcmap +0 -0
- package/engine/dist/web/assets/90msp-RKSJ-V-BWep_0YK.bcmap +0 -0
- package/engine/dist/web/assets/90pv-RKSJ-H-DOcWKG7K.bcmap +0 -0
- package/engine/dist/web/assets/90pv-RKSJ-V-BuoQaCHG.bcmap +0 -0
- package/engine/dist/web/assets/Add-H-1H-FQZVX.bcmap +0 -0
- package/engine/dist/web/assets/Add-RKSJ-H-BpPRzomz.bcmap +0 -0
- package/engine/dist/web/assets/Add-RKSJ-V-C7YDsPzV.bcmap +0 -0
- package/engine/dist/web/assets/Add-V-CzFvKnCs.bcmap +0 -0
- package/engine/dist/web/assets/Adobe-CNS1-0-CfVBOElR.bcmap +0 -0
- package/engine/dist/web/assets/Adobe-CNS1-1-Cs9TI-Yv.bcmap +0 -0
- package/engine/dist/web/assets/Adobe-CNS1-2-D3SWzMKj.bcmap +0 -0
- package/engine/dist/web/assets/Adobe-CNS1-3-BNxX-hNa.bcmap +0 -0
- package/engine/dist/web/assets/Adobe-CNS1-4-uApBOf1n.bcmap +0 -0
- package/engine/dist/web/assets/Adobe-CNS1-5-BWUfkzOP.bcmap +0 -0
- package/engine/dist/web/assets/Adobe-CNS1-6-DfjBny4_.bcmap +0 -0
- package/engine/dist/web/assets/Adobe-CNS1-UCS2-fO900upv.bcmap +0 -0
- package/engine/dist/web/assets/Adobe-GB1-0-DCLN2jmY.bcmap +0 -0
- package/engine/dist/web/assets/Adobe-GB1-1-C_oGAEc2.bcmap +0 -0
- package/engine/dist/web/assets/Adobe-GB1-2-BT5g0uLo.bcmap +0 -0
- package/engine/dist/web/assets/Adobe-GB1-3-BWe_tZZF.bcmap +0 -0
- package/engine/dist/web/assets/Adobe-GB1-4-CYF1KDey.bcmap +0 -0
- package/engine/dist/web/assets/Adobe-GB1-5-B89PBerI.bcmap +0 -0
- package/engine/dist/web/assets/Adobe-GB1-UCS2-BA083yIo.bcmap +0 -0
- package/engine/dist/web/assets/Adobe-Japan1-0-7ueYY-cc.bcmap +0 -0
- package/engine/dist/web/assets/Adobe-Japan1-1-DymoVrld.bcmap +0 -0
- package/engine/dist/web/assets/Adobe-Japan1-2-WYxwCoVU.bcmap +0 -0
- package/engine/dist/web/assets/Adobe-Japan1-3-DU7EG9_k.bcmap +0 -0
- package/engine/dist/web/assets/Adobe-Japan1-4-CPqEnWe7.bcmap +0 -0
- package/engine/dist/web/assets/Adobe-Japan1-5-CyFviWdc.bcmap +0 -0
- package/engine/dist/web/assets/Adobe-Japan1-6-8p9YIft8.bcmap +0 -0
- package/engine/dist/web/assets/Adobe-Japan1-UCS2-BL-4M2vS.bcmap +0 -0
- package/engine/dist/web/assets/Adobe-Korea1-0-A0Ew6GJu.bcmap +0 -0
- package/engine/dist/web/assets/Adobe-Korea1-1-DFSgDZ4w.bcmap +0 -0
- package/engine/dist/web/assets/Adobe-Korea1-2-LZ89XEQ6.bcmap +0 -0
- package/engine/dist/web/assets/Adobe-Korea1-UCS2-BEQZuOdo.bcmap +0 -0
- package/engine/dist/web/assets/B5-H-Cw4_8on0.bcmap +0 -0
- package/engine/dist/web/assets/B5-V-3ShXAXP7.bcmap +0 -0
- package/engine/dist/web/assets/B5pc-H-BLVOWhvh.bcmap +0 -0
- package/engine/dist/web/assets/B5pc-V-1X0fY8mT.bcmap +0 -0
- package/engine/dist/web/assets/CNS-EUC-H-C1HifF4e.bcmap +0 -0
- package/engine/dist/web/assets/CNS-EUC-V-DYWNmJNX.bcmap +0 -0
- package/engine/dist/web/assets/CNS1-H-DAbVz1sI.bcmap +0 -0
- package/engine/dist/web/assets/CNS1-V-BUvSsmpl.bcmap +0 -0
- package/engine/dist/web/assets/CNS2-H-COHfjmw4.bcmap +0 -0
- package/engine/dist/web/assets/CNS2-V-DgHqi_gL.bcmap +3 -0
- package/engine/dist/web/assets/ETHK-B5-H-1MLb5v9x.bcmap +0 -0
- package/engine/dist/web/assets/ETHK-B5-V-DmzBBgI2.bcmap +0 -0
- package/engine/dist/web/assets/ETen-B5-H-BUa4x82H.bcmap +0 -0
- package/engine/dist/web/assets/ETen-B5-V-CyIout1L.bcmap +0 -0
- package/engine/dist/web/assets/ETenms-B5-H-DbS1LlRr.bcmap +3 -0
- package/engine/dist/web/assets/ETenms-B5-V-DsL3AkeM.bcmap +0 -0
- package/engine/dist/web/assets/EUC-H-D0J4HUB1.bcmap +0 -0
- package/engine/dist/web/assets/EUC-V-BQ9CW2K_.bcmap +0 -0
- package/engine/dist/web/assets/Ext-H-BSOXjlXj.bcmap +0 -0
- package/engine/dist/web/assets/Ext-RKSJ-H-DGTwwnmY.bcmap +0 -0
- package/engine/dist/web/assets/Ext-RKSJ-V-gySMsgy2.bcmap +0 -0
- package/engine/dist/web/assets/Ext-V-ByyLJMVu.bcmap +0 -0
- package/engine/dist/web/assets/FullPaper-zL_YjQvI.js +54 -0
- package/engine/dist/web/assets/GB-EUC-H-OgfW-rVY.bcmap +0 -0
- package/engine/dist/web/assets/GB-EUC-V-DUJE8ZfE.bcmap +0 -0
- package/engine/dist/web/assets/GB-H-CjAQlF1j.bcmap +4 -0
- package/engine/dist/web/assets/GB-V-DQcXdwec.bcmap +0 -0
- package/engine/dist/web/assets/GBK-EUC-H-CAORqCkf.bcmap +0 -0
- package/engine/dist/web/assets/GBK-EUC-V-bmo5E_xa.bcmap +0 -0
- package/engine/dist/web/assets/GBK2K-H-Cv55KPk8.bcmap +0 -0
- package/engine/dist/web/assets/GBK2K-V-VEqjiYVJ.bcmap +0 -0
- package/engine/dist/web/assets/GBKp-EUC-H-CElrnV5g.bcmap +0 -0
- package/engine/dist/web/assets/GBKp-EUC-V-FKuJrjD8.bcmap +0 -0
- package/engine/dist/web/assets/GBT-EUC-H-BTsQIL1M.bcmap +0 -0
- package/engine/dist/web/assets/GBT-EUC-V-CCaONgYB.bcmap +0 -0
- package/engine/dist/web/assets/GBT-H-DL9h-WkS.bcmap +0 -0
- package/engine/dist/web/assets/GBT-V-DVsZ47Yj.bcmap +0 -0
- package/engine/dist/web/assets/GBTpc-EUC-H-DnIh5WJ3.bcmap +0 -0
- package/engine/dist/web/assets/GBTpc-EUC-V-D_dpKF5g.bcmap +0 -0
- package/engine/dist/web/assets/GBpc-EUC-H-4mXfmDQd.bcmap +0 -0
- package/engine/dist/web/assets/GBpc-EUC-V-BNBtOgzw.bcmap +0 -0
- package/engine/dist/web/assets/H-BJYV6_el.bcmap +0 -0
- package/engine/dist/web/assets/HKdla-B5-H-Bg4tZIUs.bcmap +0 -0
- package/engine/dist/web/assets/HKdla-B5-V-CHovDFKh.bcmap +0 -0
- package/engine/dist/web/assets/HKdlb-B5-H-Bf7o2mkD.bcmap +0 -0
- package/engine/dist/web/assets/HKdlb-B5-V-CDfiFKye.bcmap +0 -0
- package/engine/dist/web/assets/HKgccs-B5-H-BSg_4Hb2.bcmap +0 -0
- package/engine/dist/web/assets/HKgccs-B5-V-Bb0FD7uo.bcmap +0 -0
- package/engine/dist/web/assets/HKm314-B5-H-CUeI2x46.bcmap +0 -0
- package/engine/dist/web/assets/HKm314-B5-V-BrK5Z_J-.bcmap +0 -0
- package/engine/dist/web/assets/HKm471-B5-H-B_3IWC_G.bcmap +0 -0
- package/engine/dist/web/assets/HKm471-B5-V-B7Ks-vkf.bcmap +0 -0
- package/engine/dist/web/assets/HKscs-B5-H-CXbBbq6g.bcmap +0 -0
- package/engine/dist/web/assets/HKscs-B5-V-CrljkNDo.bcmap +0 -0
- package/engine/dist/web/assets/Hankaku-SMDjWZXJ.bcmap +0 -0
- package/engine/dist/web/assets/Hiragana-Dy6yEHKG.bcmap +0 -0
- package/engine/dist/web/assets/KSC-EUC-H-CYoqfsaA.bcmap +0 -0
- package/engine/dist/web/assets/KSC-EUC-V-C8-Y2jkl.bcmap +0 -0
- package/engine/dist/web/assets/KSC-H-CUtFgdXl.bcmap +0 -0
- package/engine/dist/web/assets/KSC-Johab-H-CGtV3MPT.bcmap +0 -0
- package/engine/dist/web/assets/KSC-Johab-V-CWzqnmVV.bcmap +0 -0
- package/engine/dist/web/assets/KSC-V-CDXb0xyy.bcmap +0 -0
- package/engine/dist/web/assets/KSCms-UHC-H-i6eA63f9.bcmap +0 -0
- package/engine/dist/web/assets/KSCms-UHC-HW-H-lQjjyo98.bcmap +0 -0
- package/engine/dist/web/assets/KSCms-UHC-HW-V-DKemUgQi.bcmap +0 -0
- package/engine/dist/web/assets/KSCms-UHC-V-DaEePsjg.bcmap +0 -0
- package/engine/dist/web/assets/KSCpc-EUC-H-BxNLD7US.bcmap +0 -0
- package/engine/dist/web/assets/KSCpc-EUC-V-Bd6pD5ID.bcmap +0 -0
- package/engine/dist/web/assets/Katakana-DueN_6wN.bcmap +0 -0
- package/engine/dist/web/assets/NWP-H-BSh0nlh5.bcmap +0 -0
- package/engine/dist/web/assets/NWP-V-DvbaGBQ0.bcmap +0 -0
- package/engine/dist/web/assets/RKSJ-H-CotmKIAY.bcmap +0 -0
- package/engine/dist/web/assets/RKSJ-V-CVQ5ujKs.bcmap +0 -0
- package/engine/dist/web/assets/Roman-CGyUGrKi.bcmap +0 -0
- package/engine/dist/web/assets/UniCNS-UCS2-H-DWzv8UhH.bcmap +0 -0
- package/engine/dist/web/assets/UniCNS-UCS2-V-CbsnO1ia.bcmap +0 -0
- package/engine/dist/web/assets/UniCNS-UTF16-H-Bw0JKgCq.bcmap +0 -0
- package/engine/dist/web/assets/UniCNS-UTF16-V-DoPO_42D.bcmap +0 -0
- package/engine/dist/web/assets/UniCNS-UTF32-H-IE_GrH2i.bcmap +0 -0
- package/engine/dist/web/assets/UniCNS-UTF32-V-DpuHKqNE.bcmap +0 -0
- package/engine/dist/web/assets/UniCNS-UTF8-H-BbLcFh6Q.bcmap +0 -0
- package/engine/dist/web/assets/UniCNS-UTF8-V-BD4cmnNX.bcmap +0 -0
- package/engine/dist/web/assets/UniGB-UCS2-H-D7xzRvZN.bcmap +0 -0
- package/engine/dist/web/assets/UniGB-UCS2-V-COOBrDBb.bcmap +0 -0
- package/engine/dist/web/assets/UniGB-UTF16-H-DKjbEwc0.bcmap +0 -0
- package/engine/dist/web/assets/UniGB-UTF16-V-E1RoE6KW.bcmap +0 -0
- package/engine/dist/web/assets/UniGB-UTF32-H-Behfouj5.bcmap +0 -0
- package/engine/dist/web/assets/UniGB-UTF32-V-BFm4myiz.bcmap +0 -0
- package/engine/dist/web/assets/UniGB-UTF8-H-EROft1ro.bcmap +0 -0
- package/engine/dist/web/assets/UniGB-UTF8-V-C8SGUjFI.bcmap +0 -0
- package/engine/dist/web/assets/UniJIS-UCS2-H-2DbZ0MaQ.bcmap +0 -0
- package/engine/dist/web/assets/UniJIS-UCS2-HW-H-BfJ3t__Z.bcmap +0 -0
- package/engine/dist/web/assets/UniJIS-UCS2-HW-V-BZyQaeYZ.bcmap +0 -0
- package/engine/dist/web/assets/UniJIS-UCS2-V-Cr3xs5m4.bcmap +0 -0
- package/engine/dist/web/assets/UniJIS-UTF16-H-BlDDPdCK.bcmap +0 -0
- package/engine/dist/web/assets/UniJIS-UTF16-V-DfK_Aqy1.bcmap +0 -0
- package/engine/dist/web/assets/UniJIS-UTF32-H-BIQPWkjv.bcmap +0 -0
- package/engine/dist/web/assets/UniJIS-UTF32-V-0rh54ED3.bcmap +0 -0
- package/engine/dist/web/assets/UniJIS-UTF8-H-CGusholx.bcmap +0 -0
- package/engine/dist/web/assets/UniJIS-UTF8-V-BGWFHinB.bcmap +0 -0
- package/engine/dist/web/assets/UniJIS2004-UTF16-H-KXixH-HI.bcmap +0 -0
- package/engine/dist/web/assets/UniJIS2004-UTF16-V-BSHgoaRy.bcmap +0 -0
- package/engine/dist/web/assets/UniJIS2004-UTF32-H-CXrD-9YE.bcmap +0 -0
- package/engine/dist/web/assets/UniJIS2004-UTF32-V-CH3k_ouF.bcmap +0 -0
- package/engine/dist/web/assets/UniJIS2004-UTF8-H-BkOctZjn.bcmap +0 -0
- package/engine/dist/web/assets/UniJIS2004-UTF8-V-D8AJhywu.bcmap +0 -0
- package/engine/dist/web/assets/UniJISPro-UCS2-HW-V-DLNkNO1x.bcmap +0 -0
- package/engine/dist/web/assets/UniJISPro-UCS2-V-BpvX7JBA.bcmap +0 -0
- package/engine/dist/web/assets/UniJISPro-UTF8-V-Db5K6XXE.bcmap +0 -0
- package/engine/dist/web/assets/UniJISX0213-UTF32-H-BavAhZjb.bcmap +0 -0
- package/engine/dist/web/assets/UniJISX0213-UTF32-V-Cz7JBDr9.bcmap +0 -0
- package/engine/dist/web/assets/UniJISX02132004-UTF32-H-DAH-AgUq.bcmap +0 -0
- package/engine/dist/web/assets/UniJISX02132004-UTF32-V-BqqDIAe5.bcmap +0 -0
- package/engine/dist/web/assets/UniKS-UCS2-H-YeUc7k1L.bcmap +0 -0
- package/engine/dist/web/assets/UniKS-UCS2-V-ahlksFWQ.bcmap +0 -0
- package/engine/dist/web/assets/UniKS-UTF16-H-B0dvB-Td.bcmap +0 -0
- package/engine/dist/web/assets/UniKS-UTF16-V-EgvK-6IG.bcmap +0 -0
- package/engine/dist/web/assets/UniKS-UTF32-H-olR_LtmR.bcmap +0 -0
- package/engine/dist/web/assets/UniKS-UTF32-V-CnOTbPsn.bcmap +0 -0
- package/engine/dist/web/assets/UniKS-UTF8-H-CbpebD-y.bcmap +0 -0
- package/engine/dist/web/assets/UniKS-UTF8-V-BIFFPWWm.bcmap +0 -0
- package/engine/dist/web/assets/V-BJkwLeFB.bcmap +0 -0
- package/engine/dist/web/assets/WP-Symbol-C3zzKJnO.bcmap +0 -0
- package/engine/dist/web/assets/{index-DGkq8xQx.css → index-CkL-mf_b.css} +1 -1
- package/engine/dist/web/assets/{index-DBYJK8Uy.js → index-e1oe-qOQ.js} +4 -4
- package/engine/dist/web/index.html +2 -2
- package/engine/src/web/FullPaper.tsx +14 -5
- package/engine/src/web/pdf-cmaps.ts +20 -0
- package/engine/src/web/review.tsx +31 -9
- package/engine/src/web/style.css +100 -28
- package/package.json +1 -1
- package/src/server/app.ts +4 -8
- package/src/server/manuscript.ts +38 -20
- package/src/server/store.ts +26 -1
- package/src/server/workspace.ts +15 -10
- package/dist/assets/index-BIc9Mnim.css +0 -1
- package/dist/assets/index-MvQvvv3I.js +0 -267
- package/dist/assets/pdf-view-ByftRBhD.js +0 -598
- package/engine/dist/web/assets/FullPaper-DWwiT43p.js +0 -54
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
rel="icon"
|
|
10
10
|
href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Crect width='16' height='16' rx='3' fill='%232b6e5c'/%3E%3Cpath d='M4 8.5l2.5 2.5L12 5' stroke='white' stroke-width='2' fill='none'/%3E%3C/svg%3E"
|
|
11
11
|
/>
|
|
12
|
-
<script type="module" crossorigin src="/assets/index-
|
|
13
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
12
|
+
<script type="module" crossorigin src="/assets/index-e1oe-qOQ.js"></script>
|
|
13
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CkL-mf_b.css">
|
|
14
14
|
</head>
|
|
15
15
|
<body>
|
|
16
16
|
<div id="root"></div>
|
|
@@ -14,12 +14,21 @@ import worker from "pdfjs-dist/build/pdf.worker.min.mjs?url";
|
|
|
14
14
|
import type { ChangeBlock, Strip } from "../types.ts";
|
|
15
15
|
import { t } from "./i18n.ts";
|
|
16
16
|
import type { PlacedChange } from "./paper.ts";
|
|
17
|
+
import { cMapData } from "./pdf-cmaps.ts";
|
|
17
18
|
|
|
18
19
|
// Built assets share one folder, and a host such as Papilot serves the viewer
|
|
19
|
-
// under its own prefix: find the worker next to this chunk, not at /assets.
|
|
20
|
-
|
|
21
|
-
?
|
|
22
|
-
|
|
20
|
+
// under its own prefix: find the worker and CMaps next to this chunk, not at /assets.
|
|
21
|
+
const besideChunk = (url: string) =>
|
|
22
|
+
import.meta.env.DEV ? url : new URL(url.split("/").pop()!, import.meta.url).href;
|
|
23
|
+
pdfjs.GlobalWorkerOptions.workerSrc = besideChunk(worker);
|
|
24
|
+
const CMapData = cMapData(
|
|
25
|
+
import.meta.glob<string>("../../node_modules/pdfjs-dist/cmaps/*.bcmap", {
|
|
26
|
+
eager: true,
|
|
27
|
+
import: "default",
|
|
28
|
+
query: "?url&no-inline",
|
|
29
|
+
}),
|
|
30
|
+
besideChunk,
|
|
31
|
+
);
|
|
23
32
|
|
|
24
33
|
// The whole paper with its changes in place: the new PDF's pages, opened up
|
|
25
34
|
// where a change's new lines are, and that change shown there as in the
|
|
@@ -134,7 +143,7 @@ export function FullPaper({
|
|
|
134
143
|
setStarted(Date.now());
|
|
135
144
|
announced.current = false;
|
|
136
145
|
const open = async (source: string, partial: boolean) => {
|
|
137
|
-
const task = pdfjs.getDocument({ url: source, enableXfa: false });
|
|
146
|
+
const task = pdfjs.getDocument({ url: source, enableXfa: false, BinaryDataFactory: CMapData });
|
|
138
147
|
tasks.push(task);
|
|
139
148
|
const pdf = await task.promise,
|
|
140
149
|
first = (await pdf.getPage(1)).getViewport({ scale: 1 }),
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// pdf.js draws CJK text from fonts without a ToUnicode map, such as xeCJK's
|
|
2
|
+
// default Fandol fonts, through Adobe's predefined CMaps; without them those
|
|
3
|
+
// characters stay blank. Each bundle ships pdf.js's packed CMaps as assets and
|
|
4
|
+
// passes their URLs here. pdf.js then asks for one file at a time by name.
|
|
5
|
+
export function cMapData(urls: Record<string, string>, resolve = (url: string) => url) {
|
|
6
|
+
const files = new Map(
|
|
7
|
+
Object.entries(urls).map(([path, url]) => [path.slice(path.lastIndexOf("/") + 1), url]),
|
|
8
|
+
);
|
|
9
|
+
return class CMapData {
|
|
10
|
+
async fetch({ kind, filename }: { kind: string; filename: string }) {
|
|
11
|
+
// Standard fonts and wasm keep pdf.js's behaviour without their URLs.
|
|
12
|
+
if (kind !== "cMapUrl") throw new Error(`Ensure that the \`${kind}\` API parameter is provided.`);
|
|
13
|
+
const url = files.get(filename);
|
|
14
|
+
if (!url) throw new Error(`Unknown CMap: ${filename}`);
|
|
15
|
+
const response = await fetch(resolve(url));
|
|
16
|
+
if (!response.ok) throw new Error(`Unable to load CMap data at: ${resolve(url)}`);
|
|
17
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -101,6 +101,12 @@ function store(key: string, value: unknown) {
|
|
|
101
101
|
/* Storage is optional. */
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
|
+
// The comment box grows with its text up to the height its style allows.
|
|
105
|
+
function fitComposer(element: HTMLTextAreaElement | null) {
|
|
106
|
+
if (!element) return;
|
|
107
|
+
element.style.height = "0";
|
|
108
|
+
element.style.height = `${element.scrollHeight}px`;
|
|
109
|
+
}
|
|
104
110
|
function jobText(job: PageState["job"]) {
|
|
105
111
|
return job?.status === "queued"
|
|
106
112
|
? job.ahead
|
|
@@ -1671,33 +1677,49 @@ export function ReviewApp({
|
|
|
1671
1677
|
void submitComposer();
|
|
1672
1678
|
}}
|
|
1673
1679
|
>
|
|
1674
|
-
<
|
|
1675
|
-
|
|
1676
|
-
|
|
1680
|
+
<div className="composer-head">
|
|
1681
|
+
<label htmlFor="composer-text">
|
|
1682
|
+
{t("Comment on change {n}", { n: composer.stop + 1 })}
|
|
1683
|
+
</label>
|
|
1684
|
+
<button
|
|
1685
|
+
type="button"
|
|
1686
|
+
className="composer-close"
|
|
1687
|
+
aria-label={t("Cancel")}
|
|
1688
|
+
title={t("Cancel")}
|
|
1689
|
+
onClick={() => closeComposer()}
|
|
1690
|
+
>
|
|
1691
|
+
<svg viewBox="0 0 24 24" aria-hidden="true">
|
|
1692
|
+
<path d="M18 6 6 18M6 6l12 12" />
|
|
1693
|
+
</svg>
|
|
1694
|
+
</button>
|
|
1695
|
+
</div>
|
|
1677
1696
|
<textarea
|
|
1678
1697
|
id="composer-text"
|
|
1698
|
+
ref={fitComposer}
|
|
1679
1699
|
autoFocus
|
|
1680
|
-
rows={
|
|
1700
|
+
rows={3}
|
|
1681
1701
|
maxLength={20000}
|
|
1682
1702
|
value={draft}
|
|
1683
1703
|
onChange={(e) => {
|
|
1684
1704
|
setDraft(e.target.value);
|
|
1685
1705
|
store(composer.key, e.target.value);
|
|
1706
|
+
fitComposer(e.target);
|
|
1686
1707
|
}}
|
|
1687
1708
|
placeholder={t("What should the agent change here?")}
|
|
1688
1709
|
/>
|
|
1689
1710
|
<div className="composer-row">
|
|
1690
1711
|
<span className="muted">{t("⌘↵ send · Esc cancel")}</span>
|
|
1691
|
-
<button type="button" onClick={() => closeComposer()}>
|
|
1692
|
-
{t("Cancel")}
|
|
1693
|
-
</button>
|
|
1694
1712
|
<button
|
|
1695
1713
|
type="submit"
|
|
1696
|
-
|
|
1714
|
+
className="composer-send"
|
|
1715
|
+
aria-label={t("Send")}
|
|
1716
|
+
title={state?.commentLock ? t(state.commentLock) : t("Send")}
|
|
1697
1717
|
aria-describedby={state?.commentLock ? "review-lock" : undefined}
|
|
1698
1718
|
disabled={busy || !draft.trim() || Boolean(state?.commentLock)}
|
|
1699
1719
|
>
|
|
1700
|
-
|
|
1720
|
+
<svg viewBox="0 0 24 24" aria-hidden="true">
|
|
1721
|
+
<path d="M12 19V5m-7 7 7-7 7 7" />
|
|
1722
|
+
</svg>
|
|
1701
1723
|
</button>
|
|
1702
1724
|
</div>
|
|
1703
1725
|
</form>
|
package/engine/src/web/style.css
CHANGED
|
@@ -1274,57 +1274,129 @@ kbd {
|
|
|
1274
1274
|
width: min(380px, calc(100vw - 24px));
|
|
1275
1275
|
}
|
|
1276
1276
|
|
|
1277
|
+
/* The comment box: a title with a close button, a borderless field that
|
|
1278
|
+
grows with its text, and a round send button, like Papilot's own composer. */
|
|
1277
1279
|
.composer {
|
|
1278
1280
|
position: fixed;
|
|
1279
1281
|
left: 50%;
|
|
1280
1282
|
bottom: 84px;
|
|
1281
1283
|
transform: translateX(-50%);
|
|
1282
1284
|
z-index: 5;
|
|
1283
|
-
width: min(
|
|
1284
|
-
padding: 12px;
|
|
1285
|
+
width: min(560px, calc(100vw - 24px));
|
|
1285
1286
|
background: var(--bar);
|
|
1286
1287
|
border: 1px solid var(--line);
|
|
1287
|
-
border-radius:
|
|
1288
|
-
box-shadow:
|
|
1288
|
+
border-radius: 14px;
|
|
1289
|
+
box-shadow:
|
|
1290
|
+
0 2px 4px rgb(0 0 0 / 0.04),
|
|
1291
|
+
0 14px 36px rgb(0 0 0 / 0.13);
|
|
1289
1292
|
}
|
|
1290
|
-
.composer
|
|
1291
|
-
display:
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1293
|
+
.composer-head {
|
|
1294
|
+
display: flex;
|
|
1295
|
+
align-items: center;
|
|
1296
|
+
gap: 8px;
|
|
1297
|
+
padding: 8px 8px 0 16px;
|
|
1298
|
+
}
|
|
1299
|
+
.composer-head label {
|
|
1300
|
+
flex: 1;
|
|
1301
|
+
min-width: 0;
|
|
1302
|
+
overflow: hidden;
|
|
1303
|
+
font-size: 13px;
|
|
1304
|
+
font-weight: 600;
|
|
1305
|
+
color: var(--muted);
|
|
1306
|
+
text-overflow: ellipsis;
|
|
1307
|
+
white-space: nowrap;
|
|
1296
1308
|
}
|
|
1297
1309
|
.composer textarea {
|
|
1310
|
+
display: block;
|
|
1298
1311
|
width: 100%;
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
border
|
|
1304
|
-
|
|
1305
|
-
|
|
1312
|
+
min-height: 70px;
|
|
1313
|
+
max-height: min(280px, 40dvh);
|
|
1314
|
+
margin: 0;
|
|
1315
|
+
padding: 4px 16px;
|
|
1316
|
+
border: 0;
|
|
1317
|
+
outline: none;
|
|
1318
|
+
resize: none;
|
|
1319
|
+
overflow-y: auto;
|
|
1320
|
+
background: transparent;
|
|
1321
|
+
color: var(--ink);
|
|
1322
|
+
font-size: 15px;
|
|
1323
|
+
line-height: 1.55;
|
|
1306
1324
|
}
|
|
1307
|
-
.composer textarea
|
|
1308
|
-
|
|
1309
|
-
border-color: transparent;
|
|
1325
|
+
.composer textarea::placeholder {
|
|
1326
|
+
color: var(--muted);
|
|
1310
1327
|
}
|
|
1311
1328
|
.composer-row {
|
|
1312
1329
|
display: flex;
|
|
1313
1330
|
align-items: center;
|
|
1314
1331
|
justify-content: space-between;
|
|
1315
|
-
|
|
1332
|
+
gap: 8px;
|
|
1333
|
+
padding: 6px 8px 8px 16px;
|
|
1316
1334
|
font-size: 12px;
|
|
1317
1335
|
}
|
|
1318
|
-
.composer-row
|
|
1319
|
-
|
|
1336
|
+
.composer-row .muted {
|
|
1337
|
+
min-width: 0;
|
|
1320
1338
|
}
|
|
1321
|
-
.composer
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1339
|
+
.composer-close,
|
|
1340
|
+
.composer-send {
|
|
1341
|
+
display: inline-flex;
|
|
1342
|
+
align-items: center;
|
|
1343
|
+
justify-content: center;
|
|
1344
|
+
flex: none;
|
|
1345
|
+
padding: 0;
|
|
1346
|
+
border: 0;
|
|
1347
|
+
}
|
|
1348
|
+
.composer svg {
|
|
1349
|
+
width: 16px;
|
|
1350
|
+
height: 16px;
|
|
1351
|
+
fill: none;
|
|
1352
|
+
stroke: currentColor;
|
|
1353
|
+
stroke-width: 2;
|
|
1354
|
+
stroke-linecap: round;
|
|
1355
|
+
stroke-linejoin: round;
|
|
1356
|
+
}
|
|
1357
|
+
.composer-close {
|
|
1358
|
+
width: 28px;
|
|
1359
|
+
height: 28px;
|
|
1360
|
+
border-radius: 8px;
|
|
1361
|
+
background: transparent;
|
|
1362
|
+
color: var(--muted);
|
|
1363
|
+
transition:
|
|
1364
|
+
background-color 0.15s,
|
|
1365
|
+
color 0.15s;
|
|
1366
|
+
}
|
|
1367
|
+
.composer-close svg {
|
|
1368
|
+
width: 14px;
|
|
1369
|
+
height: 14px;
|
|
1370
|
+
}
|
|
1371
|
+
.composer-close:hover {
|
|
1372
|
+
background: color-mix(in srgb, var(--ink) 7%, transparent);
|
|
1326
1373
|
color: var(--ink);
|
|
1327
1374
|
}
|
|
1375
|
+
.composer-send {
|
|
1376
|
+
width: 32px;
|
|
1377
|
+
height: 32px;
|
|
1378
|
+
border-radius: 50%;
|
|
1379
|
+
background: var(--accent);
|
|
1380
|
+
color: #fff;
|
|
1381
|
+
transition:
|
|
1382
|
+
background-color 0.15s,
|
|
1383
|
+
transform 0.1s;
|
|
1384
|
+
}
|
|
1385
|
+
.composer-send svg {
|
|
1386
|
+
width: 18px;
|
|
1387
|
+
height: 18px;
|
|
1388
|
+
}
|
|
1389
|
+
.composer-send:hover:not(:disabled) {
|
|
1390
|
+
background: color-mix(in srgb, var(--accent) 85%, #000);
|
|
1391
|
+
}
|
|
1392
|
+
.composer-send:active:not(:disabled) {
|
|
1393
|
+
transform: scale(0.94);
|
|
1394
|
+
}
|
|
1395
|
+
.composer-send:disabled {
|
|
1396
|
+
opacity: 1;
|
|
1397
|
+
background: color-mix(in srgb, var(--ink) 9%, transparent);
|
|
1398
|
+
color: var(--muted);
|
|
1399
|
+
}
|
|
1328
1400
|
|
|
1329
1401
|
.toast {
|
|
1330
1402
|
position: fixed;
|
package/package.json
CHANGED
package/src/server/app.ts
CHANGED
|
@@ -245,15 +245,11 @@ export async function buildApp(options: AppOptions) {
|
|
|
245
245
|
// A skill submission uses the same task endpoint and retry identity as editing.
|
|
246
246
|
const existing = store.tasks().find(task => task.id === id);
|
|
247
247
|
if (existing) { if (existing.projectId !== projectId) throw new Error('Invalid task ID.'); return existing; }
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
if (!state.build.current || !state.build.key) throw new Error('Wait for the current PDF to finish compiling, then run the AI review skill.');
|
|
252
|
-
reviewInput = await manuscripts.reviewSnapshot(projectId, state.build.key);
|
|
253
|
-
}
|
|
254
|
-
return workspace.createTask({ id, projectId, reviewInput, ...(reviewInput ? { prepareReview: (worktree: string) => manuscripts.captureReview(store.project(projectId).path, worktree, id) } : {}), title: string(b.title, 'Task title', 120), instructions: skill || annotations?.length || attachments.length ? optional(b.instructions, 'Instructions') : string(b.instructions, 'Instructions'), skill, annotations, groupInstructions, attachments, agent: agent(b.agent), model: typeof b.model === 'string' ? b.model : undefined, effort: effort(b.effort), serviceTier: b.serviceTier === undefined ? undefined : string(b.serviceTier, 'Service tier', 64), author: author(request.viewer) });
|
|
248
|
+
// AI review compiles the task's own copy of the manuscript, so it does not wait for the preview.
|
|
249
|
+
const reviewing = Boolean(skill && findSkill(store.project(projectId).path, skill).output === 'annotations');
|
|
250
|
+
return workspace.createTask({ id, projectId, ...(reviewing ? { prepareReview: (worktree: string) => manuscripts.captureReview(store.project(projectId).path, worktree, id) } : {}), title: string(b.title, 'Task title', 120), instructions: skill || annotations?.length || attachments.length ? optional(b.instructions, 'Instructions') : string(b.instructions, 'Instructions'), skill, annotations, groupInstructions, attachments, agent: agent(b.agent), model: typeof b.model === 'string' ? b.model : undefined, effort: effort(b.effort), serviceTier: b.serviceTier === undefined ? undefined : string(b.serviceTier, 'Service tier', 64), author: author(request.viewer) });
|
|
255
251
|
});
|
|
256
|
-
app.get<{ Params: { id: string } }>('/api/tasks/:id/items', task, request => { store.task(request.params.id); return
|
|
252
|
+
app.get<{ Params: { id: string }; Querystring: { since?: string } }>('/api/tasks/:id/items', task, request => { store.task(request.params.id); return store.itemsSince(request.params.id, typeof request.query.since === 'string' ? request.query.since : undefined); });
|
|
257
253
|
app.get<{ Params: { id: string } }>('/api/tasks/:id/note-results', task, async request => {
|
|
258
254
|
const task = store.task(request.params.id), client = await connectReview(store.project(task.projectId).path);
|
|
259
255
|
return { notes: await taskNoteResults(task, store.items(task.id), store.runs().filter(run => run.taskId === task.id), client) };
|
package/src/server/manuscript.ts
CHANGED
|
@@ -79,6 +79,19 @@ function gitDir(root: string) {
|
|
|
79
79
|
return fs.statSync(dot).isFile() ? path.resolve(root, readText(dot).replace(/^gitdir:\s*/, '').trim()) : dot;
|
|
80
80
|
} catch { return null; }
|
|
81
81
|
}
|
|
82
|
+
// Cheap evidence that Git's own state moved: the index, HEAD, the current
|
|
83
|
+
// branch, a fetch or a merge. Each changes when a commit, stage, checkout,
|
|
84
|
+
// fetch or merge happens, from Papilot or from a terminal.
|
|
85
|
+
export function gitStamp(root: string) {
|
|
86
|
+
const dir = gitDir(root);
|
|
87
|
+
if (!dir) return '';
|
|
88
|
+
const common = path.resolve(dir, readText(path.join(dir, 'commondir')).trim() || '.');
|
|
89
|
+
const branch = readText(path.join(dir, 'HEAD')).match(/^ref: (refs\/\S+)/)?.[1];
|
|
90
|
+
// FETCH_HEAD belongs to the worktree that fetched, so a linked worktree checks
|
|
91
|
+
// its own and the main one's.
|
|
92
|
+
return [path.join(dir, 'index'), path.join(dir, 'HEAD'), path.join(dir, 'MERGE_HEAD'), path.join(dir, 'FETCH_HEAD'), common !== dir && path.join(common, 'FETCH_HEAD'), path.join(common, 'packed-refs'), branch && path.join(common, branch)]
|
|
93
|
+
.map(file => { try { const stat = file && fs.statSync(file); return stat ? `${stat.mtimeMs}:${stat.size}` : '-'; } catch { return '-'; } }).join('|');
|
|
94
|
+
}
|
|
82
95
|
export function mergeInProgress(root: string) {
|
|
83
96
|
const dir = gitDir(root);
|
|
84
97
|
return Boolean(dir && fs.existsSync(path.join(dir, 'MERGE_HEAD')));
|
|
@@ -298,13 +311,13 @@ async function pageSizes(mupdf: any, pdf: string) {
|
|
|
298
311
|
}
|
|
299
312
|
|
|
300
313
|
async function engine() {
|
|
301
|
-
const [{ Builder }, { Repo, gitBinary }, { loadProject }, { mupdf, PdfDocument, containsCenter, walkText }] = await Promise.all([
|
|
314
|
+
const [{ Builder, BuildError }, { Repo, gitBinary }, { loadProject }, { mupdf, PdfDocument, containsCenter, walkText }] = await Promise.all([
|
|
302
315
|
import(pathToFileURL(path.join(ENGINE, 'src/core/build.ts')).href),
|
|
303
316
|
import(pathToFileURL(path.join(ENGINE, 'src/core/git.ts')).href),
|
|
304
317
|
import(pathToFileURL(path.join(ENGINE, 'src/core/config.ts')).href),
|
|
305
318
|
import(pathToFileURL(path.join(ENGINE, 'src/render/pdf.ts')).href),
|
|
306
319
|
]);
|
|
307
|
-
return { Builder, Repo, gitBinary, loadProject, mupdf, PdfDocument, containsCenter, walkText };
|
|
320
|
+
return { Builder, BuildError, Repo, gitBinary, loadProject, mupdf, PdfDocument, containsCenter, walkText };
|
|
308
321
|
}
|
|
309
322
|
|
|
310
323
|
// `synctex edit` maps a PDF position (big points from the top-left corner) to
|
|
@@ -483,12 +496,16 @@ export class Manuscripts extends EventEmitter {
|
|
|
483
496
|
// The view polls every second and a half while a build runs; Git's status
|
|
484
497
|
// costs a walk of the working tree, which competes with TeX, so it is
|
|
485
498
|
// read again at most every few seconds then, and after Papilot's own writes.
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
499
|
+
// Between builds the view polls every five seconds to notice other editors:
|
|
500
|
+
// the status is read again when the working tree or Git's own state moved,
|
|
501
|
+
// and at least every half minute for what neither shows, such as a push
|
|
502
|
+
// from a terminal changing how far the branch is ahead.
|
|
503
|
+
private statuses = new Map<string, { at: number; value: Promise<GitStatus>; tree?: string; stamp?: string }>();
|
|
504
|
+
private gitStatus(root: string, building: boolean, tree?: string) {
|
|
505
|
+
const cached = this.statuses.get(root), age = cached ? Date.now() - cached.at : Infinity, stamp = building ? undefined : gitStamp(root);
|
|
506
|
+
if (cached && (building ? age < 5000 : tree !== undefined && cached.tree === tree && cached.stamp === stamp && age < 30000)) return cached.value;
|
|
490
507
|
const value = gitStatus(root);
|
|
491
|
-
this.statuses.set(root, { at: Date.now(), value });
|
|
508
|
+
this.statuses.set(root, { at: Date.now(), value, tree, stamp });
|
|
492
509
|
value.catch(() => { if (this.statuses.get(root)?.value === value) this.statuses.delete(root); });
|
|
493
510
|
return value;
|
|
494
511
|
}
|
|
@@ -497,7 +514,8 @@ export class Manuscripts extends EventEmitter {
|
|
|
497
514
|
const current = this.builds.get(projectId), building = current?.status === 'building';
|
|
498
515
|
// Polling an in-flight build needs its status, not another source snapshot.
|
|
499
516
|
// Once it finishes, the next read checks for edits made during compilation.
|
|
500
|
-
|
|
517
|
+
// The snapshot comes first so an unchanged tree can reuse the last status.
|
|
518
|
+
const tree = building ? current.tree : await this.snapshot(root), status = await this.gitStatus(root, building, building ? undefined : tree);
|
|
501
519
|
let build = this.builds.get(projectId);
|
|
502
520
|
if (!build || (build.status !== 'building' && (build.tree !== tree || rebuild))) build = this.start(projectId, root, tree, build, rebuild);
|
|
503
521
|
const { status: state, key, pages, builtAt, error } = build;
|
|
@@ -701,13 +719,17 @@ export class Manuscripts extends EventEmitter {
|
|
|
701
719
|
}
|
|
702
720
|
|
|
703
721
|
// Compile the task's frozen source tree, then retain its PDF outside the build cache.
|
|
722
|
+
// A preview build of the same tree is shared, not repeated.
|
|
704
723
|
async captureReview(root: string, worktree: string, taskId: string): Promise<ReviewSnapshot> {
|
|
705
|
-
const { Builder, Repo, loadProject } = await engine();
|
|
724
|
+
const { Builder, BuildError, Repo, loadProject } = await engine();
|
|
706
725
|
const tree = await workingTree(worktree);
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
726
|
+
let result;
|
|
727
|
+
try { result = await new Builder(loadProject(root), new Repo(root)).build(tree, ['full']); }
|
|
728
|
+
catch (error) { throw error instanceof BuildError ? new Error('The manuscript could not be compiled for AI review. Fix the errors shown in the manuscript preview, then try again.') : error; }
|
|
729
|
+
const key = createHash('sha256').update(fs.readFileSync(result.pdf)).digest('hex').slice(0, 16);
|
|
730
|
+
const snapshot = await this.snapshotPdf(result.pdf, key);
|
|
731
|
+
// The task does not exist yet, so a PDF kept by an earlier failed attempt is replaced.
|
|
732
|
+
return { ...snapshot, pdf: await this.preserveReviewPdf(root, taskId, result.pdf, true) };
|
|
711
733
|
}
|
|
712
734
|
|
|
713
735
|
private async reviewPdfPath(root: string, taskId: string) {
|
|
@@ -720,11 +742,11 @@ export class Manuscripts extends EventEmitter {
|
|
|
720
742
|
return fs.existsSync(pdf) ? pdf : null;
|
|
721
743
|
}
|
|
722
744
|
|
|
723
|
-
async preserveReviewPdf(root: string, taskId: string, source: string) {
|
|
745
|
+
async preserveReviewPdf(root: string, taskId: string, source: string, replace = false) {
|
|
724
746
|
const pdf = await this.reviewPdfPath(root, taskId), folder = path.dirname(pdf);
|
|
725
747
|
fs.mkdirSync(folder, { recursive: true });
|
|
726
748
|
// A retry always uses the original task copy, even after its build cache is pruned.
|
|
727
|
-
if (!fs.existsSync(pdf)) { const temporary = path.join(folder, 'review.tmp'); fs.copyFileSync(source, temporary); fs.renameSync(temporary, pdf); }
|
|
749
|
+
if (replace || !fs.existsSync(pdf)) { const temporary = path.join(folder, 'review.tmp'); fs.copyFileSync(source, temporary); fs.renameSync(temporary, pdf); }
|
|
728
750
|
return pdf;
|
|
729
751
|
}
|
|
730
752
|
|
|
@@ -762,11 +784,7 @@ export class Manuscripts extends EventEmitter {
|
|
|
762
784
|
} finally { original.close(); current.close(); }
|
|
763
785
|
}
|
|
764
786
|
|
|
765
|
-
async
|
|
766
|
-
return this.snapshotPdf(this.pdf(projectId, key), key);
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
private async snapshotPdf(pdf: string, key: string): Promise<ReviewSnapshot> {
|
|
787
|
+
async snapshotPdf(pdf: string, key: string): Promise<ReviewSnapshot> {
|
|
770
788
|
const { PdfDocument } = await engine();
|
|
771
789
|
const document = new PdfDocument(pdf), lines: ReviewSnapshot['lines'] = [];
|
|
772
790
|
let length = 0;
|
package/src/server/store.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ReviewSnapshot } from './ai-review.ts';
|
|
2
2
|
import { DatabaseSync } from 'node:sqlite';
|
|
3
3
|
import { EventEmitter } from 'node:events';
|
|
4
|
+
import { randomUUID } from 'node:crypto';
|
|
4
5
|
import fs from 'node:fs';
|
|
5
6
|
import path from 'node:path';
|
|
6
7
|
import type { Annotation, Project, Task, Run, TimelineItem } from '../shared.ts';
|
|
@@ -39,6 +40,7 @@ export class Store extends EventEmitter {
|
|
|
39
40
|
saveProject(p: Project) { this.db.prepare('INSERT INTO projects VALUES(?,?,?) ON CONFLICT(id) DO UPDATE SET path=excluded.path,data=excluded.data').run(p.id, p.path, JSON.stringify(p)); this.emit('change'); }
|
|
40
41
|
removeProject(id: string) {
|
|
41
42
|
this.project(id);
|
|
43
|
+
const tasks = (this.db.prepare('SELECT id FROM tasks WHERE project_id=?').all(id) as { id: string }[]).map(row => row.id);
|
|
42
44
|
this.transaction(() => {
|
|
43
45
|
// Only Papilot records are removed. Never finish review tasks or touch their files.
|
|
44
46
|
this.db.prepare('INSERT OR IGNORE INTO deleted_tasks SELECT id, ? FROM tasks WHERE project_id=?').run(Date.now(), id);
|
|
@@ -46,6 +48,7 @@ export class Store extends EventEmitter {
|
|
|
46
48
|
for (const table of ['tasks', 'annotations', 'members']) this.db.prepare(`DELETE FROM ${table} WHERE project_id=?`).run(id);
|
|
47
49
|
this.db.prepare('DELETE FROM projects WHERE id=?').run(id);
|
|
48
50
|
});
|
|
51
|
+
for (const task of tasks) this.itemRevisions.delete(task);
|
|
49
52
|
this.emit('change');
|
|
50
53
|
}
|
|
51
54
|
saveTask(t: Task) { t.updatedAt = Date.now(); this.db.prepare('INSERT INTO tasks VALUES(?,?,?) ON CONFLICT(id) DO UPDATE SET data=excluded.data').run(t.id, t.projectId, JSON.stringify(t)); this.emit('change'); return t; }
|
|
@@ -53,7 +56,28 @@ export class Store extends EventEmitter {
|
|
|
53
56
|
saveRun(r: Run) { this.db.prepare('INSERT INTO runs VALUES(?,?,?,?,?) ON CONFLICT(id) DO UPDATE SET status=excluded.status,data=excluded.data').run(r.id, r.taskId, r.requestKey, r.status, JSON.stringify(r)); this.emit('change'); }
|
|
54
57
|
runByKey(key: string) { const row = this.db.prepare('SELECT data FROM runs WHERE request_key=?').get(key) as { data: string } | undefined; return row ? JSON.parse(row.data) as Run : undefined; }
|
|
55
58
|
items(taskId: string) { return (this.db.prepare('SELECT data FROM items WHERE task_id=? ORDER BY created,rowid').all(taskId) as { data: string }[]).map(r => JSON.parse(r.data) as TimelineItem); }
|
|
56
|
-
|
|
59
|
+
// A running task's view reads its timeline every second, and a command's
|
|
60
|
+
// output alone can be 40 KB, so a view that has the timeline asks only for
|
|
61
|
+
// the items written since its revision. Revisions start over with the
|
|
62
|
+
// process; one from an earlier run gets the whole timeline.
|
|
63
|
+
private readonly boot = randomUUID().slice(0, 8);
|
|
64
|
+
private revision = 0;
|
|
65
|
+
private itemRevisions = new Map<string, Map<string, number>>();
|
|
66
|
+
itemsSince(taskId: string, since?: string): { items: TimelineItem[]; revision: string; partial?: true } {
|
|
67
|
+
const revision = `${this.boot}:${this.revision}`, match = /^([0-9a-f]{8}):(\d{1,15})$/.exec(since || ''), after = match ? Number(match[2]) : NaN;
|
|
68
|
+
if (!match || match[1] !== this.boot || after > this.revision) return { items: this.items(taskId), revision };
|
|
69
|
+
const changed = [...this.itemRevisions.get(taskId) || []].filter(([, at]) => at > after).map(([id]) => id);
|
|
70
|
+
if (changed.length > 500) return { items: this.items(taskId), revision };
|
|
71
|
+
const items = changed.length ? (this.db.prepare(`SELECT data FROM items WHERE task_id=? AND id IN (${changed.map(() => '?').join()}) ORDER BY created,rowid`).all(taskId, ...changed) as { data: string }[]).map(r => JSON.parse(r.data) as TimelineItem) : [];
|
|
72
|
+
return { items, revision, partial: true };
|
|
73
|
+
}
|
|
74
|
+
saveItem(item: TimelineItem) {
|
|
75
|
+
this.db.prepare('INSERT INTO items VALUES(?,?,?,?) ON CONFLICT(id) DO UPDATE SET data=excluded.data').run(item.id, item.taskId, item.createdAt, JSON.stringify(item));
|
|
76
|
+
let revisions = this.itemRevisions.get(item.taskId);
|
|
77
|
+
if (!revisions) this.itemRevisions.set(item.taskId, revisions = new Map());
|
|
78
|
+
revisions.set(item.id, ++this.revision);
|
|
79
|
+
this.emit('change');
|
|
80
|
+
}
|
|
57
81
|
saveReviewInput(taskId: string, snapshot: ReviewSnapshot) { this.db.prepare('INSERT INTO ai_review_inputs VALUES(?,?) ON CONFLICT(task_id) DO UPDATE SET data=excluded.data').run(taskId, JSON.stringify(snapshot)); }
|
|
58
82
|
reviewInput(taskId: string): ReviewSnapshot {
|
|
59
83
|
const row = this.db.prepare('SELECT data FROM ai_review_inputs WHERE task_id=?').get(taskId) as { data: string } | undefined;
|
|
@@ -81,6 +105,7 @@ export class Store extends EventEmitter {
|
|
|
81
105
|
this.db.prepare('DELETE FROM ai_review_inputs WHERE task_id=?').run(id);
|
|
82
106
|
this.db.prepare('DELETE FROM tasks WHERE id=?').run(id);
|
|
83
107
|
});
|
|
108
|
+
this.itemRevisions.delete(id);
|
|
84
109
|
this.emit('change');
|
|
85
110
|
}
|
|
86
111
|
transaction<T>(fn: () => T): T { this.db.exec('BEGIN IMMEDIATE'); try { const result = fn(); this.db.exec('COMMIT'); return result; } catch (e) { this.db.exec('ROLLBACK'); throw e; } }
|
package/src/server/workspace.ts
CHANGED
|
@@ -130,7 +130,7 @@ export class Workspace extends EventEmitter {
|
|
|
130
130
|
|| this.store.tasks().some(task => ['queued', 'running', 'waiting_input', 'building', 'uncertain', 'deleting'].includes(task.status) || task.pendingReview));
|
|
131
131
|
}
|
|
132
132
|
readonly attachments: Attachments;
|
|
133
|
-
constructor(public store: Store, public codex: AgentRuntime = new Codex(), private
|
|
133
|
+
constructor(public store: Store, public codex: AgentRuntime = new Codex(), private connectReview = reviewClient, others: Partial<Record<AgentId, AgentRuntime>> = {}) {
|
|
134
134
|
super();
|
|
135
135
|
this.attachments = new Attachments(store);
|
|
136
136
|
this.agents = { codex, claude: others.claude || new ClaudeAgent(), cursor: others.cursor || new CursorAgent(), copilot: others.copilot || new AcpAgent(ACP_PROFILES.copilot), grok: others.grok || new AcpAgent(ACP_PROFILES.grok), antigravity: others.antigravity || new AntigravityAgent(),
|
|
@@ -281,14 +281,15 @@ export class Workspace extends EventEmitter {
|
|
|
281
281
|
}
|
|
282
282
|
// A task combines the author's words, an optional skill and selected PDF notes.
|
|
283
283
|
annotationSending(id: string) { return this.preparingNotes.has(id); }
|
|
284
|
-
|
|
284
|
+
// An AI review passes prepareReview, which reads the PDF of the new task's manuscript copy.
|
|
285
|
+
async createTask(input: { prepareReview?: (worktree: string) => Promise<ReviewSnapshot>; projectId: string; title: string; instructions: string; skill?: string; annotations?: string[]; groupInstructions?: GroupInstructions; attachments?: string[]; agent?: AgentId; model?: string; effort?: string; serviceTier?: string; id: string; author?: Author }) {
|
|
285
286
|
if (!/^[\w-]{16,64}$/.test(input.id)) throw new Error('Invalid task ID.');
|
|
286
287
|
if (this.store.taskDeleted(input.id)) throw new Error('This task was deleted. Create a new task instead.');
|
|
287
288
|
// A retried request returns its task; an ID from another paper is refused.
|
|
288
289
|
const existing = this.store.tasks().find(t => t.id === input.id);
|
|
289
|
-
if (existing) { if (existing.projectId !== input.projectId || (existing.mode === 'review') !== Boolean(input.
|
|
290
|
-
if (input.
|
|
291
|
-
if (input.
|
|
290
|
+
if (existing) { if (existing.projectId !== input.projectId || (existing.mode === 'review') !== Boolean(input.prepareReview)) throw new Error('Invalid task ID.'); return existing; }
|
|
291
|
+
if (input.prepareReview && !REVIEW_AGENTS.includes(input.agent || 'codex')) throw new Error('Choose Codex, Claude or GLM for AI review.');
|
|
292
|
+
if (input.prepareReview && (input.annotations?.length || input.attachments?.length)) throw new Error('AI review uses the manuscript PDF. Start a separate editing task to send notes or attachments.');
|
|
292
293
|
if (input.serviceTier !== undefined) {
|
|
293
294
|
if (input.agent && input.agent !== 'codex') throw new Error('Service tiers are only available for Codex.');
|
|
294
295
|
const models = this.runtimes.codex.models;
|
|
@@ -301,7 +302,7 @@ export class Workspace extends EventEmitter {
|
|
|
301
302
|
let releaseAttachments = () => {};
|
|
302
303
|
try {
|
|
303
304
|
const project = this.store.project(input.projectId), skill = input.skill ? findSkill(project.path, input.skill) : undefined;
|
|
304
|
-
if (skill?.output === 'annotations' && !input.
|
|
305
|
+
if (skill?.output === 'annotations' && !input.prepareReview) throw new Error('Start a new task to run the AI review skill on the current PDF.');
|
|
305
306
|
const files = this.attachments.resolve(project.id, input.attachments || [], input.author?.id || 'owner', input.id);
|
|
306
307
|
releaseAttachments = this.attachments.reserve(project.id, input.attachments || [], input.author?.id || 'owner', input.id);
|
|
307
308
|
const available = new Map(this.store.annotations(project.id).filter(note => !note.taskId && !isTaskReviewNote(note)).map(note => [note.id, note]));
|
|
@@ -316,12 +317,17 @@ export class Workspace extends EventEmitter {
|
|
|
316
317
|
if (Object.keys(groupInstructions).some(group => !selectedGroups.has(group as keyof GroupInstructions))) throw new Error('Group instructions must refer to selected PDF notes.');
|
|
317
318
|
if (groupsWithoutInstructions(notes, groupInstructions).length && !input.instructions.trim() && !skill) throw new Error('Describe what to do with each selected color group.');
|
|
318
319
|
claimed = notes.map(note => note.id); claimed.forEach(id => this.preparingNotes.add(id));
|
|
319
|
-
if (!input.
|
|
320
|
+
if (!input.prepareReview && !skill && !notes.length && !files.length && !input.instructions.trim()) throw new Error('Describe the revision, choose a skill, or add a PDF note.');
|
|
320
321
|
const instructions = notes.length ? notesInstructions(notes, input.instructions, groupInstructions) : input.instructions;
|
|
321
322
|
const client = await this.connectReview(project.path);
|
|
322
323
|
const name = 'p-' + input.id.toLowerCase().replace(/[^a-z0-9-]/g, '').slice(0, 48);
|
|
323
324
|
const info = await client.post('/api/tasks', { name, title: input.title, deferred: true });
|
|
324
|
-
|
|
325
|
+
let reviewInput: ReviewSnapshot | undefined;
|
|
326
|
+
if (input.prepareReview) try { reviewInput = await input.prepareReview(info.worktree); } catch (error) {
|
|
327
|
+
// Discard the copy, so a retry copies the manuscript again with the author's fixes.
|
|
328
|
+
await client.post(`${taskRoute(name)}/finish`, { force: true }).catch(() => undefined);
|
|
329
|
+
throw error;
|
|
330
|
+
}
|
|
325
331
|
const task: Task = { ...(reviewInput ? { mode: 'review' as const, aiReview: { key: reviewInput.key } } : {}), id: input.id, projectId: project.id, name, title: input.title, titleSource: 'prompt', instructions: skill ? skillPrompt(skill, instructions) : instructions,
|
|
326
332
|
worktree: info.worktree, deferred: info.deferred === true, threadId: null, turnId: null, agent: input.agent || 'codex', model: input.model || null, effort: input.effort || null, ...(input.serviceTier ? { serviceTier: input.serviceTier } : {}), status: 'idle',
|
|
327
333
|
createdAt: Date.now(), updatedAt: Date.now(), feedbackCursor: 0, error: null, review: null };
|
|
@@ -329,7 +335,7 @@ export class Workspace extends EventEmitter {
|
|
|
329
335
|
this.store.saveTask(task);
|
|
330
336
|
if (reviewInput) this.store.saveReviewInput(task.id, reviewInput);
|
|
331
337
|
});
|
|
332
|
-
await this.enqueue(task.id, input.instructions || (input.
|
|
338
|
+
await this.enqueue(task.id, input.instructions || (input.prepareReview && !skill ? 'Review this manuscript and suggest improvements.' : ''), 'initial-' + task.id, false, { skill, notes, groupInstructions, attachments: files.map(file => file.id), author: input.author });
|
|
333
339
|
// Naming is independent of the editing turn. An unavailable helper must
|
|
334
340
|
// not fail creation or prevent the queued task from running.
|
|
335
341
|
try { this.requestTaskTitle(task.id); } catch { /* Keep the prompt-derived title. */ }
|
|
@@ -407,7 +413,6 @@ export class Workspace extends EventEmitter {
|
|
|
407
413
|
private async drain() {
|
|
408
414
|
if (this.closing) return;
|
|
409
415
|
for (const run of this.store.runs().filter(r => r.status === 'queued')) {
|
|
410
|
-
if (this.active.size >= this.concurrency) break;
|
|
411
416
|
const agent = this.agentOf(this.store.task(run.taskId));
|
|
412
417
|
if (!this.agents[agent].ready || !this.runtimes[agent].authenticated) continue;
|
|
413
418
|
if (this.active.has(run.taskId) || this.reviewWrites.has(run.taskId) || this.publishing.has(run.taskId) || this.store.task(run.taskId).delivery?.pending || BUSY.has(this.store.task(run.taskId).status)) continue;
|