@jackuait/blok 0.16.1 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/blok.cjs +1 -1
- package/dist/blok.iife.js +4 -4
- package/dist/blok.mjs +2 -2
- package/dist/blok.umd.js +4 -4
- package/dist/chunks/{blok-DEVVs3xk.cjs → blok-BPd65xOj.cjs} +11 -11
- package/dist/chunks/{blok-D1e9swtF.mjs → blok-D4ApwnD7.mjs} +776 -780
- package/dist/chunks/constants-C3wBsvmH.cjs +606 -0
- package/dist/chunks/{constants-DfGAwzgk.mjs → constants-DObdf8NU.mjs} +274 -274
- package/dist/chunks/{i18next-loader-BH_a6G9t.mjs → i18next-loader-BMhspWFn.mjs} +1 -1
- package/dist/chunks/{i18next-loader-BID4jAFG.cjs → i18next-loader-CNLosgId.cjs} +1 -1
- package/dist/chunks/{lightweight-i18n-Clk37huC.cjs → lightweight-i18n-ByswFbdt.cjs} +1 -1
- package/dist/chunks/{lightweight-i18n-B0VO8Rft.mjs → lightweight-i18n-Ck1HlVxq.mjs} +32 -0
- package/dist/chunks/{messages-BGoPxZ5n.mjs → messages--uPFKcl3.mjs} +33 -1
- package/dist/{messages-BmKhZE7I.mjs → chunks/messages-0ZOxg81Y.mjs} +33 -1
- package/dist/chunks/{messages-BxhJFFrR.mjs → messages-1H2SY5H1.mjs} +33 -1
- package/dist/chunks/{messages-px_8gpHT.cjs → messages-1bAMgAsO.cjs} +1 -1
- package/dist/chunks/{messages-CO_hj1x6.cjs → messages-3B44ZmLZ.cjs} +1 -1
- package/dist/chunks/{messages-5tb-mSXS.cjs → messages-3iZ6jJ6e.cjs} +1 -1
- package/dist/chunks/{messages-BVuodJIJ.cjs → messages-5Rp1xyL4.cjs} +1 -1
- package/dist/chunks/{messages-CgGwGZs22.cjs → messages-5Vbzn-DL2.cjs} +1 -1
- package/dist/chunks/{messages-De0Fkl0K.mjs → messages-61uWTg1y.mjs} +33 -1
- package/dist/chunks/messages-6lqZ4lhc2.cjs +1 -0
- package/dist/chunks/messages-795D2ldP2.cjs +1 -0
- package/dist/chunks/{messages-CUxk6xrQ.cjs → messages-79WWudxY.cjs} +1 -1
- package/dist/chunks/{messages-B-3_M44j.cjs → messages-8uD5ctYP.cjs} +1 -1
- package/dist/{messages-Z8HMjhe9.mjs → chunks/messages-9gvi5jul.mjs} +33 -1
- package/dist/chunks/{messages-BDNdC6-X.cjs → messages-B0o3rVBB.cjs} +1 -1
- package/dist/chunks/messages-B1h0nDQW.cjs +1 -0
- package/dist/chunks/{messages-BPYQEADI.cjs → messages-B37vUkBi.cjs} +1 -1
- package/dist/{messages-BrkXHD2D2.mjs → chunks/messages-B4BvJtlu.mjs} +33 -1
- package/dist/{messages-CsHwUgSZ.mjs → chunks/messages-BChCPw99.mjs} +33 -1
- package/dist/chunks/{messages-DkINYHo4.cjs → messages-BDs_gdD_.cjs} +1 -1
- package/dist/chunks/{messages-Cz7AbEFm.cjs → messages-BE6rW2R-.cjs} +1 -1
- package/dist/{messages-DFKu1WGX2.mjs → chunks/messages-BFGgLdIA2.mjs} +33 -1
- package/dist/{messages-D1jUHOKr.mjs → chunks/messages-BFcq7wsx.mjs} +33 -1
- package/dist/{messages-BbFJ2r9p.mjs → chunks/messages-BGaAuDRS.mjs} +33 -1
- package/dist/chunks/{messages-Dml6u25s2.cjs → messages-BGoJHXUk2.cjs} +1 -1
- package/dist/chunks/{messages-DDnB1EFE2.mjs → messages-BIHeeP7X2.mjs} +33 -1
- package/dist/{messages-6RcfJ59a.mjs → chunks/messages-BIQ2VT8X2.mjs} +33 -1
- package/dist/chunks/{messages-Bnm45Rmp.mjs → messages-BK32XCpH.mjs} +33 -1
- package/dist/{messages-BkNVOuvO2.mjs → chunks/messages-BKSDnjOs.mjs} +33 -1
- package/dist/chunks/{messages-CdOX6JHb2.mjs → messages-BMNmXaC8.mjs} +33 -1
- package/dist/{messages-Cg3w36Bc.mjs → chunks/messages-BNuGVAEy.mjs} +33 -1
- package/dist/chunks/{messages-C62iC79d2.mjs → messages-BQEh67Mi2.mjs} +33 -1
- package/dist/chunks/{messages-BG1Tn41d.cjs → messages-BQOx3XGd.cjs} +1 -1
- package/dist/{messages-DaoTSrJW.mjs → chunks/messages-BQUzqXIO.mjs} +33 -1
- package/dist/chunks/{messages-DuOkV5T_.mjs → messages-BTKPcoLu.mjs} +33 -1
- package/dist/chunks/messages-BXgTHDIw.cjs +1 -0
- package/dist/chunks/{messages-BItPVbHx.cjs → messages-BXvKhyQH.cjs} +1 -1
- package/dist/chunks/{messages-98gouyMa.cjs → messages-BYW-U0iT.cjs} +1 -1
- package/dist/chunks/{messages-Bs9JOJDc.cjs → messages-B_lkWkPQ.cjs} +1 -1
- package/dist/chunks/{messages-Bq7pLz_h2.mjs → messages-BaWaLwcn2.mjs} +33 -1
- package/dist/chunks/{messages-DsWTIuli2.cjs → messages-BeTQxvmX2.cjs} +1 -1
- package/dist/chunks/{messages-BSyXBvUi2.cjs → messages-BfWGa4V82.cjs} +1 -1
- package/dist/{messages-GUgmAfdB.mjs → chunks/messages-Bgzyr5Lh.mjs} +33 -1
- package/dist/chunks/{messages-UoKgx7_8.cjs → messages-BjFB8sow.cjs} +1 -1
- package/dist/chunks/messages-Bjmg_8lv2.cjs +1 -0
- package/dist/chunks/{messages-CVHSfosB.cjs → messages-BlOVJtaj.cjs} +1 -1
- package/dist/{messages-CSVEbRhT.mjs → chunks/messages-BnW_Fwro.mjs} +33 -1
- package/dist/chunks/{messages-BdjiMHzU2.cjs → messages-Bpvja9xW2.cjs} +1 -1
- package/dist/chunks/{messages-BSxgqWqe.cjs → messages-BtMQ6v_L.cjs} +1 -1
- package/dist/chunks/{messages-Cx1kdFH_2.cjs → messages-BvChjyuU2.cjs} +1 -1
- package/dist/chunks/{messages-BSstxb1m2.mjs → messages-Bvge__AJ2.mjs} +33 -1
- package/dist/chunks/{messages-BhTV7PCN.mjs → messages-BwxzN2q3.mjs} +33 -1
- package/dist/chunks/{messages-DubJ63UL.mjs → messages-Byntrl2c.mjs} +33 -1
- package/dist/{messages-BfGAGIg-2.mjs → chunks/messages-C3B8l3Hy.mjs} +33 -1
- package/dist/chunks/{messages-CZQuqbNz2.cjs → messages-C3fAHxfE2.cjs} +1 -1
- package/dist/{messages-BOEP2ly_.mjs → chunks/messages-C56BTPcC.mjs} +33 -1
- package/dist/chunks/{messages-BXmg4Rer2.mjs → messages-C6vijVZv2.mjs} +33 -1
- package/dist/{messages-zoHZbvbe.mjs → chunks/messages-C79Ef9MU.mjs} +33 -1
- package/dist/{messages-CzabNeP5.mjs → chunks/messages-C93gDUHr.mjs} +33 -1
- package/dist/chunks/messages-CAix8jdZ.cjs +1 -0
- package/dist/chunks/{messages-BHE1mg5A2.mjs → messages-CEUDK26P2.mjs} +33 -1
- package/dist/chunks/{messages-D2nTfhRa2.cjs → messages-CEZ528TO2.cjs} +1 -1
- package/dist/chunks/{messages-Ced9OY9X.mjs → messages-CHx6m_Bo.mjs} +33 -1
- package/dist/chunks/{messages-Ck6jEhXV.cjs → messages-CKiWXTe1.cjs} +1 -1
- package/dist/{messages-DfnSbN512.mjs → chunks/messages-CMFcaa9C.mjs} +33 -1
- package/dist/chunks/{messages-jAgR8EPP.cjs → messages-CO4J7Nns.cjs} +1 -1
- package/dist/chunks/messages-COTP4ZJ_2.cjs +1 -0
- package/dist/{messages-TfPmsrxh.mjs → chunks/messages-CV3U2Vsm.mjs} +33 -1
- package/dist/chunks/{messages-BClegbPD.mjs → messages-CYOersvL.mjs} +33 -1
- package/dist/chunks/{messages-B3xnntgx2.mjs → messages-C_s-wv1r2.mjs} +33 -1
- package/dist/{messages-DSmNNjPb.mjs → chunks/messages-CbdaJSfC2.mjs} +33 -1
- package/dist/chunks/{messages-CxVi9cnz.mjs → messages-Ccng4hJ2.mjs} +33 -1
- package/dist/chunks/{messages-CpJW0LBR.cjs → messages-Cgph4q9d.cjs} +1 -1
- package/dist/chunks/{messages-DNYtMplL2.cjs → messages-Cp7wFDrY2.cjs} +1 -1
- package/dist/chunks/{messages-Bttb4o0k.cjs → messages-CpZneXUI.cjs} +1 -1
- package/dist/chunks/{messages-C2-v6HdR.cjs → messages-CpbireNX.cjs} +1 -1
- package/dist/{messages-C5sc4wjR.mjs → chunks/messages-CpcZgGYg.mjs} +33 -1
- package/dist/chunks/{messages-Cd-FaIr_2.mjs → messages-CqhQuuRo2.mjs} +33 -1
- package/dist/{messages-B8BXCJBA.mjs → chunks/messages-CrFIbrXr2.mjs} +33 -1
- package/dist/chunks/{messages-DB6z9ybS.cjs → messages-CrwZs1Gq.cjs} +1 -1
- package/dist/chunks/{messages-DQb9YRoO2.mjs → messages-D0_Mxuh62.mjs} +33 -1
- package/dist/{messages-2jeEK-EB.mjs → chunks/messages-D1q601Wg.mjs} +33 -1
- package/dist/{messages-BCGyLzFB2.mjs → chunks/messages-D3kC7iDa2.mjs} +33 -1
- package/dist/chunks/{messages-DwU7qOBO2.mjs → messages-D4UnR6e52.mjs} +33 -1
- package/dist/chunks/{messages-f1NcMMTR2.cjs → messages-D5CeQW1e2.cjs} +1 -1
- package/dist/chunks/messages-D8UH8jc6.cjs +1 -0
- package/dist/chunks/{messages-BJ7clJXu2.cjs → messages-DAmOTPVW2.cjs} +1 -1
- package/dist/{messages-DPZsygzY.mjs → chunks/messages-DBPkzBZO2.mjs} +33 -1
- package/dist/chunks/{messages-o3omNKfH.cjs → messages-DEduQerF.cjs} +1 -1
- package/dist/chunks/{messages-Beh905t72.mjs → messages-DFA5OVrs2.mjs} +33 -1
- package/dist/chunks/{messages-poLdFEEs2.cjs → messages-DHah-frn.cjs} +1 -1
- package/dist/chunks/messages-DI1ZLPxM.cjs +1 -0
- package/dist/chunks/{messages-BqM6l_JM2.mjs → messages-DKJ5CX9h2.mjs} +33 -1
- package/dist/chunks/{messages-s9I-JHCa.cjs → messages-DKjhx7Y3.cjs} +1 -1
- package/dist/chunks/{messages-GXQbRrHa2.mjs → messages-DR5DyRRY2.mjs} +33 -1
- package/dist/chunks/{messages-B94JmzMj.mjs → messages-DWjHAdb3.mjs} +33 -1
- package/dist/chunks/{messages-Cn0B5LU1.mjs → messages-DYsO5SIA.mjs} +33 -1
- package/dist/chunks/{messages-M2lNlKhI2.mjs → messages-DZwozMZD2.mjs} +33 -1
- package/dist/{messages-DGwIRpOH2.mjs → chunks/messages-Db_L5RNl.mjs} +33 -1
- package/dist/chunks/{messages-Bi3snvmH2.cjs → messages-DeXUZcFn2.cjs} +1 -1
- package/dist/chunks/messages-Dgp7Wk4H.cjs +1 -0
- package/dist/chunks/{messages-Cg6qicee.cjs → messages-DhnCZQEY.cjs} +1 -1
- package/dist/chunks/{messages-Ckej-nnJ.cjs → messages-DjU1jbKf.cjs} +1 -1
- package/dist/{messages-CJsMZvUv.mjs → chunks/messages-DkqiXPS7.mjs} +33 -1
- package/dist/chunks/{messages-p5w1kuc5.cjs → messages-Dl6VC9Q9.cjs} +1 -1
- package/dist/chunks/{messages-DG8Lmqf52.cjs → messages-Dlc1DdZ22.cjs} +1 -1
- package/dist/chunks/{messages-D8qYNZ_T.mjs → messages-Dna-8bQq.mjs} +33 -1
- package/dist/chunks/{messages-DgSRzW-Y.mjs → messages-DtTgOx9a.mjs} +33 -1
- package/dist/chunks/{messages-h43rfIyt2.mjs → messages-Duj8jpve2.mjs} +33 -1
- package/dist/chunks/{messages-XM0y9kXy2.cjs → messages-DvxZjb5g2.cjs} +1 -1
- package/dist/chunks/{messages-DWeyV2N3.cjs → messages-Dz8AmvFq.cjs} +1 -1
- package/dist/chunks/messages-DzarYc1J2.cjs +1 -0
- package/dist/{messages-BXs-Zwhx.mjs → chunks/messages-FIgYAomz2.mjs} +33 -1
- package/dist/chunks/{messages-04T5j1ww2.cjs → messages-FM0KHIkQ2.cjs} +1 -1
- package/dist/chunks/{messages-BtEDKVO7.cjs → messages-JsmKjXPZ.cjs} +1 -1
- package/dist/{messages-DvSC5JGC2.mjs → chunks/messages-LW9Kd-vn.mjs} +33 -1
- package/dist/chunks/{messages-LWyC-1ol.mjs → messages-R2RH1RLt.mjs} +33 -1
- package/dist/chunks/{messages-0OyEWZDW.cjs → messages-RWVOHsZu.cjs} +1 -1
- package/dist/chunks/{messages-DwAl9zoh2.mjs → messages-X90YfYzG2.mjs} +33 -1
- package/dist/chunks/{messages-BxRX_hY12.cjs → messages-YhmR1nl02.cjs} +1 -1
- package/dist/chunks/{messages-CT025FgD2.cjs → messages-Ywb6VdfO2.cjs} +1 -1
- package/dist/{messages-qjir1Zx9.mjs → chunks/messages-ZPOc6IQC.mjs} +33 -1
- package/dist/chunks/{messages-C-N1XBvh2.cjs → messages-_ECIn1VC2.cjs} +1 -1
- package/dist/chunks/{messages-Cf5mu1AT.cjs → messages-cYjlWqco.cjs} +1 -1
- package/dist/{messages-CS6vG53A.mjs → chunks/messages-dp4NwDVM.mjs} +33 -1
- package/dist/chunks/{messages-Bz_fTG46.mjs → messages-hhT3Vii-.mjs} +33 -1
- package/dist/chunks/messages-iwzDjgrP.cjs +1 -0
- package/dist/chunks/{messages-B5eo-pGw.mjs → messages-k4Do3gkF.mjs} +33 -1
- package/dist/chunks/{messages-BfYWRC3o.cjs → messages-l0nKmGOV.cjs} +1 -1
- package/dist/chunks/{messages-BtevAuQS2.cjs → messages-l7DqwqOu2.cjs} +1 -1
- package/dist/{messages-C9XTx39w2.mjs → chunks/messages-oLqnmqJf2.mjs} +33 -1
- package/dist/chunks/{messages-Vbqmvxs1.cjs → messages-sLVwrcar.cjs} +1 -1
- package/dist/chunks/{messages-DFUn1PHK2.mjs → messages-yGQn67fw2.mjs} +33 -1
- package/dist/chunks/{messages-CXlSU8cQ2.cjs → messages-zkunMjZf2.cjs} +1 -1
- package/dist/chunks/{tools-vX3wCW3a.mjs → tools-CM6aPuR4.mjs} +3442 -2226
- package/dist/chunks/tools-kfoXLTzY.cjs +208 -0
- package/dist/chunks/webm-muxer-CC98PYSV.cjs +5 -0
- package/dist/chunks/webm-muxer-CUUz719e.mjs +864 -0
- package/dist/full.cjs +1 -1
- package/dist/full.mjs +13 -13
- package/dist/locales.mjs +99 -67
- package/dist/{messages-iJKNW8hW2.mjs → messages-1ZSVxDYN2.mjs} +33 -1
- package/dist/{chunks/messages-dZdV8VYV.mjs → messages-AK_9q2yu.mjs} +33 -1
- package/dist/{messages-eFz25q1T.mjs → messages-B0Gc0lBw.mjs} +33 -1
- package/dist/{messages-ChivLKDg2.mjs → messages-B5THTOwb2.mjs} +33 -1
- package/dist/{messages-Bd2Q-tnQ.mjs → messages-B5xy5hCa.mjs} +33 -1
- package/dist/{chunks/messages-CSc7Lh_3.mjs → messages-B6J8l1aF.mjs} +33 -1
- package/dist/{messages-tcfLP2b2.mjs → messages-B7E1zFAv.mjs} +33 -1
- package/dist/{messages-gJWgJtGo.mjs → messages-B87GXoqt.mjs} +33 -1
- package/dist/{chunks/messages-8vGF8iWb.mjs → messages-B95smO_C.mjs} +33 -1
- package/dist/{messages-Dw7P8bMa2.mjs → messages-B9h-mKVO2.mjs} +33 -1
- package/dist/{chunks/messages-D1gz17wt.mjs → messages-BBb_hXxm.mjs} +33 -1
- package/dist/{messages-kb2GSl-V.mjs → messages-BDJfFuXR.mjs} +33 -1
- package/dist/{messages-C1d7I7f_2.mjs → messages-BH5AKf_i2.mjs} +33 -1
- package/dist/{chunks/messages-CZ4Jcn6y.mjs → messages-BLslMhf-.mjs} +33 -1
- package/dist/{chunks/messages-PtDEkfSR2.mjs → messages-BPsdJnwT.mjs} +33 -1
- package/dist/{chunks/messages-BQKWm9xB.mjs → messages-BTrok9K8.mjs} +33 -1
- package/dist/{messages-B0n-WDK72.mjs → messages-BctWdAZP2.mjs} +33 -1
- package/dist/{messages-CX2ioEBc2.mjs → messages-BhGHyHKC2.mjs} +33 -1
- package/dist/{messages-CWdh59uz2.mjs → messages-BlInN8XP2.mjs} +33 -1
- package/dist/{chunks/messages-xMrks-Gg.mjs → messages-Bn19MGza.mjs} +33 -1
- package/dist/{chunks/messages-Cc56SxY7.mjs → messages-BqFRlEJE2.mjs} +33 -1
- package/dist/{messages-DMH0zRig2.mjs → messages-Bt5Sbl952.mjs} +33 -1
- package/dist/{chunks/messages-CHizzxUv.mjs → messages-Bumbqs6s.mjs} +33 -1
- package/dist/{messages-BytEaMMs.mjs → messages-BvwVDbII.mjs} +33 -1
- package/dist/{chunks/messages-DzZZDveu.mjs → messages-Bwese2OQ.mjs} +33 -1
- package/dist/{chunks/messages-Vdmc6CaN.mjs → messages-Bx5KWmyJ.mjs} +33 -1
- package/dist/{chunks/messages-BzC8TVYC.mjs → messages-ByntluqZ.mjs} +33 -1
- package/dist/{messages-C2pz0mJs2.mjs → messages-C-8BMvSp2.mjs} +33 -1
- package/dist/{messages-BInBVzW42.mjs → messages-C-ytN6Dz2.mjs} +33 -1
- package/dist/{messages-CUsdyCrN2.mjs → messages-C8QB980F2.mjs} +33 -1
- package/dist/{chunks/messages-ID7kZQyc.mjs → messages-CCdDP2DE.mjs} +33 -1
- package/dist/{chunks/messages-Cx_kyYnf2.mjs → messages-CCgDqF-e.mjs} +33 -1
- package/dist/{chunks/messages-DyuKfskv.mjs → messages-CLkbvdRh2.mjs} +33 -1
- package/dist/{messages-DPzZM5FG.mjs → messages-CQ6A0zI9.mjs} +33 -1
- package/dist/{chunks/messages-DjSuzS2m2.mjs → messages-CQnz6pXD2.mjs} +33 -1
- package/dist/{messages-CWWIbebM2.mjs → messages-CQotju8u2.mjs} +33 -1
- package/dist/{chunks/messages-C4Wacagz.mjs → messages-CRL2ecWR.mjs} +33 -1
- package/dist/{chunks/messages-CkRbSW3z2.mjs → messages-CRhhyqEy2.mjs} +33 -1
- package/dist/{chunks/messages-CZfG1cmA2.mjs → messages-CSf1g9n_.mjs} +33 -1
- package/dist/{messages-Ck7HWiG_.mjs → messages-CYuTyoK6.mjs} +33 -1
- package/dist/{messages-BEnINcno2.mjs → messages-Co2GV7rA2.mjs} +33 -1
- package/dist/{chunks/messages-CpC5Cv_s.mjs → messages-CqtZ_gtb.mjs} +33 -1
- package/dist/{chunks/messages-Dp5C7vWz.mjs → messages-CxK_Vgra.mjs} +33 -1
- package/dist/{messages-2pXizYBd.mjs → messages-D08TSj5Q.mjs} +33 -1
- package/dist/{chunks/messages-n1T_9w-Z.mjs → messages-DFDw0nkI.mjs} +33 -1
- package/dist/{messages-S8uz2_bw2.mjs → messages-DKoeDmZV2.mjs} +33 -1
- package/dist/{chunks/messages-EI0ZLMIG.mjs → messages-DLKTwtBI.mjs} +33 -1
- package/dist/{chunks/messages-CcvcAZOt.mjs → messages-DQaDNQat2.mjs} +33 -1
- package/dist/{messages-B4cc5O5K.mjs → messages-DZlYqaxx.mjs} +33 -1
- package/dist/{messages-CdmaH2dA2.mjs → messages-D_ql7LL-2.mjs} +33 -1
- package/dist/{messages-CoSj68Ef2.mjs → messages-Da09byG42.mjs} +33 -1
- package/dist/{messages-BXgGBpmY.mjs → messages-DaKjx7Gh.mjs} +33 -1
- package/dist/{messages-C-XKbE2c.mjs → messages-Dc1aAz5V.mjs} +33 -1
- package/dist/{chunks/messages-CEslBsO62.mjs → messages-DlxBkSUv.mjs} +33 -1
- package/dist/{messages-BYyRYlTQ2.mjs → messages-DoahmNMI2.mjs} +33 -1
- package/dist/{messages-CGZPFOEr.mjs → messages-DpbHfnge.mjs} +33 -1
- package/dist/{chunks/messages-m_zk_AR3.mjs → messages-DrjZz7u9.mjs} +33 -1
- package/dist/{chunks/messages-DaqNOQ8p.mjs → messages-DrwEVbKk2.mjs} +33 -1
- package/dist/{messages-B_XoP1A3.mjs → messages-DvIPbDsq.mjs} +33 -1
- package/dist/{messages-BWzRZ5Un.mjs → messages-Dy3mYP_n.mjs} +33 -1
- package/dist/{chunks/messages-Dp5SW9D-.mjs → messages-STUXoPH72.mjs} +33 -1
- package/dist/{messages-BCeeFrNO.mjs → messages-Vfl9lWod.mjs} +33 -1
- package/dist/{messages-Nbssq1-N.mjs → messages-YPF71WjJ.mjs} +33 -1
- package/dist/{chunks/messages-14GzwdH92.mjs → messages-aPBlKHzr.mjs} +33 -1
- package/dist/{messages-Bj8K6_bv.mjs → messages-bCZW7LiC.mjs} +33 -1
- package/dist/{chunks/messages-Bh3E53-f2.mjs → messages-kjO2WeG-2.mjs} +33 -1
- package/dist/{chunks/messages-S_Bmo_Qc.mjs → messages-un7M6YZC2.mjs} +33 -1
- package/dist/react.cjs +1 -1
- package/dist/react.mjs +1 -1
- package/dist/tools.cjs +1 -1
- package/dist/tools.mjs +3 -3
- package/dist/vendor.LICENSE.txt +35 -0
- package/package.json +2 -1
- package/src/components/i18n/locales/am/messages.json +33 -1
- package/src/components/i18n/locales/ar/messages.json +33 -1
- package/src/components/i18n/locales/az/messages.json +33 -1
- package/src/components/i18n/locales/bg/messages.json +33 -1
- package/src/components/i18n/locales/bn/messages.json +33 -1
- package/src/components/i18n/locales/bs/messages.json +33 -1
- package/src/components/i18n/locales/cs/messages.json +33 -1
- package/src/components/i18n/locales/da/messages.json +33 -1
- package/src/components/i18n/locales/de/messages.json +33 -1
- package/src/components/i18n/locales/dv/messages.json +33 -1
- package/src/components/i18n/locales/el/messages.json +33 -1
- package/src/components/i18n/locales/en/messages.json +32 -0
- package/src/components/i18n/locales/es/messages.json +33 -1
- package/src/components/i18n/locales/et/messages.json +33 -1
- package/src/components/i18n/locales/fa/messages.json +33 -1
- package/src/components/i18n/locales/fi/messages.json +33 -1
- package/src/components/i18n/locales/fil/messages.json +33 -1
- package/src/components/i18n/locales/fr/messages.json +33 -1
- package/src/components/i18n/locales/gu/messages.json +33 -1
- package/src/components/i18n/locales/he/messages.json +33 -1
- package/src/components/i18n/locales/hi/messages.json +33 -1
- package/src/components/i18n/locales/hr/messages.json +33 -1
- package/src/components/i18n/locales/hu/messages.json +33 -1
- package/src/components/i18n/locales/hy/messages.json +33 -1
- package/src/components/i18n/locales/id/messages.json +33 -1
- package/src/components/i18n/locales/it/messages.json +33 -1
- package/src/components/i18n/locales/ja/messages.json +33 -1
- package/src/components/i18n/locales/ka/messages.json +33 -1
- package/src/components/i18n/locales/km/messages.json +33 -1
- package/src/components/i18n/locales/kn/messages.json +33 -1
- package/src/components/i18n/locales/ko/messages.json +33 -1
- package/src/components/i18n/locales/ku/messages.json +33 -1
- package/src/components/i18n/locales/lo/messages.json +33 -1
- package/src/components/i18n/locales/lt/messages.json +33 -1
- package/src/components/i18n/locales/lv/messages.json +33 -1
- package/src/components/i18n/locales/mk/messages.json +33 -1
- package/src/components/i18n/locales/ml/messages.json +33 -1
- package/src/components/i18n/locales/mn/messages.json +33 -1
- package/src/components/i18n/locales/mr/messages.json +33 -1
- package/src/components/i18n/locales/ms/messages.json +33 -1
- package/src/components/i18n/locales/my/messages.json +33 -1
- package/src/components/i18n/locales/ne/messages.json +33 -1
- package/src/components/i18n/locales/nl/messages.json +33 -1
- package/src/components/i18n/locales/no/messages.json +33 -1
- package/src/components/i18n/locales/pa/messages.json +33 -1
- package/src/components/i18n/locales/pl/messages.json +33 -1
- package/src/components/i18n/locales/ps/messages.json +33 -1
- package/src/components/i18n/locales/pt/messages.json +33 -1
- package/src/components/i18n/locales/ro/messages.json +33 -1
- package/src/components/i18n/locales/ru/messages.json +33 -1
- package/src/components/i18n/locales/sd/messages.json +33 -1
- package/src/components/i18n/locales/si/messages.json +33 -1
- package/src/components/i18n/locales/sk/messages.json +33 -1
- package/src/components/i18n/locales/sl/messages.json +33 -1
- package/src/components/i18n/locales/sq/messages.json +33 -1
- package/src/components/i18n/locales/sr/messages.json +33 -1
- package/src/components/i18n/locales/sv/messages.json +33 -1
- package/src/components/i18n/locales/sw/messages.json +33 -1
- package/src/components/i18n/locales/ta/messages.json +33 -1
- package/src/components/i18n/locales/te/messages.json +33 -1
- package/src/components/i18n/locales/th/messages.json +33 -1
- package/src/components/i18n/locales/tr/messages.json +33 -1
- package/src/components/i18n/locales/ug/messages.json +33 -1
- package/src/components/i18n/locales/uk/messages.json +33 -1
- package/src/components/i18n/locales/ur/messages.json +33 -1
- package/src/components/i18n/locales/vi/messages.json +33 -1
- package/src/components/i18n/locales/yi/messages.json +33 -1
- package/src/components/i18n/locales/zh/messages.json +33 -1
- package/src/components/icons/index.ts +106 -0
- package/src/components/modules/paste/handlers/pattern-handler.ts +4 -7
- package/src/components/utils/format-bytes.ts +15 -0
- package/src/components/utils/max-size.ts +36 -0
- package/src/components/utils/media-empty-state.ts +1 -11
- package/src/components/utils/upload-error-message.ts +43 -0
- package/src/styles/colors.css +31 -0
- package/src/styles/keyframes.css +8 -0
- package/src/styles/main.css +2 -0
- package/src/styles/resize-cue.css +26 -0
- package/src/styles/video.css +1221 -0
- package/src/tools/file/constants.ts +3 -0
- package/src/tools/file/index.ts +45 -4
- package/src/tools/file/uploader.ts +5 -2
- package/src/tools/image/constants.ts +1 -1
- package/src/tools/image/converted-uploader.ts +30 -0
- package/src/tools/image/gif-to-webm.ts +170 -0
- package/src/tools/image/index.ts +125 -3
- package/src/tools/image/resizer.ts +39 -6
- package/src/tools/image/uploader.ts +2 -1
- package/src/tools/image/uploading-state.ts +2 -1
- package/src/tools/index.ts +2 -0
- package/src/tools/video/constants.ts +22 -0
- package/src/tools/video/controls.ts +1448 -0
- package/src/tools/video/empty-state.ts +45 -0
- package/src/tools/video/i18n.ts +13 -0
- package/src/tools/video/index.ts +521 -0
- package/src/tools/video/ui.ts +83 -0
- package/src/tools/video/uploader.ts +75 -0
- package/types/configs/blok-config.d.ts +2 -9
- package/types/tools/file.d.ts +9 -3
- package/types/tools/image.d.ts +11 -2
- package/types/tools/max-size.d.ts +12 -0
- package/types/tools/video.d.ts +72 -0
- package/dist/chunks/constants-B2Lcd7Kk.cjs +0 -539
- package/dist/chunks/messages-3iHkF7Vy.cjs +0 -1
- package/dist/chunks/messages-BAgSlZpS.cjs +0 -1
- package/dist/chunks/messages-Br3ERe172.cjs +0 -1
- package/dist/chunks/messages-C6Xc3L-B2.cjs +0 -1
- package/dist/chunks/messages-CEquPedm.cjs +0 -1
- package/dist/chunks/messages-CFo-pG-t2.cjs +0 -1
- package/dist/chunks/messages-Cf8u0aln2.cjs +0 -1
- package/dist/chunks/messages-Cgg1HHSo.cjs +0 -1
- package/dist/chunks/messages-DOBcfgRF.cjs +0 -1
- package/dist/chunks/messages-JLj0J5so.cjs +0 -1
- package/dist/chunks/messages-p-1X5oFM2.cjs +0 -1
- package/dist/chunks/messages-vdupz5T0.cjs +0 -1
- package/dist/chunks/tools-DiTb45BS.cjs +0 -208
- /package/dist/chunks/{am-B37aLxvu.cjs → am-BA02TqF9.cjs} +0 -0
- /package/dist/chunks/{am-D7yfVqJS.mjs → am-C9_6r8HY.mjs} +0 -0
- /package/dist/chunks/{ar-D00SixXA.mjs → ar-BM96nSXF.mjs} +0 -0
- /package/dist/chunks/{ar-ySo_KDqd.cjs → ar-CB4IsGCa.cjs} +0 -0
- /package/dist/chunks/{az-BmIwKcoq.mjs → az-DnZSY5ok.mjs} +0 -0
- /package/dist/chunks/{az-nCoWuGBd.cjs → az-FykE_aDi.cjs} +0 -0
- /package/dist/chunks/{bg-BTZaB21W.mjs → bg-BplUvPeX.mjs} +0 -0
- /package/dist/chunks/{bg-BKB8_J3v.cjs → bg-D6r90gqH.cjs} +0 -0
- /package/dist/chunks/{bn-BQiBKtpR.cjs → bn-B7w2dbK4.cjs} +0 -0
- /package/dist/chunks/{bn-BVpf0XL6.mjs → bn-CdP9lnfr.mjs} +0 -0
- /package/dist/chunks/{bs-BSz4cPth.mjs → bs-BtX-qLsk.mjs} +0 -0
- /package/dist/chunks/{bs-D_lekdEb.cjs → bs-DztHHorM.cjs} +0 -0
- /package/dist/chunks/{cs-Dv8Jg_Fa.cjs → cs-CLMsCdIt.cjs} +0 -0
- /package/dist/chunks/{cs-p5s4GudH.mjs → cs-NcDYS-aU.mjs} +0 -0
- /package/dist/chunks/{da-BqJa8BMd.mjs → da-ANg6gK3-.mjs} +0 -0
- /package/dist/chunks/{da-D33rjz-t.cjs → da-sJ7EsHcO.cjs} +0 -0
- /package/dist/chunks/{de-BDDktkfI.mjs → de-CTxvRxuE.mjs} +0 -0
- /package/dist/chunks/{de-I8YFq0Ej.cjs → de-CxMqLKhy.cjs} +0 -0
- /package/dist/chunks/{el-BInM0nQo.cjs → el-Bg6QXLt5.cjs} +0 -0
- /package/dist/chunks/{el-R63G08P0.mjs → el-CgOoJaXB.mjs} +0 -0
- /package/dist/chunks/{es-Cj_AHhb9.cjs → es-7zUbslPJ.cjs} +0 -0
- /package/dist/chunks/{es-BleH_dHg.mjs → es-D5YDFRu-.mjs} +0 -0
- /package/dist/chunks/{et-BuISCVFl.mjs → et-57a7ees8.mjs} +0 -0
- /package/dist/chunks/{et-BkMzN_qG.cjs → et-D_rhRdDD.cjs} +0 -0
- /package/dist/chunks/{fa-CI9f3My0.mjs → fa-Dp_BXnBP.mjs} +0 -0
- /package/dist/chunks/{fa-C3VQIfEJ.cjs → fa-JBr2zsfI.cjs} +0 -0
- /package/dist/chunks/{fi-p8xxtchQ.cjs → fi-DjbYN6MA.cjs} +0 -0
- /package/dist/chunks/{fi-C0_Jyps9.mjs → fi-at45xjOS.mjs} +0 -0
- /package/dist/chunks/{fil-BZsJ4YZG.mjs → fil-D_HT2vGt.mjs} +0 -0
- /package/dist/chunks/{fil-DC8VTmfd.cjs → fil-dcVBIQoi.cjs} +0 -0
- /package/dist/chunks/{fr-BjeioA2N.mjs → fr-ByR9PLTE.mjs} +0 -0
- /package/dist/chunks/{fr-Cbstr5ii.cjs → fr-Ch4zj1rM.cjs} +0 -0
- /package/dist/chunks/{gu-DpPoUPem.cjs → gu-CoLW5Saq.cjs} +0 -0
- /package/dist/chunks/{gu-CNi9WnhQ.mjs → gu-CvfPvH3Y.mjs} +0 -0
- /package/dist/chunks/{he-Rlv-PVKK.cjs → he-2wS2DK0D.cjs} +0 -0
- /package/dist/chunks/{he-bU_Id5XQ.mjs → he-Bmqypvkq.mjs} +0 -0
- /package/dist/chunks/{hi-CYYjJgoQ.mjs → hi-DbcN0x64.mjs} +0 -0
- /package/dist/chunks/{hi-CfLaLKZL.cjs → hi-f4gshON3.cjs} +0 -0
- /package/dist/chunks/{hr-CerKCstu.mjs → hr-D5mydeyn.mjs} +0 -0
- /package/dist/chunks/{hr-B6eFd_4M.cjs → hr-DMExeWwd.cjs} +0 -0
- /package/dist/chunks/{hu-DQcisouf.cjs → hu-COmlHkse.cjs} +0 -0
- /package/dist/chunks/{hu-DNYoHMic.mjs → hu-DVCPn8un.mjs} +0 -0
- /package/dist/chunks/{hy-x9QOWTT6.cjs → hy-BoBSSiJc.cjs} +0 -0
- /package/dist/chunks/{hy-N0XBqVv_.mjs → hy-CP6oqcK_.mjs} +0 -0
- /package/dist/chunks/{id-dmUIQB07.mjs → id-COwan-Bb.mjs} +0 -0
- /package/dist/chunks/{id-G7ASv812.cjs → id-Dk2AQ6-R.cjs} +0 -0
- /package/dist/chunks/{it-DXDwm7Aj.cjs → it-BCmU3zOg.cjs} +0 -0
- /package/dist/chunks/{it-G9Rg2LaN.mjs → it-DJqN-zLs.mjs} +0 -0
- /package/dist/chunks/{ja-C1Nou_k5.mjs → ja-CvoLNAXS.mjs} +0 -0
- /package/dist/chunks/{ja-DHMurrnk.cjs → ja-DcMqJvnV.cjs} +0 -0
- /package/dist/chunks/{ka-P0_q1_oW.cjs → ka-BdyjQFkv.cjs} +0 -0
- /package/dist/chunks/{ka-9rtmTdhq.mjs → ka-BsqbDXqr.mjs} +0 -0
- /package/dist/chunks/{km-DedsOdPY.cjs → km-BShH0mB9.cjs} +0 -0
- /package/dist/chunks/{km-T8J0lGcC.mjs → km-C9FxSKv0.mjs} +0 -0
- /package/dist/chunks/{kn-CMb5Cv2d.mjs → kn-BKTm-E-A.mjs} +0 -0
- /package/dist/chunks/{kn-CAGP6H3x.cjs → kn-bNx6e6NK.cjs} +0 -0
- /package/dist/chunks/{ko-DulVmkjC.mjs → ko-BLEDr4t_.mjs} +0 -0
- /package/dist/chunks/{ko-3D0kPtnu.cjs → ko-LmGjEODJ.cjs} +0 -0
- /package/dist/chunks/{ku-BYzD3GnU.cjs → ku-BekLwcHQ.cjs} +0 -0
- /package/dist/chunks/{ku-C5mlSVfC.mjs → ku-DGpIv9vX.mjs} +0 -0
- /package/dist/chunks/{lo-Cw2hfj84.mjs → lo-C3wsS73b.mjs} +0 -0
- /package/dist/chunks/{lo-D9ixPNJD.cjs → lo-YgZPFD63.cjs} +0 -0
- /package/dist/chunks/{lt-CzfEJdVd.cjs → lt-CIvBJk2h.cjs} +0 -0
- /package/dist/chunks/{lt-Ceg9NAdI.mjs → lt-DWWjfR4Z.mjs} +0 -0
- /package/dist/chunks/{lv-DWyBbTHI.cjs → lv-B6Djmvjl.cjs} +0 -0
- /package/dist/chunks/{lv-3CXzQoic.mjs → lv-D4nb-9oK.mjs} +0 -0
- /package/dist/chunks/{mk-CEbNT0Oe.mjs → mk-BPUvDORf.mjs} +0 -0
- /package/dist/chunks/{mk-Anl5AUSU.cjs → mk-Ck56Emvp.cjs} +0 -0
- /package/dist/chunks/{ml-B1QmnbTX.cjs → ml-DsibsgSA.cjs} +0 -0
- /package/dist/chunks/{ml-DaF_zn1h.mjs → ml-qo0n1JHs.mjs} +0 -0
- /package/dist/chunks/{mn-DLpgZmIl.mjs → mn-BAQnLAOv.mjs} +0 -0
- /package/dist/chunks/{mn-DM3AWPQI.cjs → mn-Db0fVNYE.cjs} +0 -0
- /package/dist/chunks/{mr-CPCMiBsx.cjs → mr-BTz5VL4u.cjs} +0 -0
- /package/dist/chunks/{mr-C-d3CWSD.mjs → mr-CFEPrQYy.mjs} +0 -0
- /package/dist/chunks/{ms-D3Zil5Dz.mjs → ms-C9p2_6EB.mjs} +0 -0
- /package/dist/chunks/{ms-BKg5zJ6g.cjs → ms-ItzWfmiu.cjs} +0 -0
- /package/dist/chunks/{my-HzQdQq6H.mjs → my-BQhcHK_d.mjs} +0 -0
- /package/dist/chunks/{my-BhrbZPAE.cjs → my-Bq1EPoFX.cjs} +0 -0
- /package/dist/chunks/{ne-Dw6kUPkS.mjs → ne-Cf4GpHyI.mjs} +0 -0
- /package/dist/chunks/{ne-BsEUv3Vp.cjs → ne-Mog3kAWt.cjs} +0 -0
- /package/dist/chunks/{nl-D8QQ6wmJ.mjs → nl-7DE7AKK5.mjs} +0 -0
- /package/dist/chunks/{nl-C1jFx32v.cjs → nl-Do9Y1u3F.cjs} +0 -0
- /package/dist/chunks/{no-BWEOGeAo.cjs → no-DNuo9wW6.cjs} +0 -0
- /package/dist/chunks/{no-Rg2uuKdV.mjs → no-J4jj2fW7.mjs} +0 -0
- /package/dist/chunks/{notifier-CEnelp-K.mjs → notifier-BDNwSr07.mjs} +0 -0
- /package/dist/chunks/{notifier-BOB8o7IG.cjs → notifier-BW5H3Ctc.cjs} +0 -0
- /package/dist/chunks/{pa-BitCteAA.cjs → pa-BVCDv2EV.cjs} +0 -0
- /package/dist/chunks/{pa-BSwIZvA0.mjs → pa-DMuUOUxa.mjs} +0 -0
- /package/dist/chunks/{pl-CbCrHaA6.cjs → pl-Ct8v4d8V.cjs} +0 -0
- /package/dist/chunks/{pl-FhXomIGp.mjs → pl-DIZm5XzV.mjs} +0 -0
- /package/dist/chunks/{ps-4sPZuv3g.cjs → ps-DT4DAHOy.cjs} +0 -0
- /package/dist/chunks/{ps-BoBtbrB6.mjs → ps-X3--6pS3.mjs} +0 -0
- /package/dist/chunks/{pt-BYA5NOw_.mjs → pt-BMT2BIpj.mjs} +0 -0
- /package/dist/chunks/{pt-CSzjho-V.cjs → pt-Dwe6Dinw.cjs} +0 -0
- /package/dist/chunks/{ro-C9tN3AFF.cjs → ro-CoHEUo3b.cjs} +0 -0
- /package/dist/chunks/{ro-mtobtMpD.mjs → ro-Ndh2J4Fv.mjs} +0 -0
- /package/dist/chunks/{ru-B791LPq3.mjs → ru-DEoON4H0.mjs} +0 -0
- /package/dist/chunks/{ru-BYPO5wNO.cjs → ru-Pmgo28Kt.cjs} +0 -0
- /package/dist/chunks/{sd-C8ngG92I.mjs → sd-B46KwBlt.mjs} +0 -0
- /package/dist/chunks/{sd-BWuglgs_.cjs → sd-DxSmw84d.cjs} +0 -0
- /package/dist/chunks/{si-C_q8DxVt.mjs → si-CEX6-_X4.mjs} +0 -0
- /package/dist/chunks/{si-IX2P_DGB.cjs → si-attpuQg-.cjs} +0 -0
- /package/dist/chunks/{sk-HJJlM9uq.mjs → sk-Crsvi9aQ.mjs} +0 -0
- /package/dist/chunks/{sk-CxtyIOlr.cjs → sk-CvkpO0ti.cjs} +0 -0
- /package/dist/chunks/{sl-CPjYG80u.cjs → sl-BT19lhkA.cjs} +0 -0
- /package/dist/chunks/{sl-kB1f5rPn.mjs → sl-Cqe1IKhE.mjs} +0 -0
- /package/dist/chunks/{sq-CD0W0D6z.mjs → sq-BOQb1hx9.mjs} +0 -0
- /package/dist/chunks/{sq-Do6UyHn9.cjs → sq-rY2-arjV.cjs} +0 -0
- /package/dist/chunks/{sr-lLJu9TsT.mjs → sr-WVqhi-MV.mjs} +0 -0
- /package/dist/chunks/{sr-BcyT-MWS.cjs → sr-jnkxoHkR.cjs} +0 -0
- /package/dist/chunks/{sv-Bsz7ersx.cjs → sv-BiWSYdw7.cjs} +0 -0
- /package/dist/chunks/{sv-C3GL-a0k.mjs → sv-cjssSK3x.mjs} +0 -0
- /package/dist/chunks/{sw-D6-DZ14-.mjs → sw-BNNAgQNn.mjs} +0 -0
- /package/dist/chunks/{sw-CUElHZxL.cjs → sw-DFyk62c4.cjs} +0 -0
- /package/dist/chunks/{ta-D0DQba-y.cjs → ta-DowDx1bh.cjs} +0 -0
- /package/dist/chunks/{ta-BZy78kBk.mjs → ta-jg4mBsL9.mjs} +0 -0
- /package/dist/chunks/{te-vigCVKdg.mjs → te-BiLupA2T.mjs} +0 -0
- /package/dist/chunks/{te-DPMvJyXg.cjs → te-G0buJU1V.cjs} +0 -0
- /package/dist/chunks/{th-DtcrIt9J.cjs → th-DsjA6OWh.cjs} +0 -0
- /package/dist/chunks/{th-CGc0RLWB.mjs → th-Dtb_u833.mjs} +0 -0
- /package/dist/chunks/{tr-Cgkf5aJo.mjs → tr-01MbT9-l.mjs} +0 -0
- /package/dist/chunks/{tr-BrEFaVNA.cjs → tr-DN1xdxQn.cjs} +0 -0
- /package/dist/chunks/{ug-BVK7qBs4.mjs → ug-BFlA2VRZ.mjs} +0 -0
- /package/dist/chunks/{ug-CqZXfhXi.cjs → ug-DEJ9cNGH.cjs} +0 -0
- /package/dist/chunks/{uk-DgReWEge.cjs → uk-Bv6aB1Nd.cjs} +0 -0
- /package/dist/chunks/{uk-CSPaCalw.mjs → uk-DY7rBPyo.mjs} +0 -0
- /package/dist/chunks/{ur-ByrwJnY1.cjs → ur-C3CFfx9s.cjs} +0 -0
- /package/dist/chunks/{ur-VpFFsATs.mjs → ur-DHfULZeF.mjs} +0 -0
- /package/dist/chunks/{vi-CWQtvATS.mjs → vi-BFuaq2Tk.mjs} +0 -0
- /package/dist/chunks/{vi-lUD3w7gP.cjs → vi-DQrrRkmq.cjs} +0 -0
- /package/dist/chunks/{zh-fSp1qOne.cjs → zh-5rdSdLkR.cjs} +0 -0
- /package/dist/chunks/{zh-akJj2cdF.mjs → zh-BGpl7x7W.mjs} +0 -0
|
@@ -16,4 +16,7 @@ export const PASTE_EXTENSIONS = [
|
|
|
16
16
|
'mp3', 'wav', 'ogg', 'mp4', 'mov', 'avi', 'mkv',
|
|
17
17
|
] as const;
|
|
18
18
|
|
|
19
|
+
/** Default upload ceiling (bytes) when the consumer does not configure `maxSize`. */
|
|
20
|
+
export const DEFAULT_MAX_SIZE = 30 * 1024 * 1024; // 30 MiB
|
|
21
|
+
|
|
19
22
|
export const DEFAULT_CAPTION_PLACEHOLDER = 'Write a caption…';
|
package/src/tools/file/index.ts
CHANGED
|
@@ -11,12 +11,15 @@ import type {
|
|
|
11
11
|
import type { MenuConfig } from '../../../types/tools/menu-config';
|
|
12
12
|
import type { FileConfig, FileData, FileUploadResult } from '../../../types/tools/file';
|
|
13
13
|
import type { ImageData } from '../../../types/tools/image';
|
|
14
|
+
import type { VideoData } from '../../../types/tools/video';
|
|
14
15
|
import { IconCaption, IconCopy, IconDownload, IconFile, IconReplace } from '../../components/icons';
|
|
15
16
|
import { DEFAULT_CAPTION_PLACEHOLDER, PASTE_EXTENSIONS, PASTE_MIME_TYPES } from './constants';
|
|
16
17
|
import { renderEmptyState, type EmptyStateElement } from './empty-state';
|
|
17
18
|
import { renderUploadingState, type UploadingStateElement } from './uploading-state';
|
|
18
19
|
import { renderCaptionRow, renderFileCard } from './ui';
|
|
19
20
|
import { Uploader } from './uploader';
|
|
21
|
+
import { FileToolError } from './errors';
|
|
22
|
+
import { uploadErrorMessage } from '../../components/utils/upload-error-message';
|
|
20
23
|
import { safeHttpHref } from './url';
|
|
21
24
|
import { isPreviewable } from './preview';
|
|
22
25
|
import { openFilePreview } from './preview-modal';
|
|
@@ -26,6 +29,9 @@ type ToolState = 'EMPTY' | 'LOADING' | 'RENDERED' | 'ERROR';
|
|
|
26
29
|
/** Image filenames/URLs that should auto-convert the File block into an Image. */
|
|
27
30
|
const IMAGE_EXTENSION_RE = /\.(png|jpe?g|gif|webp|svg)(?:[?#]|$)/i;
|
|
28
31
|
|
|
32
|
+
/** Video filenames/URLs that should auto-convert the File block into a Video. */
|
|
33
|
+
const VIDEO_EXTENSION_RE = /\.(mp4|webm|ogg|mov|m4v)(?:[?#]|$)/i;
|
|
34
|
+
|
|
29
35
|
export class FileTool implements BlockTool {
|
|
30
36
|
private readonly api: API;
|
|
31
37
|
private readonly block: BlockAPI;
|
|
@@ -37,9 +43,12 @@ export class FileTool implements BlockTool {
|
|
|
37
43
|
private state: ToolState;
|
|
38
44
|
private uploadingEl: UploadingStateElement | null = null;
|
|
39
45
|
private lastFileName: string | null = null;
|
|
46
|
+
private errorMessage: string | null = null;
|
|
40
47
|
private previewTeardown: (() => void) | null = null;
|
|
41
48
|
/** When the pending upload is an image, the result converts to an Image block. */
|
|
42
49
|
private pendingImageConversion = false;
|
|
50
|
+
/** When the pending upload is a video, the result converts to a Video block. */
|
|
51
|
+
private pendingVideoConversion = false;
|
|
43
52
|
|
|
44
53
|
constructor(options: BlockToolConstructorOptions<FileData, FileConfig>) {
|
|
45
54
|
this.api = options.api;
|
|
@@ -153,26 +162,29 @@ export class FileTool implements BlockTool {
|
|
|
153
162
|
private startUpload(file: File): void {
|
|
154
163
|
this.lastFileName = file.name;
|
|
155
164
|
this.pendingImageConversion = file.type.startsWith('image/') || IMAGE_EXTENSION_RE.test(file.name);
|
|
165
|
+
this.pendingVideoConversion = file.type.startsWith('video/') || VIDEO_EXTENSION_RE.test(file.name);
|
|
156
166
|
this.state = 'LOADING';
|
|
157
167
|
this.renderState();
|
|
158
168
|
void this.uploader
|
|
159
169
|
.handleFile(file, { onProgress: (p) => this.uploadingEl?.setProgress(p) })
|
|
160
170
|
.then((result) => this.applyResult(result))
|
|
161
|
-
.catch(() => this.applyError());
|
|
171
|
+
.catch((err) => this.applyError(err));
|
|
162
172
|
}
|
|
163
173
|
|
|
164
174
|
private startUrl(url: string): void {
|
|
165
175
|
this.lastFileName = null;
|
|
166
176
|
this.pendingImageConversion = IMAGE_EXTENSION_RE.test(url);
|
|
177
|
+
this.pendingVideoConversion = VIDEO_EXTENSION_RE.test(url);
|
|
167
178
|
this.state = 'LOADING';
|
|
168
179
|
this.renderState();
|
|
169
180
|
void this.uploader
|
|
170
181
|
.handleUrl(url, { onProgress: (p) => this.uploadingEl?.setProgress(p) })
|
|
171
182
|
.then((result) => this.applyResult(result))
|
|
172
|
-
.catch(() => this.applyError());
|
|
183
|
+
.catch((err) => this.applyError(err));
|
|
173
184
|
}
|
|
174
185
|
|
|
175
186
|
private applyResult(result: FileUploadResult): void {
|
|
187
|
+
this.errorMessage = null;
|
|
176
188
|
this.data = {
|
|
177
189
|
...this.data,
|
|
178
190
|
url: result.url,
|
|
@@ -180,6 +192,9 @@ export class FileTool implements BlockTool {
|
|
|
180
192
|
size: result.size ?? this.data.size,
|
|
181
193
|
mimeType: result.mimeType ?? this.data.mimeType,
|
|
182
194
|
};
|
|
195
|
+
if (this.pendingVideoConversion && this.convertToVideo()) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
183
198
|
if (this.pendingImageConversion && this.convertToImage()) {
|
|
184
199
|
return;
|
|
185
200
|
}
|
|
@@ -207,7 +222,33 @@ export class FileTool implements BlockTool {
|
|
|
207
222
|
return true;
|
|
208
223
|
}
|
|
209
224
|
|
|
210
|
-
|
|
225
|
+
/**
|
|
226
|
+
* Swap this File block for a Video block carrying the just-uploaded url.
|
|
227
|
+
* Mirrors {@link convertToImage}: File and Video declare no conversionConfig,
|
|
228
|
+
* so we replace in place. Returns false (keep the file card) when the block
|
|
229
|
+
* can no longer be located.
|
|
230
|
+
*/
|
|
231
|
+
private convertToVideo(): boolean {
|
|
232
|
+
const index = this.api.blocks.getBlockIndex(this.block.id);
|
|
233
|
+
if (index === undefined) {
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
const videoData: VideoData = { url: this.data.url };
|
|
237
|
+
if (this.data.fileName !== undefined) videoData.fileName = this.data.fileName;
|
|
238
|
+
if (this.data.mimeType !== undefined) videoData.mimeType = this.data.mimeType;
|
|
239
|
+
if ((this.data.caption ?? '') !== '') videoData.caption = this.data.caption;
|
|
240
|
+
if (this.data.captionVisible !== undefined) videoData.captionVisible = this.data.captionVisible;
|
|
241
|
+
this.api.blocks.insert('video', videoData, {}, index, false, true);
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
private applyError(err?: unknown): void {
|
|
246
|
+
this.errorMessage = err instanceof FileToolError
|
|
247
|
+
? uploadErrorMessage(err, (key) => this.api.i18n.t(key), {
|
|
248
|
+
tooLarge: 'tools.file.errorFileTooLarge',
|
|
249
|
+
generic: 'tools.file.errorUploadFailed',
|
|
250
|
+
})
|
|
251
|
+
: null;
|
|
211
252
|
this.state = 'ERROR';
|
|
212
253
|
this.renderState();
|
|
213
254
|
}
|
|
@@ -331,7 +372,7 @@ export class FileTool implements BlockTool {
|
|
|
331
372
|
wrap.setAttribute('data-role', 'file-error');
|
|
332
373
|
|
|
333
374
|
const message = document.createElement('span');
|
|
334
|
-
message.textContent = i18n.t('tools.file.errorUploadFailed');
|
|
375
|
+
message.textContent = this.errorMessage ?? i18n.t('tools.file.errorUploadFailed');
|
|
335
376
|
|
|
336
377
|
const retry = document.createElement('button');
|
|
337
378
|
retry.type = 'button';
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { FileConfig, FileUploadResult } from '../../../types/tools/file';
|
|
2
|
+
import { resolveMaxSize } from '../../components/utils/max-size';
|
|
3
|
+
import { DEFAULT_MAX_SIZE } from './constants';
|
|
2
4
|
import { FileToolError } from './errors';
|
|
3
5
|
|
|
4
6
|
export interface UploadOptions {
|
|
@@ -120,12 +122,13 @@ export class Uploader {
|
|
|
120
122
|
}
|
|
121
123
|
|
|
122
124
|
private validateFile(file: File): void {
|
|
123
|
-
const { types
|
|
125
|
+
const { types } = this.config;
|
|
126
|
+
const maxSize = resolveMaxSize(this.config.maxSize, file.type, DEFAULT_MAX_SIZE);
|
|
124
127
|
|
|
125
128
|
if (types !== undefined && !types.includes(file.type)) {
|
|
126
129
|
throw new FileToolError('UNSUPPORTED_TYPE', file.type || 'unknown');
|
|
127
130
|
}
|
|
128
|
-
if (
|
|
131
|
+
if (file.size > maxSize) {
|
|
129
132
|
throw new FileToolError('FILE_TOO_LARGE', `${file.size} > ${maxSize}`);
|
|
130
133
|
}
|
|
131
134
|
}
|
|
@@ -6,7 +6,7 @@ export const DEFAULT_MIME_TYPES = [
|
|
|
6
6
|
'image/svg+xml',
|
|
7
7
|
] as const;
|
|
8
8
|
|
|
9
|
-
export const DEFAULT_MAX_SIZE =
|
|
9
|
+
export const DEFAULT_MAX_SIZE = 30 * 1024 * 1024; // 30 MiB
|
|
10
10
|
|
|
11
11
|
export const DEFAULT_CAPTION_PLACEHOLDER = 'Write a caption…';
|
|
12
12
|
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { API } from '../../../types';
|
|
2
|
+
import type { ImageConfig } from '../../../types/tools/image';
|
|
3
|
+
|
|
4
|
+
export type ConvertedUploadFn = (
|
|
5
|
+
file: File,
|
|
6
|
+
ctx?: { onProgress?: (percent: number) => void },
|
|
7
|
+
) => Promise<{ url: string; fileName?: string }>;
|
|
8
|
+
|
|
9
|
+
interface UploaderHolder { uploader?: { uploadByFile?: ConvertedUploadFn } }
|
|
10
|
+
|
|
11
|
+
function asUploaderHolder(value: unknown): UploaderHolder | undefined {
|
|
12
|
+
return typeof value === 'object' && value !== null ? (value as UploaderHolder) : undefined;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Resolve which `uploadByFile` to use for a converted WebM file:
|
|
17
|
+
* the video tool's uploader (if registered under the `video` key), else the
|
|
18
|
+
* image tool's own uploader, else undefined (caller uses a blob URL).
|
|
19
|
+
*/
|
|
20
|
+
export function resolveConvertedUploader(
|
|
21
|
+
api: API,
|
|
22
|
+
imageConfig: ImageConfig,
|
|
23
|
+
): ConvertedUploadFn | undefined {
|
|
24
|
+
const videoEntry = asUploaderHolder(api.tools.getToolsConfig().tools?.video);
|
|
25
|
+
// Tool settings carry the config under `.config`; support a flat shape too.
|
|
26
|
+
const videoConfig = asUploaderHolder((videoEntry as { config?: unknown })?.config) ?? videoEntry;
|
|
27
|
+
const videoUpload = videoConfig?.uploader?.uploadByFile;
|
|
28
|
+
|
|
29
|
+
return videoUpload ?? imageConfig.uploader?.uploadByFile;
|
|
30
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// Minimal structural types for the WebCodecs surface we use (avoids relying on
|
|
2
|
+
// lib.dom types that may not be present in the project's TS target).
|
|
3
|
+
interface DecodedFrame {
|
|
4
|
+
displayWidth: number;
|
|
5
|
+
displayHeight: number;
|
|
6
|
+
duration: number | null;
|
|
7
|
+
timestamp: number;
|
|
8
|
+
close(): void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface ImageDecoderLike {
|
|
12
|
+
tracks: { ready: Promise<void>; selectedTrack?: { frameCount: number } };
|
|
13
|
+
decode(opts: { frameIndex: number }): Promise<{ image: DecodedFrame; complete: boolean }>;
|
|
14
|
+
close(): void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface EncodedChunk { type: string; }
|
|
18
|
+
|
|
19
|
+
interface VideoEncoderLike {
|
|
20
|
+
configure(config: Record<string, unknown>): void;
|
|
21
|
+
encode(frame: DecodedFrame, opts?: { keyFrame?: boolean }): void;
|
|
22
|
+
flush(): Promise<void>;
|
|
23
|
+
close(): void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface VideoEncoderCtor {
|
|
27
|
+
new (init: {
|
|
28
|
+
output: (chunk: EncodedChunk, meta: unknown) => void;
|
|
29
|
+
error: (err: unknown) => void;
|
|
30
|
+
}): VideoEncoderLike;
|
|
31
|
+
isConfigSupported(config: Record<string, unknown>): Promise<{ supported?: boolean }>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const KEYFRAME_INTERVAL = 30;
|
|
35
|
+
|
|
36
|
+
function hasWebCodecs(): boolean {
|
|
37
|
+
const g = globalThis as Record<string, unknown>;
|
|
38
|
+
return typeof g.ImageDecoder !== 'undefined' && typeof g.VideoEncoder !== 'undefined';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function pickCodec(
|
|
42
|
+
Encoder: VideoEncoderCtor,
|
|
43
|
+
width: number,
|
|
44
|
+
height: number,
|
|
45
|
+
): Promise<{ encoderCodec: string; muxerCodec: 'V_VP9' | 'V_VP8' } | null> {
|
|
46
|
+
const candidates: Array<{ encoderCodec: string; muxerCodec: 'V_VP9' | 'V_VP8' }> = [
|
|
47
|
+
{ encoderCodec: 'vp09.00.10.08', muxerCodec: 'V_VP9' },
|
|
48
|
+
{ encoderCodec: 'vp8', muxerCodec: 'V_VP8' },
|
|
49
|
+
];
|
|
50
|
+
for (const c of candidates) {
|
|
51
|
+
const probe = await Encoder.isConfigSupported({ codec: c.encoderCodec, width, height });
|
|
52
|
+
if (probe.supported) return c;
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Convert an animated GIF (raw bytes) into a WebM Blob using WebCodecs.
|
|
59
|
+
* Returns null whenever the caller should keep the original GIF: WebCodecs
|
|
60
|
+
* unavailable, a static/single-frame GIF, an unsupported codec, or any error.
|
|
61
|
+
*/
|
|
62
|
+
export async function convertGifToWebm(
|
|
63
|
+
data: ArrayBuffer,
|
|
64
|
+
opts: { onProgress?: (percent: number) => void } = {},
|
|
65
|
+
): Promise<Blob | null> {
|
|
66
|
+
if (!hasWebCodecs()) return null;
|
|
67
|
+
|
|
68
|
+
const g = globalThis as Record<string, unknown>;
|
|
69
|
+
const DecoderCtor = g.ImageDecoder as new (init: { data: ArrayBuffer; type: string }) => ImageDecoderLike;
|
|
70
|
+
const EncoderCtor = g.VideoEncoder as VideoEncoderCtor;
|
|
71
|
+
|
|
72
|
+
const resources: { decoder?: ImageDecoderLike; encoder?: VideoEncoderLike } = {};
|
|
73
|
+
const openFrames: DecodedFrame[] = [];
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
const decoder = new DecoderCtor({ data, type: 'image/gif' });
|
|
77
|
+
resources.decoder = decoder;
|
|
78
|
+
await decoder.tracks.ready;
|
|
79
|
+
const frameCount = decoder.tracks.selectedTrack?.frameCount ?? 0;
|
|
80
|
+
if (frameCount <= 1) return null;
|
|
81
|
+
|
|
82
|
+
const first = await decoder.decode({ frameIndex: 0 });
|
|
83
|
+
openFrames.push(first.image);
|
|
84
|
+
const width = first.image.displayWidth;
|
|
85
|
+
const height = first.image.displayHeight;
|
|
86
|
+
|
|
87
|
+
const codec = await pickCodec(EncoderCtor, width, height);
|
|
88
|
+
if (!codec) return null;
|
|
89
|
+
|
|
90
|
+
const { Muxer, ArrayBufferTarget } = await import('webm-muxer');
|
|
91
|
+
const target = new ArrayBufferTarget();
|
|
92
|
+
const muxer = new Muxer({
|
|
93
|
+
target,
|
|
94
|
+
video: { codec: codec.muxerCodec, width, height },
|
|
95
|
+
firstTimestampBehavior: 'offset',
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const encoder = new EncoderCtor({
|
|
99
|
+
output: (chunk, meta) => muxer.addVideoChunk(chunk as never, meta as never),
|
|
100
|
+
error: () => undefined,
|
|
101
|
+
});
|
|
102
|
+
resources.encoder = encoder;
|
|
103
|
+
encoder.configure({
|
|
104
|
+
codec: codec.encoderCodec,
|
|
105
|
+
width,
|
|
106
|
+
height,
|
|
107
|
+
bitrate: Math.max(200_000, width * height * 4),
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const indices = Array.from({ length: frameCount }, (_unused, i) => i);
|
|
111
|
+
// Running timestamp held in a const cursor so each frame is re-stamped to be
|
|
112
|
+
// monotonic from the accumulated GIF delays (µs) without a mutable `let`.
|
|
113
|
+
const cursor = { timestamp: 0 };
|
|
114
|
+
for (const i of indices) {
|
|
115
|
+
const frame = await getFrame(i, first.image, decoder);
|
|
116
|
+
cursor.timestamp = encodeFrame(encoder, frame, i, cursor.timestamp);
|
|
117
|
+
collectFrame(openFrames, frame, i);
|
|
118
|
+
opts.onProgress?.(Math.round(((i + 1) / frameCount) * 100));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
await encoder.flush();
|
|
122
|
+
muxer.finalize();
|
|
123
|
+
return new Blob([target.buffer], { type: 'video/webm' });
|
|
124
|
+
} catch {
|
|
125
|
+
return null;
|
|
126
|
+
} finally {
|
|
127
|
+
openFrames.forEach((f) => {
|
|
128
|
+
try { f.close(); } catch { /* already closed */ }
|
|
129
|
+
});
|
|
130
|
+
try { resources.encoder?.close(); } catch { /* noop */ }
|
|
131
|
+
try { resources.decoder?.close(); } catch { /* noop */ }
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Resolve a decoded frame for the given index. Frame 0 reuses the already
|
|
137
|
+
* decoded first image; later frames are decoded on demand.
|
|
138
|
+
*/
|
|
139
|
+
async function getFrame(
|
|
140
|
+
index: number,
|
|
141
|
+
first: DecodedFrame,
|
|
142
|
+
decoder: ImageDecoderLike,
|
|
143
|
+
): Promise<DecodedFrame> {
|
|
144
|
+
if (index === 0) return first;
|
|
145
|
+
return (await decoder.decode({ frameIndex: index })).image;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Encode one frame at the given timestamp and return the timestamp for the
|
|
150
|
+
* next frame (current + this frame's GIF delay, in µs).
|
|
151
|
+
*/
|
|
152
|
+
function encodeFrame(
|
|
153
|
+
encoder: VideoEncoderLike,
|
|
154
|
+
frame: DecodedFrame,
|
|
155
|
+
index: number,
|
|
156
|
+
timestamp: number,
|
|
157
|
+
): number {
|
|
158
|
+
const stamped = frame as DecodedFrame & { timestamp: number };
|
|
159
|
+
stamped.timestamp = timestamp;
|
|
160
|
+
encoder.encode(frame, { keyFrame: index % KEYFRAME_INTERVAL === 0 });
|
|
161
|
+
return timestamp + (frame.duration ?? 100_000);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Track later frames so they can be closed in the finally block. Frame 0 is
|
|
166
|
+
* already tracked by the caller.
|
|
167
|
+
*/
|
|
168
|
+
function collectFrame(openFrames: DecodedFrame[], frame: DecodedFrame, index: number): void {
|
|
169
|
+
if (index !== 0) openFrames.push(frame);
|
|
170
|
+
}
|
package/src/tools/image/index.ts
CHANGED
|
@@ -33,6 +33,8 @@ import {
|
|
|
33
33
|
} from '../../components/icons';
|
|
34
34
|
import { DEFAULT_CAPTION_PLACEHOLDER, URL_PATTERN } from './constants';
|
|
35
35
|
import { renderEmptyState, type EmptyStateElement } from './empty-state';
|
|
36
|
+
import { uploadErrorMessage } from '../../components/utils/upload-error-message';
|
|
37
|
+
import { pickDisplayMaxSize } from '../../components/utils/max-size';
|
|
36
38
|
import { renderErrorState } from './error-state';
|
|
37
39
|
import { ImageError } from './errors';
|
|
38
40
|
import { attachResizeHandle, type ResizeEdge } from './resizer';
|
|
@@ -50,6 +52,8 @@ import { openAltPopover } from './alt-popover';
|
|
|
50
52
|
import { probeImageDimensions } from './probe-dimensions';
|
|
51
53
|
import { renderUploadingState, type UploadingStateElement } from './uploading-state';
|
|
52
54
|
import { Uploader, type UploadResult } from './uploader';
|
|
55
|
+
import { convertGifToWebm } from './gif-to-webm';
|
|
56
|
+
import { resolveConvertedUploader } from './converted-uploader';
|
|
53
57
|
|
|
54
58
|
type ToolState = 'EMPTY' | 'LOADING' | 'RENDERED' | 'ERROR';
|
|
55
59
|
|
|
@@ -83,6 +87,7 @@ export class ImageTool implements BlockTool {
|
|
|
83
87
|
private brokenImage = false;
|
|
84
88
|
private retrying = false;
|
|
85
89
|
private reloadAttempts = 0;
|
|
90
|
+
private converting = false;
|
|
86
91
|
|
|
87
92
|
constructor(options: BlockToolConstructorOptions<ImageData, ImageConfig>) {
|
|
88
93
|
this.api = options.api;
|
|
@@ -153,7 +158,11 @@ export class ImageTool implements BlockTool {
|
|
|
153
158
|
public onPaste(event: PasteEvent): void {
|
|
154
159
|
if (event.type === 'pattern') {
|
|
155
160
|
const detail = (event as PatternPasteEvent).detail;
|
|
156
|
-
this.
|
|
161
|
+
if (this.shouldConvertGifUrl(detail.data)) {
|
|
162
|
+
this.startUrl(detail.data);
|
|
163
|
+
} else {
|
|
164
|
+
this.applyResult({ url: detail.data });
|
|
165
|
+
}
|
|
157
166
|
return;
|
|
158
167
|
}
|
|
159
168
|
if (event.type === 'tag') {
|
|
@@ -174,6 +183,18 @@ export class ImageTool implements BlockTool {
|
|
|
174
183
|
};
|
|
175
184
|
|
|
176
185
|
private startUpload(file: File): void {
|
|
186
|
+
if (this.shouldConvertGif(file.type)) {
|
|
187
|
+
void this.convertGifToVideoBlock(file).then((handled) => {
|
|
188
|
+
if (!handled) this.uploadImageFile(file);
|
|
189
|
+
});
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
this.uploadImageFile(file);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** Existing upload body, extracted verbatim. */
|
|
196
|
+
private uploadImageFile(file: File): void {
|
|
197
|
+
this.converting = false;
|
|
177
198
|
this.lastFileName = file.name;
|
|
178
199
|
this.lastSource = { kind: 'file', file };
|
|
179
200
|
this.state = 'LOADING';
|
|
@@ -186,7 +207,67 @@ export class ImageTool implements BlockTool {
|
|
|
186
207
|
.catch((err) => this.applyError(err));
|
|
187
208
|
}
|
|
188
209
|
|
|
210
|
+
private shouldConvertGif(mimeType: string): boolean {
|
|
211
|
+
return this.config.convertGifToVideo !== false && mimeType === 'image/gif';
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Try to convert a GIF to a looping WebM video block. Returns true only when
|
|
216
|
+
* the block was swapped; false means the caller should keep the GIF (image).
|
|
217
|
+
*/
|
|
218
|
+
private async convertGifToVideoBlock(file: File): Promise<boolean> {
|
|
219
|
+
this.converting = true;
|
|
220
|
+
this.state = 'LOADING';
|
|
221
|
+
this.errorMessage = null;
|
|
222
|
+
this.brokenImage = false;
|
|
223
|
+
this.lastFileName = file.name;
|
|
224
|
+
this.renderState();
|
|
225
|
+
try {
|
|
226
|
+
const blob = await convertGifToWebm(await file.arrayBuffer(), { onProgress: this.reportProgress });
|
|
227
|
+
if (!blob) { this.converting = false; return false; }
|
|
228
|
+
const webm = new File([blob], file.name.replace(/\.gif$/i, '.webm'), { type: 'video/webm' });
|
|
229
|
+
const url = await this.uploadConverted(webm);
|
|
230
|
+
if (url === null) { this.converting = false; return false; }
|
|
231
|
+
this.converting = false;
|
|
232
|
+
return this.swapToVideoBlock(url, webm.name);
|
|
233
|
+
} catch {
|
|
234
|
+
this.converting = false;
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
private async uploadConverted(file: File): Promise<string | null> {
|
|
240
|
+
const upload = resolveConvertedUploader(this.api, this.config);
|
|
241
|
+
if (upload) {
|
|
242
|
+
const res = await upload(file, { onProgress: this.reportProgress });
|
|
243
|
+
return res.url;
|
|
244
|
+
}
|
|
245
|
+
return URL.createObjectURL(file);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
private swapToVideoBlock(url: string, fileName: string): boolean {
|
|
249
|
+
const index = this.api.blocks.getBlockIndex(this.block.id);
|
|
250
|
+
if (index === undefined) return false;
|
|
251
|
+
this.api.blocks.insert(
|
|
252
|
+
'video',
|
|
253
|
+
{ url, autoplay: true, loop: true, mimeType: 'video/webm', fileName },
|
|
254
|
+
{}, index, false, true,
|
|
255
|
+
);
|
|
256
|
+
return true;
|
|
257
|
+
}
|
|
258
|
+
|
|
189
259
|
private startUrl(url: string): void {
|
|
260
|
+
if (this.shouldConvertGifUrl(url)) {
|
|
261
|
+
void this.convertGifUrlToVideoBlock(url).then((handled) => {
|
|
262
|
+
if (!handled) this.loadImageUrl(url);
|
|
263
|
+
});
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
this.loadImageUrl(url);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/** Existing startUrl body, extracted verbatim. */
|
|
270
|
+
private loadImageUrl(url: string): void {
|
|
190
271
|
this.lastFileName = null;
|
|
191
272
|
this.lastSource = { kind: 'url', url };
|
|
192
273
|
this.state = 'LOADING';
|
|
@@ -199,6 +280,41 @@ export class ImageTool implements BlockTool {
|
|
|
199
280
|
.catch((err) => this.applyError(err));
|
|
200
281
|
}
|
|
201
282
|
|
|
283
|
+
private shouldConvertGifUrl(url: string): boolean {
|
|
284
|
+
return this.config.convertGifToVideo !== false && /\.gif(\?|#|$)/i.test(url);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
private async convertGifUrlToVideoBlock(url: string): Promise<boolean> {
|
|
288
|
+
const bytes = await this.fetchGifBytes(url);
|
|
289
|
+
if (!bytes) return false;
|
|
290
|
+
this.converting = true;
|
|
291
|
+
this.state = 'LOADING';
|
|
292
|
+
this.errorMessage = null;
|
|
293
|
+
this.renderState();
|
|
294
|
+
try {
|
|
295
|
+
const blob = await convertGifToWebm(bytes, { onProgress: this.reportProgress });
|
|
296
|
+
if (!blob) { this.converting = false; return false; }
|
|
297
|
+
const webm = new File([blob], 'animation.webm', { type: 'video/webm' });
|
|
298
|
+
const uploadedUrl = await this.uploadConverted(webm);
|
|
299
|
+
if (uploadedUrl === null) { this.converting = false; return false; }
|
|
300
|
+
this.converting = false;
|
|
301
|
+
return this.swapToVideoBlock(uploadedUrl, webm.name);
|
|
302
|
+
} catch {
|
|
303
|
+
this.converting = false;
|
|
304
|
+
return false;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
private async fetchGifBytes(url: string): Promise<ArrayBuffer | null> {
|
|
309
|
+
try {
|
|
310
|
+
const res = await fetch(url);
|
|
311
|
+
if (!res.ok) return null;
|
|
312
|
+
return await res.arrayBuffer();
|
|
313
|
+
} catch {
|
|
314
|
+
return null; // CORS / network → keep the GIF as an image URL
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
202
318
|
private retryLastSource(): void {
|
|
203
319
|
const source = this.lastSource;
|
|
204
320
|
if (!source) {
|
|
@@ -231,7 +347,12 @@ export class ImageTool implements BlockTool {
|
|
|
231
347
|
|
|
232
348
|
private applyError(err: unknown): void {
|
|
233
349
|
this.state = 'ERROR';
|
|
234
|
-
this.errorMessage = err instanceof
|
|
350
|
+
this.errorMessage = err instanceof ImageError
|
|
351
|
+
? uploadErrorMessage(err, (key) => this.api.i18n.t(key), {
|
|
352
|
+
tooLarge: 'tools.image.errorFileTooLarge',
|
|
353
|
+
generic: 'tools.image.errorUploadFailed',
|
|
354
|
+
})
|
|
355
|
+
: this.api.i18n.t('tools.image.errorUploadFailed');
|
|
235
356
|
this.brokenImage = false;
|
|
236
357
|
this.retrying = false;
|
|
237
358
|
this.renderState();
|
|
@@ -565,7 +686,7 @@ export class ImageTool implements BlockTool {
|
|
|
565
686
|
onFile: (file) => this.startUpload(file),
|
|
566
687
|
onUrl: (url) => this.startUrl(url),
|
|
567
688
|
acceptTypes: this.config.types,
|
|
568
|
-
maxSize: this.config.maxSize,
|
|
689
|
+
maxSize: pickDisplayMaxSize(this.config.maxSize),
|
|
569
690
|
i18n: this.api.i18n,
|
|
570
691
|
});
|
|
571
692
|
this.emptyStateEl = el;
|
|
@@ -578,6 +699,7 @@ export class ImageTool implements BlockTool {
|
|
|
578
699
|
fileName: this.lastFileName ?? this.api.i18n.t('tools.image.uploading'),
|
|
579
700
|
onCancel: () => this.transitionToEmpty(),
|
|
580
701
|
i18n: this.api.i18n,
|
|
702
|
+
...(this.converting ? { statusLabel: this.api.i18n.t('tools.image.converting') } : {}),
|
|
581
703
|
});
|
|
582
704
|
this.uploadingEl = el;
|
|
583
705
|
this.root.appendChild(el);
|
|
@@ -41,13 +41,26 @@ export function clampPercent(value: number, minPercent: number = MIN_WIDTH_PERCE
|
|
|
41
41
|
return value;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
export interface WidthResult {
|
|
45
|
+
/** The clamped width as a percent of the container. */
|
|
46
|
+
percent: number;
|
|
47
|
+
/**
|
|
48
|
+
* True when the drag wanted to shrink past the lower floor and was pinned
|
|
49
|
+
* there — used to surface a "can't go smaller" cue to the user.
|
|
50
|
+
*/
|
|
51
|
+
clampedToMin: boolean;
|
|
52
|
+
}
|
|
53
|
+
|
|
44
54
|
/**
|
|
45
55
|
* Pointer follows the dragged edge; width grows around the alignment anchor.
|
|
46
56
|
* Center (frac 0.5) → both edges move → width delta = 2× pointer delta.
|
|
47
57
|
* Edge-anchored (frac 0 or 1) → single side grows → width delta = 1× pointer delta.
|
|
58
|
+
*
|
|
59
|
+
* Returns the clamped percent plus whether it hit the lower floor, so callers
|
|
60
|
+
* can animate a resistance cue when the player refuses to shrink further.
|
|
48
61
|
*/
|
|
49
|
-
export function
|
|
50
|
-
if (input.containerWidth <= 0) return clampPercent(0);
|
|
62
|
+
export function computeWidthResult(input: ComputeWidthInput): WidthResult {
|
|
63
|
+
if (input.containerWidth <= 0) return { percent: clampPercent(0), clampedToMin: true };
|
|
51
64
|
const frac = input.alignFrac ?? 0;
|
|
52
65
|
const denom = input.edge === 'right' ? 1 - frac : frac;
|
|
53
66
|
const multiplier = denom === 0 ? 1 : 1 / denom;
|
|
@@ -57,9 +70,23 @@ export function computeWidthPercent(input: ComputeWidthInput): number {
|
|
|
57
70
|
const minPercent = input.minWidthPx !== undefined
|
|
58
71
|
? Math.round((input.minWidthPx / input.containerWidth) * 100)
|
|
59
72
|
: MIN_WIDTH_PERCENT;
|
|
60
|
-
|
|
73
|
+
const floor = Math.min(Math.max(minPercent, MIN_WIDTH_PERCENT), MAX_WIDTH_PERCENT);
|
|
74
|
+
const raw = Math.round((nextWidth / input.containerWidth) * 100);
|
|
75
|
+
return { percent: clampPercent(raw, minPercent), clampedToMin: raw < floor };
|
|
61
76
|
}
|
|
62
77
|
|
|
78
|
+
export function computeWidthPercent(input: ComputeWidthInput): number {
|
|
79
|
+
return computeWidthResult(input).percent;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Marker set on the figure while a drag is pinned at the lower floor. Shared
|
|
84
|
+
* resize-cue CSS keys off it to recoil the player/image/embed and neutralise
|
|
85
|
+
* the dragged handle, so every resizable tool gets the "can't shrink further"
|
|
86
|
+
* feedback for free.
|
|
87
|
+
*/
|
|
88
|
+
export const RESIZE_BLOCKED_ATTR = 'data-resize-blocked';
|
|
89
|
+
|
|
63
90
|
export interface AttachResizeHandleOptions {
|
|
64
91
|
handle: HTMLElement;
|
|
65
92
|
figure: HTMLElement;
|
|
@@ -103,7 +130,7 @@ export function attachResizeHandle(opts: AttachResizeHandleOptions): () => void
|
|
|
103
130
|
|
|
104
131
|
const onMove = (event: PointerEvent): void => {
|
|
105
132
|
if (!state.active) return;
|
|
106
|
-
const
|
|
133
|
+
const result = computeWidthResult({
|
|
107
134
|
edge: opts.edge,
|
|
108
135
|
containerWidth: state.containerWidth,
|
|
109
136
|
startWidth: state.startWidth,
|
|
@@ -112,13 +139,19 @@ export function attachResizeHandle(opts: AttachResizeHandleOptions): () => void
|
|
|
112
139
|
alignFrac,
|
|
113
140
|
minWidthPx: opts.minWidthPx,
|
|
114
141
|
});
|
|
115
|
-
state.lastPercent =
|
|
116
|
-
|
|
142
|
+
state.lastPercent = result.percent;
|
|
143
|
+
if (result.clampedToMin) {
|
|
144
|
+
opts.figure.setAttribute(RESIZE_BLOCKED_ATTR, 'true');
|
|
145
|
+
} else {
|
|
146
|
+
opts.figure.removeAttribute(RESIZE_BLOCKED_ATTR);
|
|
147
|
+
}
|
|
148
|
+
opts.onPreview(result.percent);
|
|
117
149
|
};
|
|
118
150
|
|
|
119
151
|
const onUp = (event: PointerEvent): void => {
|
|
120
152
|
if (!state.active) return;
|
|
121
153
|
state.active = false;
|
|
154
|
+
opts.figure.removeAttribute(RESIZE_BLOCKED_ATTR);
|
|
122
155
|
opts.handle.releasePointerCapture(event.pointerId);
|
|
123
156
|
if (state.lastPercent !== undefined) opts.onCommit(state.lastPercent);
|
|
124
157
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ImageConfig } from '../../../types/tools/image';
|
|
2
|
+
import { resolveMaxSize } from '../../components/utils/max-size';
|
|
2
3
|
import { DEFAULT_MAX_SIZE, DEFAULT_MIME_TYPES } from './constants';
|
|
3
4
|
import { ImageError } from './errors';
|
|
4
5
|
|
|
@@ -87,7 +88,7 @@ export class Uploader {
|
|
|
87
88
|
|
|
88
89
|
private validateFile(file: File): void {
|
|
89
90
|
const types = this.config.types ?? [...DEFAULT_MIME_TYPES];
|
|
90
|
-
const maxSize = this.config.maxSize
|
|
91
|
+
const maxSize = resolveMaxSize(this.config.maxSize, file.type, DEFAULT_MAX_SIZE);
|
|
91
92
|
|
|
92
93
|
if (!types.includes(file.type)) {
|
|
93
94
|
throw new ImageError('UNSUPPORTED_TYPE', file.type || 'unknown');
|
|
@@ -5,6 +5,7 @@ import { tr } from './i18n';
|
|
|
5
5
|
export interface UploadingStateOptions {
|
|
6
6
|
fileName: string;
|
|
7
7
|
sizeLabel?: string;
|
|
8
|
+
statusLabel?: string;
|
|
8
9
|
onCancel?(): void;
|
|
9
10
|
i18n?: I18nInstance;
|
|
10
11
|
}
|
|
@@ -33,7 +34,7 @@ export function renderUploadingState(opts: UploadingStateOptions): UploadingStat
|
|
|
33
34
|
|
|
34
35
|
const label = document.createElement('span');
|
|
35
36
|
label.className = 'blok-image-uploading__label';
|
|
36
|
-
label.textContent = tr(opts.i18n, 'tools.image.uploadingLabel');
|
|
37
|
+
label.textContent = opts.statusLabel ?? tr(opts.i18n, 'tools.image.uploadingLabel');
|
|
37
38
|
|
|
38
39
|
const name = document.createElement('span');
|
|
39
40
|
name.className = 'blok-image-uploading__filename';
|