@jant/core 0.3.40 → 0.3.42
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/README.md +21 -34
- package/bin/commands/assets/upload.js +247 -0
- package/bin/commands/collections.js +268 -0
- package/bin/commands/import-site.js +1169 -343
- package/bin/commands/media.js +302 -0
- package/bin/commands/migrate-text-attachments.js +183 -0
- package/bin/commands/migrate.js +263 -2
- package/bin/commands/posts.js +262 -0
- package/bin/commands/search.js +53 -0
- package/bin/commands/settings.js +93 -0
- package/bin/commands/site/export.js +34 -33
- package/bin/commands/site/{localize-media.js → pull-media.js} +17 -17
- package/bin/lib/d1-query.js +15 -3
- package/bin/lib/http-api.js +223 -0
- package/bin/lib/media-upload.js +206 -0
- package/bin/lib/migration-runner.js +64 -0
- package/bin/lib/runtime-target.js +7 -2
- package/bin/lib/site-media-parser.js +71 -16
- package/bin/lib/site-pull-media.js +698 -0
- package/bin/lib/site-snapshot.js +0 -1
- package/dist/{app-CAtsuLLh.js → app-Cu3lveYI.js} +23768 -23057
- package/dist/app-DzCB4yOp.js +5 -0
- package/dist/client/.vite/manifest.json +3485 -0
- package/dist/client/_assets/chunks/02611a045a7fe83a12014e3debc9f731-BqEDtwXC.woff2 +0 -0
- package/dist/client/_assets/chunks/033466ef683afe931f7f520cfb42d928-BluLHEh9.woff2 +0 -0
- package/dist/client/_assets/chunks/04b8bde59cff68eeee74fb1914eb09c7-D5j2CLTt.woff2 +0 -0
- package/dist/client/_assets/chunks/057a6a98bda7fe57105ddaa99ec82015-DY_AoijT.woff2 +0 -0
- package/dist/client/_assets/chunks/05ac821472e235943ed1435e4bb8ecad-CUm8GiH6.woff2 +0 -0
- package/dist/client/_assets/chunks/05da12edb9d52210581dc6ec4541031f-BXAOx2XP.woff2 +0 -0
- package/dist/client/_assets/chunks/074c1f8483d5a4d8c45c8c5f3e4cbb32-CDqdMQm8.woff2 +0 -0
- package/dist/client/_assets/chunks/0968e4861204b51f62a2f8e9f15dd5e0-D5tLgF_8.woff2 +0 -0
- package/dist/client/_assets/chunks/0acd1fe2b2ea1ad1bfee7ae1fa139d27-BKK1m412.woff2 +0 -0
- package/dist/client/_assets/chunks/0c2ab6b295e55f356f8020d4e7747522-CkCQjQw3.woff2 +0 -0
- package/dist/client/_assets/chunks/0c5f9492af03a4fa42c784de94649de1-CQlJqksa.woff2 +0 -0
- package/dist/client/_assets/chunks/0d5dec931dc885f07fe5cd5af8bed675-C74x-U_U.woff2 +0 -0
- package/dist/client/_assets/chunks/0d9a936885a4c39077438effd3779cbd-Bw0fYF4S.woff2 +0 -0
- package/dist/client/_assets/chunks/0e645da524f7cfc0e8c3c03fb2b08428-BVeI_5Th.woff2 +0 -0
- package/dist/client/_assets/chunks/0f5e1a18987dbc84ca05188c129e1936-B6_lZ6fE.woff2 +0 -0
- package/dist/client/_assets/chunks/1076f0f6f66d28d7a2f16427faad4413-ubiXnD1P.woff2 +0 -0
- package/dist/client/_assets/chunks/112743a4ab5fdd1498dfdf2b11336380-B5FzQxTK.woff2 +0 -0
- package/dist/client/_assets/chunks/1211f03d3ff5759a702631445793f60e-DDcSiyCt.woff2 +0 -0
- package/dist/client/_assets/chunks/12ef1ba76bd20b004b865266a1aa70b3-BwmaD87S.woff2 +0 -0
- package/dist/client/_assets/chunks/135e83b403475c5dc9e49b43501a5b84-C5mE_KZS.woff2 +0 -0
- package/dist/client/_assets/chunks/1380210a722ac9aeef54005ad7b015c9-CjA-MP82.woff2 +0 -0
- package/dist/client/_assets/chunks/13b2c53b30e6a3e4de7132dbc18dbdfc-B9WOB7kv.woff2 +0 -0
- package/dist/client/_assets/chunks/147b24c2f08d03bbed30887d4cb3311c-IeeFCJ13.woff2 +0 -0
- package/dist/client/_assets/chunks/152395634a207579552f8cb54db88599-C2y2JyNZ.woff2 +0 -0
- package/dist/client/_assets/chunks/15d95680dc31cc6ce20e0a5464106dc4-CpD8dqRm.woff2 +0 -0
- package/dist/client/_assets/chunks/16663e567f81f4725a1522f37e18f71f-BNoxS9pQ.woff2 +0 -0
- package/dist/client/_assets/chunks/1733f27476507ca68b68a803bc533cc2-DXL-14Ug.woff2 +0 -0
- package/dist/client/_assets/chunks/17c7f5d0a45e92ede0e5dec3a2c77efa-CNj3jQMl.woff2 +0 -0
- package/dist/client/_assets/chunks/1824321da801e6257b902f3d1d09054c-D4dgdyWB.woff2 +0 -0
- package/dist/client/_assets/chunks/188c2db794f3dd7a45889ddbc81da9bb-C9NbUBBO.woff2 +0 -0
- package/dist/client/_assets/chunks/1a08931435f885e707edb85978ea3bb6-BOiP2u7G.woff2 +0 -0
- package/dist/client/_assets/chunks/1a3a92c7c060a71a6c35819b9ebcdbb9-DFfonQfz.woff2 +0 -0
- package/dist/client/_assets/chunks/1be602ad456b0d75902d352116cb35fd-Bc-XQtQh.woff2 +0 -0
- package/dist/client/_assets/chunks/1cf737900dd49c2e88f1b3221a82a602-CfTmnns3.woff2 +0 -0
- package/dist/client/_assets/chunks/1fba7ec0e412e911bf31841de5a8a4e4-18ELMbFF.woff2 +0 -0
- package/dist/client/_assets/chunks/233d3a685ee18276b319363474599d47-BaFdJ0-G.woff2 +0 -0
- package/dist/client/_assets/chunks/2612c60f9dc5459ac42763591240a1d6-CTD69cq1.woff2 +0 -0
- package/dist/client/_assets/chunks/284b53bbefb06924cf236d24c8ed5641-CPDiuOv6.woff2 +0 -0
- package/dist/client/_assets/chunks/28a97af9ab9d38983d20f39ff09840e1-mFqcT3Jw.woff2 +0 -0
- package/dist/client/_assets/chunks/2a56eaf19d1d38a6b57e2a388f733676-CU52qBlE.woff2 +0 -0
- package/dist/client/_assets/chunks/2ab8cf1f23a5ac7939a7876054e711a7-B4T3dK7A.woff2 +0 -0
- package/dist/client/_assets/chunks/2abfbab82b6a7c04426afc054d2464da-D5PIIHde.woff2 +0 -0
- package/dist/client/_assets/chunks/2bfaadaf3479c72286248e6de0be0ec9-DEpU6O74.woff2 +0 -0
- package/dist/client/_assets/chunks/2c8c55e4cec85ce77e95cac9d330f5cf-BCTA96EZ.woff2 +0 -0
- package/dist/client/_assets/chunks/2e6f4bb71ef6b38765d51acc5a79f638-Mbs6PECD.woff2 +0 -0
- package/dist/client/_assets/chunks/2ed9981d2e8983365bd051159b976b6d-D8gg6NWM.woff2 +0 -0
- package/dist/client/_assets/chunks/2f4c633e923ba30c6ba376367379fc91-CIJCFztv.woff2 +0 -0
- package/dist/client/_assets/chunks/31194d303a67561926a544ed0e072aee-w3-eGYAi.woff2 +0 -0
- package/dist/client/_assets/chunks/31a197213ae61d7043c81013f52d10d6-DXZrrkJP.woff2 +0 -0
- package/dist/client/_assets/chunks/349965eee0a9b6c984a319ab96a4ece9-DByAVmXw.woff2 +0 -0
- package/dist/client/_assets/chunks/34e6750e00c3a911ef87dc66b336594d-DNbWesOu.woff2 +0 -0
- package/dist/client/_assets/chunks/3623d96698132c61e861f984ce11cc23-jqV9ErzX.woff2 +0 -0
- package/dist/client/_assets/chunks/36cda3eae13370b934bba4429fab9078-BPDcUlw9.woff2 +0 -0
- package/dist/client/_assets/chunks/36f3df4730cfca4fc77fe52b52e6a126-BwK10NC0.woff2 +0 -0
- package/dist/client/_assets/chunks/37887deb7a9c466cf2af6ee7ff372ea6-BrAerJB1.woff2 +0 -0
- package/dist/client/_assets/chunks/37dbd564820cce2f7b3331e442da0df3-Cql2kzIi.woff2 +0 -0
- package/dist/client/_assets/chunks/37dc8505b20b37a2477a9121b16d44a7-CoXw6XWV.woff2 +0 -0
- package/dist/client/_assets/chunks/39211b02f24c69cbd1b54f6d41b01933-4Xu7vvnq.woff2 +0 -0
- package/dist/client/_assets/chunks/3aa13f1843d7c8fa16109b6395a9a29d-BQFZUc3B.woff2 +0 -0
- package/dist/client/_assets/chunks/3aba89c4d4281553078de4622b498cbb-8QS1zDYk.woff2 +0 -0
- package/dist/client/_assets/chunks/3ac0a07878cacdc98bb889f40f5970d3-C-BaM6-y.woff2 +0 -0
- package/dist/client/_assets/chunks/3d88558097559d775231194d43745ba7-CR2aqGC7.woff2 +0 -0
- package/dist/client/_assets/chunks/3dc3e8c35524cad2d1236f3393104ccf-9sqBsF7B.woff2 +0 -0
- package/dist/client/_assets/chunks/3e24063b19abd2b051af986a6870046d-TlweM3SW.woff2 +0 -0
- package/dist/client/_assets/chunks/3e2c06bdd9dcab29aeaeb0cfb7fa608a-BaqU-dIt.woff2 +0 -0
- package/dist/client/_assets/chunks/424123f9371ba357087363263eeafcda-C8SVfEJi.woff2 +0 -0
- package/dist/client/_assets/chunks/43b3bbf43055c3e65db9cdf2c5b18c1c-CbHx6-Ki.woff2 +0 -0
- package/dist/client/_assets/chunks/4410f876d27c9821839474b6b1059fd7-ClYz91aY.woff2 +0 -0
- package/dist/client/_assets/chunks/44704471b60c18b3ecec5300a88f1102-DDmmy6-m.woff2 +0 -0
- package/dist/client/_assets/chunks/45140742dfa8686b58db2899ce89f89a-D5IMMzDM.woff2 +0 -0
- package/dist/client/_assets/chunks/46604090efbb99c2db1800928af0a239-JX2fpYX_.woff2 +0 -0
- package/dist/client/_assets/chunks/46998df85d31e629c0142f0556f5e7c5-BIkNG9Wh.woff2 +0 -0
- package/dist/client/_assets/chunks/469d2754e315e77270c12fa0ab28026c-D-y-vsfA.woff2 +0 -0
- package/dist/client/_assets/chunks/479329a39a3eb6c0047e6b0981919855-vZKCRx42.woff2 +0 -0
- package/dist/client/_assets/chunks/491bbba374093fff045f55803a22bfef-DSQXiDCd.woff2 +0 -0
- package/dist/client/_assets/chunks/49902f1c41b281f8eb3771abdd4dccb8-DxVxNg2W.woff2 +0 -0
- package/dist/client/_assets/chunks/4c0760284569b87d8f2290c324953973-Bqd-TKHH.woff2 +0 -0
- package/dist/client/_assets/chunks/4cba92c942694bdc479bc54101bb1aa4-BrfsrgrC.woff2 +0 -0
- package/dist/client/_assets/chunks/4d38e56738156625329d93bd5c8cc74a-DONkMP0y.woff2 +0 -0
- package/dist/client/_assets/chunks/4db4f31a16965baa42cb7dad667a8f04-Dt1Cy4gr.woff2 +0 -0
- package/dist/client/_assets/chunks/4e7d263c30bb48604069a3b4404d4eeb-DNPCkvKD.woff2 +0 -0
- package/dist/client/_assets/chunks/5279c7e465d9871edf51fd02e691eee7-CKhyhvVf.woff2 +0 -0
- package/dist/client/_assets/chunks/527ff336279465617aafbafe1a830632-CC3lv4pm.woff2 +0 -0
- package/dist/client/_assets/chunks/54bfdb6f21f9d6191e5100580239e14f-PGlqYvgl.woff2 +0 -0
- package/dist/client/_assets/chunks/56b40518ea0608e62826bd44ee27f5f9-BD9F9Oss.woff2 +0 -0
- package/dist/client/_assets/chunks/56be5e32fd01668fe5ba814d94905839-CSyFA-9l.woff2 +0 -0
- package/dist/client/_assets/chunks/572b9e28f1e6d89b765a16d809db84a1-BIYMjzEa.woff2 +0 -0
- package/dist/client/_assets/chunks/57e9a86651366c1ba299e47aa7e358c2-BHuZXB4W.woff2 +0 -0
- package/dist/client/_assets/chunks/57f89adeae55aa7fe2add3fc1801ce03-Cy3PNrfx.woff2 +0 -0
- package/dist/client/_assets/chunks/5921cef36750ff6092d94b83a802823b-hF-uymTh.woff2 +0 -0
- package/dist/client/_assets/chunks/59e95d8b5332dcdae894a19946ab767d-ClRfsjWZ.woff2 +0 -0
- package/dist/client/_assets/chunks/5acc7f736217259db79a42bf44241c48-FYQUuFOM.woff2 +0 -0
- package/dist/client/_assets/chunks/5c05f8b75c9d7f25b3c04ca711cf43b3-D-YWPkSq.woff2 +0 -0
- package/dist/client/_assets/chunks/5df9ce98c75f8c50fb4fff7f01a23925-DgoCENyW.woff2 +0 -0
- package/dist/client/_assets/chunks/5e0124c7265f1b4cf0fc797b94efbebc-BTBNHsSB.woff2 +0 -0
- package/dist/client/_assets/chunks/5ec349ff62b9ee5fe0c6dae867704dc4-B4dezhrz.woff2 +0 -0
- package/dist/client/_assets/chunks/60020a830d809c26970cf8e48ba1eeda-B6FDTSik.woff2 +0 -0
- package/dist/client/_assets/chunks/601f812e8b1eafecd5ba585a6a6d7962-gIVBlUU9.woff2 +0 -0
- package/dist/client/_assets/chunks/6025ddaf99d86cc8bfd781006b19fbd6-CS-ze337.woff2 +0 -0
- package/dist/client/_assets/chunks/60bcfa3ea7446eb42394aab02d28be40-C-PQfCu3.woff2 +0 -0
- package/dist/client/_assets/chunks/60f6e28b3f0400b0e8fe32c3c7116102-Dag9L2qU.woff2 +0 -0
- package/dist/client/_assets/chunks/615a7e0d63f257109d599b2a1977de68-D6Y93yoG.woff2 +0 -0
- package/dist/client/_assets/chunks/615b38e0b5bb3ea68426f44f47706e6f-B5yzIIer.woff2 +0 -0
- package/dist/client/_assets/chunks/61a2c80d0c924d5a6187c02e8d1d1642-BY3MmHbZ.woff2 +0 -0
- package/dist/client/_assets/chunks/62fc94f8d4c5a750b7f25e7387539910-3ql2_WDK.woff2 +0 -0
- package/dist/client/_assets/chunks/639c7c6b0b0c27c738702741cfa4b8c0-BCD3OGri.woff2 +0 -0
- package/dist/client/_assets/chunks/63fafcb069520613d0ea35ad3e6b1e42-DP0Rrj4o.woff2 +0 -0
- package/dist/client/_assets/chunks/64a404d675f1d726f0891b7a80794595-DU3L0I9s.woff2 +0 -0
- package/dist/client/_assets/chunks/65a60d87c64228d258a123cbe85f5f31-B4kbf-79.woff2 +0 -0
- package/dist/client/_assets/chunks/65e9c4585d71bf48a5c62f367010da16-CvzgzReN.woff2 +0 -0
- package/dist/client/_assets/chunks/68a3a4bf337f8a0722be76676e20b850-Dnaw8EPD.woff2 +0 -0
- package/dist/client/_assets/chunks/6968e889e18891d912809fe484c2e745-Bj1m5e0s.woff2 +0 -0
- package/dist/client/_assets/chunks/6abdd163d2c4b85698db8aa1ce149361-B5FVGJuo.woff2 +0 -0
- package/dist/client/_assets/chunks/6b75213eb0be40ce84241eb2bb438a2e-CqHmF-NR.woff2 +0 -0
- package/dist/client/_assets/chunks/70714891ad3fbfc3d5f10a8669dacc5a-BWk5nLFN.woff2 +0 -0
- package/dist/client/_assets/chunks/7158022889c6c177062ac85036e7af10-B3D4K22n.woff2 +0 -0
- package/dist/client/_assets/chunks/73b794d885b88f7befb7ea8ab1780a15-D0ZehnEI.woff2 +0 -0
- package/dist/client/_assets/chunks/743f290baf027d4626a86e22f3d44600-DDTDQdRC.woff2 +0 -0
- package/dist/client/_assets/chunks/7520ca6ca8c60eb1e62d50e71c8d30f1-CVRaGu2G.woff2 +0 -0
- package/dist/client/_assets/chunks/757019c01c5e2c2b0f54293cea3b5636-BTovUnoH.woff2 +0 -0
- package/dist/client/_assets/chunks/759a647b77791b1e98f99bc0ab5317a7-CrK6FIFz.woff2 +0 -0
- package/dist/client/_assets/chunks/761d84afca8d7f34eebefe538adba827-DRbiVuwX.woff2 +0 -0
- package/dist/client/_assets/chunks/76e07530323418ec723c5d7634c9bcca-DGtVJTTH.woff2 +0 -0
- package/dist/client/_assets/chunks/76e51630143b95b6322ef93ad330da7a-D-kV82C_.woff2 +0 -0
- package/dist/client/_assets/chunks/76f117a3baacda304a71965a17266a13-DFfvsFUm.woff2 +0 -0
- package/dist/client/_assets/chunks/773819b7b9b8fd404a929867c0fd677e-Bu1xCMZb.woff2 +0 -0
- package/dist/client/_assets/chunks/77747f17625a259fb405b7bbdd84ac4b-o_vNfh-6.woff2 +0 -0
- package/dist/client/_assets/chunks/7817dd16805145d8538ad57590f69f5a-DguSUP3O.woff2 +0 -0
- package/dist/client/_assets/chunks/788498548ddfb710d6ef3c7bff79fa97-3ffoQfRd.woff2 +0 -0
- package/dist/client/_assets/chunks/78ea6c40923ce95367e3517dd1e5a849-DvLDRhX9.woff2 +0 -0
- package/dist/client/_assets/chunks/79aa7c8c842c4a27cf57c0a3a1ffca5a-sDoCeoKI.woff2 +0 -0
- package/dist/client/_assets/chunks/7b59f0ec7792b18458dc5a361e37884c-DWvxkKmT.woff2 +0 -0
- package/dist/client/_assets/chunks/7c3945788a689a69356c1a622d69d48b-1JjIpss2.woff2 +0 -0
- package/dist/client/_assets/chunks/7cc72fd2c9105560422b6a67c6162945-Co3AF5_G.woff2 +0 -0
- package/dist/client/_assets/chunks/7d0ea0690e432462f4d05a23d720ec19-Dx7DK4C3.woff2 +0 -0
- package/dist/client/_assets/chunks/7dffa5c1bec57e0903fd62357401ff1a-DuQHmiiN.woff2 +0 -0
- package/dist/client/_assets/chunks/7e38ca789a9e76ac91d9256374e154f0-BtETgGRe.woff2 +0 -0
- package/dist/client/_assets/chunks/7f9a6c9286b68de9c72b0024f7beeb40-JpXdo4FC.woff2 +0 -0
- package/dist/client/_assets/chunks/815a0b797465190f60d8b1a04656c42e-Spzo94nU.woff2 +0 -0
- package/dist/client/_assets/chunks/823d1bb11097331238d103c7f72138dc-CGN6m2VF.woff2 +0 -0
- package/dist/client/_assets/chunks/82f6ccc063960eed1cdfd1d61ada8862-CsmAqx2a.woff2 +0 -0
- package/dist/client/_assets/chunks/835b6505bb9eea9678925a1fa885353d-C6mSBwUc.woff2 +0 -0
- package/dist/client/_assets/chunks/845b4b67564d62cf5cad242f7ac08ea5-DnvzrloJ.woff2 +0 -0
- package/dist/client/_assets/chunks/84c8e7bc0931008ed91f44ed12dfea94-C2Hvhh-b.woff2 +0 -0
- package/dist/client/_assets/chunks/8726558cf297cbda831cc19514897205-gSFV1vW0.woff2 +0 -0
- package/dist/client/_assets/chunks/8855472f3c02f6c7ebb3216617ebe4af-C1JsWVqq.woff2 +0 -0
- package/dist/client/_assets/chunks/887a11290ba78b1e66c6d2f67043005e-L_A1f5Fz.woff2 +0 -0
- package/dist/client/_assets/chunks/888ba53510213d5d1f6a7deb60e569b6-OP8E-ELN.woff2 +0 -0
- package/dist/client/_assets/chunks/88db1d042074fb6e66821ffc10941930-kZ6K3e62.woff2 +0 -0
- package/dist/client/_assets/chunks/892aa49b2529c89bb4076d4aa51fe30f-Bw0_Km2A.woff2 +0 -0
- package/dist/client/_assets/chunks/8b5c9ef81159f31d2ab35f45ca4373e0-Djw95sKC.woff2 +0 -0
- package/dist/client/_assets/chunks/8b91c7c2ed390f1278b9befa3aa87233-B2C3iFbQ.woff2 +0 -0
- package/dist/client/_assets/chunks/8dc18c0cebe6aa4bf4c45dbb831048ab-bUlANLit.woff2 +0 -0
- package/dist/client/_assets/chunks/8e231d707f0c4f8e3cde90a6b52a79aa-CvwbWlul.woff2 +0 -0
- package/dist/client/_assets/chunks/8fb45117a62d92dce44a80f0b729ead5-Djr4-o5-.woff2 +0 -0
- package/dist/client/_assets/chunks/9064c12fd72c7aba235d8c3881755b91-BnguxT-p.woff2 +0 -0
- package/dist/client/_assets/chunks/90b7848e9b1623b77bdcf155e90b839c-CFvPRnoM.woff2 +0 -0
- package/dist/client/_assets/chunks/911b9e53e9814de2998c60bf3115f560-DmPmj635.woff2 +0 -0
- package/dist/client/_assets/chunks/919a879bd2d580d8491a31a449390689-BY2w-8_Y.woff2 +0 -0
- package/dist/client/_assets/chunks/91da6cb174bebfb96976e2f1316be2fe-DztAJb6W.woff2 +0 -0
- package/dist/client/_assets/chunks/92fdc376bce277874e75db666f175b44-DJUnHEIN.woff2 +0 -0
- package/dist/client/_assets/chunks/930a23430f0eb64480d7fe5f82834e21-2VrDPT7E.woff2 +0 -0
- package/dist/client/_assets/chunks/934dfdbed2bb2c4b6129199d699a34fa-CtpHsjRD.woff2 +0 -0
- package/dist/client/_assets/chunks/93bed0e5f2dd5a21cf73304fcfbc149d-DJiaETX7.woff2 +0 -0
- package/dist/client/_assets/chunks/93f3ea6918533d96d9c252378b9a4af0-D-FJaGi-.woff2 +0 -0
- package/dist/client/_assets/chunks/95a0951d2a2722ff773a1a45e8c474d6-DuZg-BpP.woff2 +0 -0
- package/dist/client/_assets/chunks/95ff41191c76ef893ac70a1043e95e44-C08QDbJO.woff2 +0 -0
- package/dist/client/_assets/chunks/96c75862c8cec51a7c05ff025fea86cd-DBnRwCuW.woff2 +0 -0
- package/dist/client/_assets/chunks/97d7a17234f2b5030ae9697ae00aded2-CkUSaB-p.woff2 +0 -0
- package/dist/client/_assets/chunks/982cd1765ca6bbb3e6547e47834d5148-DkIybx1r.woff2 +0 -0
- package/dist/client/_assets/chunks/985d4d41afa0934a4eb2de35473fb9a2-CF7rgJMw.woff2 +0 -0
- package/dist/client/_assets/chunks/995c4fda62d25c3b79dd5987737df6ae-CY5ri8HW.woff2 +0 -0
- package/dist/client/_assets/chunks/9b0c3caac458c7bc9c9133e40405ddea-DhPrYOcz.woff2 +0 -0
- package/dist/client/_assets/chunks/9b21e8244f5930c48ad5073f83777b6c-CuE9-r-r.woff2 +0 -0
- package/dist/client/_assets/chunks/9bb38fd05201de666b88b82d56a386f7-nMl6lB8X.woff2 +0 -0
- package/dist/client/_assets/chunks/9d0afa53dc2f92ba42024f55f11f6779-Bozpx8bL.woff2 +0 -0
- package/dist/client/_assets/chunks/9e13a47242926f1cd7d68c08d9ef8889-DwKQjc8M.woff2 +0 -0
- package/dist/client/_assets/chunks/9eff2ab2a9d2ced47ab761bf6fdb11ec-D86-Zm1H.woff2 +0 -0
- package/dist/client/_assets/chunks/9f1963fe8d4d6878d717d872a8f3fdb5-DxjITUFq.woff2 +0 -0
- package/dist/client/_assets/chunks/9f3bdab4025dc7c5ba1a5871e62e8918-BqxDEFLJ.woff2 +0 -0
- package/dist/client/_assets/chunks/9fce02d1a09f464c36c9fcfb14a354c5-F2tMAB_j.woff2 +0 -0
- package/dist/client/_assets/chunks/a00e94d4f04df6aa659db9c4954c7efe-CRBKCj_3.woff2 +0 -0
- package/dist/client/_assets/chunks/a0a6755c7e3f9e4cf03387027dd9f16c-BAyRqF2w.woff2 +0 -0
- package/dist/client/_assets/chunks/a0d00fe4816c95a8c7dffd445ef00b03-CxcTiRLJ.woff2 +0 -0
- package/dist/client/_assets/chunks/a0f199077fa1a33bc2a1e01c64de7fe6-IdgVJmZD.woff2 +0 -0
- package/dist/client/_assets/chunks/a2b0597837e382aca19919c4b74e58c8-7iVOBjzI.woff2 +0 -0
- package/dist/client/_assets/chunks/a373e85c96aa0dc303092429fb837e51-Dvmw8mbX.woff2 +0 -0
- package/dist/client/_assets/chunks/a379ada89abd750c587ded29f77e731c-CgPBsEsv.woff2 +0 -0
- package/dist/client/_assets/chunks/a4475c442c6b259354c9eda62e64e7df-TZ0c8vK-.woff2 +0 -0
- package/dist/client/_assets/chunks/a48be56ce5d3a99dc8f8331398004412-BR_ixH1W.woff2 +0 -0
- package/dist/client/_assets/chunks/a4a39b5f048671aa37c2b2a9581ab08a-EDfDYsUd.woff2 +0 -0
- package/dist/client/_assets/chunks/a534449d67561ac9b06489f64e57ad98-C355TCrD.woff2 +0 -0
- package/dist/client/_assets/chunks/a6e044424780a57610833cc856c15bf5-x1b8FE4H.woff2 +0 -0
- package/dist/client/_assets/chunks/a7c35e42a659347e490c3cb7983b2e7f-BWpA0-oz.woff2 +0 -0
- package/dist/client/_assets/chunks/a7c58070d68dc1724e9d4e781afb78db-ql27pT-6.woff2 +0 -0
- package/dist/client/_assets/chunks/a806759e72987951d6d08b7cbdf36a1b-TvLDwB_u.woff2 +0 -0
- package/dist/client/_assets/chunks/a88481ec8bc7be11cb66e46781a79bb9-Ck-r_sGx.woff2 +0 -0
- package/dist/client/_assets/chunks/a919a8c6fec6d7e8bb21177965f2e9d7-CC5uWzwd.woff2 +0 -0
- package/dist/client/_assets/chunks/a9a016d4a93409f65278327e8a8bb38d-dylrNwF8.woff2 +0 -0
- package/dist/client/_assets/chunks/aa662092cf249707123ca4d575e2764b-CSxNsTUL.woff2 +0 -0
- package/dist/client/_assets/chunks/aae1eda193285ab817a2d1b408440326-CjyjBoRm.woff2 +0 -0
- package/dist/client/_assets/chunks/ab92d41062a7644faa45f50bf384454a-Cq0X8a9H.woff2 +0 -0
- package/dist/client/_assets/chunks/accc43762ad05edfbff66ba1380d192a-B5jIykaZ.woff2 +0 -0
- package/dist/client/_assets/chunks/ad47bcd6d4663026d648c132d969318d-BYdIbDge.woff2 +0 -0
- package/dist/client/_assets/chunks/ad67113f88b59582991cb0c5d33ea19f-DOH1vcIZ.woff2 +0 -0
- package/dist/client/_assets/chunks/adb6e613fb99c6dd660b727101f554a8-DJDw_ZZK.woff2 +0 -0
- package/dist/client/_assets/chunks/adc4a98a89870ef984ee8f4b57c8a3a5-D30vw26R.woff2 +0 -0
- package/dist/client/_assets/chunks/af57a2e8c72f9ba0efb1af771d32c124-D5Zwq9pS.woff2 +0 -0
- package/dist/client/_assets/chunks/b0ae5f374e43dac992a4a5d334cebc0b-Ccoi4dPt.woff2 +0 -0
- package/dist/client/_assets/chunks/b0cb664cb2e1371efda8943c0b7dcd1c-YzGFnelF.woff2 +0 -0
- package/dist/client/_assets/chunks/b0d1cdced482352cf0d3ae58638aacb9-IunWfc6R.woff2 +0 -0
- package/dist/client/_assets/chunks/b12379ab782468d725519cd07a7d15bc-IjbydKs2.woff2 +0 -0
- package/dist/client/_assets/chunks/b320f5d185b2cff933ac549c184031c5-IYnPrIz4.woff2 +0 -0
- package/dist/client/_assets/chunks/b326a8a9c33b554db570da94f60bc380-B6dzaCQg.woff2 +0 -0
- package/dist/client/_assets/chunks/b55486f8a459c838fe329d4e79a8c211-DW8q7oGV.woff2 +0 -0
- package/dist/client/_assets/chunks/b6117ff2993b11bb1fdc7ea3588a010c-JoMYL5W_.woff2 +0 -0
- package/dist/client/_assets/chunks/b61982951bd51b724143c30dfaaa9fe9-D3KhBKl6.woff2 +0 -0
- package/dist/client/_assets/chunks/b91234c10fd8b8c8abc88e03afe66a1f-BABNh2la.woff2 +0 -0
- package/dist/client/_assets/chunks/b9317198f118a1dfd8ddf2b82ec028f3-CiMecxeH.woff2 +0 -0
- package/dist/client/_assets/chunks/ba825ae79b1a6f7e0cce5215fcb5c96f-B6EWU_D5.woff2 +0 -0
- package/dist/client/_assets/chunks/ba9c59d7dfa4494db1bb764ada81467d-D0yPcC1m.woff2 +0 -0
- package/dist/client/_assets/chunks/bab547459d514f46206e340c4bb2dc88-YnBqlGhw.woff2 +0 -0
- package/dist/client/_assets/chunks/bb0b15492b8cdbbec57c0bcfa0aa9241-BjIFbEsN.woff2 +0 -0
- package/dist/client/_assets/chunks/bc6183ac08f0fac78c46f80c10cf7c92-B81nbRb3.woff2 +0 -0
- package/dist/client/_assets/chunks/bc8ccea5abf6598cf3cfa97eb59804bb-B_7IbluG.woff2 +0 -0
- package/dist/client/_assets/chunks/bc95a792cbca6639214c9b0da13392ff-CHQe0E-D.woff2 +0 -0
- package/dist/client/_assets/chunks/bcc39eda837bb7a7a3d37c8c60fffb81-CGd1Zk6S.woff2 +0 -0
- package/dist/client/_assets/chunks/bcfa62f35731856246c146d3a6932bf3-DMDq6w4v.woff2 +0 -0
- package/dist/client/_assets/chunks/bd73264d7f98776708d5d6f3c9b78fcc-d9QWWMym.woff2 +0 -0
- package/dist/client/_assets/chunks/bed610b217d500f5975cfc9fe6157570-B5t7s1bw.woff2 +0 -0
- package/dist/client/_assets/chunks/bedc74b423b7293b6ad0bdecc61c42cc-4Nnh7peP.woff2 +0 -0
- package/dist/client/_assets/chunks/bf8901f8f11d4f433ea17dabc9370ea6-B8dkfvGn.woff2 +0 -0
- package/dist/client/_assets/chunks/bfb00d4a4c48661bd0be99f300a0faae-OxsWZ7l-.woff2 +0 -0
- package/dist/client/_assets/chunks/c0098958e20db68cab90097b5e62516f-VpjQo3vV.woff2 +0 -0
- package/dist/client/_assets/chunks/c063897793f593eb26d6ff7b7baaba18-BWeJJ1sH.woff2 +0 -0
- package/dist/client/_assets/chunks/c1b0df29ae41d764904df84e9ac83d1e-D1N934vs.woff2 +0 -0
- package/dist/client/_assets/chunks/c4143bb9f2fe77b6ccf20088a8904650-nHfYM24e.woff2 +0 -0
- package/dist/client/_assets/chunks/c42c67070cfe99cf823df92d81c7fa6e-B_a405Ez.woff2 +0 -0
- package/dist/client/_assets/chunks/c4bfaa5e50798246e3770718b7a7c84a-B-r6Y_o0.woff2 +0 -0
- package/dist/client/_assets/chunks/c4d749e45ecd5a5aed5a0bb3ebfd355d-CkqE1fRi.woff2 +0 -0
- package/dist/client/_assets/chunks/c6c2971ad1f3221f6cf84028aa0f477e-D7BZGIcY.woff2 +0 -0
- package/dist/client/_assets/chunks/c7ff3f6bbdcd5f604b7343602ab904df-Bq7UjVrZ.woff2 +0 -0
- package/dist/client/_assets/chunks/c84598999133455503042e06f4ab79cb-DIXndJX3.woff2 +0 -0
- package/dist/client/_assets/chunks/c945c62368357d05a53206620460fb30-82gFEf6n.woff2 +0 -0
- package/dist/client/_assets/chunks/c96d83978add28b356c22c4c84916733-BL4VaPIE.woff2 +0 -0
- package/dist/client/_assets/chunks/c97f41eef722121d86f55d553c056a39-CjejgQO2.woff2 +0 -0
- package/dist/client/_assets/chunks/ca62704509932d3232d62918de97af3f-CKWF-3mX.woff2 +0 -0
- package/dist/client/_assets/chunks/ca9a533988d7019597a60d4e17127e0c-ClZ6ygZM.woff2 +0 -0
- package/dist/client/_assets/chunks/cb7ccd6494256f7a2977b7c2b0225592-Cxp8XObm.woff2 +0 -0
- package/dist/client/_assets/chunks/ce022e18a1377ac509443c3c3790b431-DpH0t6Tn.woff2 +0 -0
- package/dist/client/_assets/chunks/ce41d70ce6a069a498525c9e15c45cf2-DGtgv-_b.woff2 +0 -0
- package/dist/client/_assets/chunks/cf2b28f90f47276f7e2688a65e88a101-LVbNkXHj.woff2 +0 -0
- package/dist/client/_assets/chunks/cf9cffe56636322f62b40d61130fbc5e-DNCBMqAo.woff2 +0 -0
- package/dist/client/_assets/chunks/d1f064825fa5784b5c930652bd831cce-BU6qwjp8.woff2 +0 -0
- package/dist/client/_assets/chunks/d20d8944bc0b85f5b2aae4b24f343516-oY9l-NmC.woff2 +0 -0
- package/dist/client/_assets/chunks/d2faabcedd19f016e7b21bce073c0ec8-Dv6eFCf5.woff2 +0 -0
- package/dist/client/_assets/chunks/d320171e57480510f87dbbc7d5264b0a-CcCfjaFX.woff2 +0 -0
- package/dist/client/_assets/chunks/d36644d6502527e1fff205d0c7eca434-kVBj8zy_.woff2 +0 -0
- package/dist/client/_assets/chunks/d3a72a99d365dddfbca8d017a8011368-Bgdmhl3M.woff2 +0 -0
- package/dist/client/_assets/chunks/d40ab99c7a38026f411c8f112f742b48-D7Nm7fg6.woff2 +0 -0
- package/dist/client/_assets/chunks/d42aafa0f246ad3b4c16fe96d3a3a432-CRxTqZXU.woff2 +0 -0
- package/dist/client/_assets/chunks/d4aaf23c13ae808a4bed617afd13aa07-d_T1SKe9.woff2 +0 -0
- package/dist/client/_assets/chunks/d509ee5c8241bc7c3de2039d75564fa5-BpNFD9JE.woff2 +0 -0
- package/dist/client/_assets/chunks/d675d717cd329bfd4c0524f76ae1579c-Bd5_ctoQ.woff2 +0 -0
- package/dist/client/_assets/chunks/d689b1861d7e4377dd72ad3013482612-jPsVOU3F.woff2 +0 -0
- package/dist/client/_assets/chunks/d6bb686bddfbe8f38a36a68e609a8667-C_QduS-X.woff2 +0 -0
- package/dist/client/_assets/chunks/d9020ff69a83b2a6ee1f42ae480f7db0-DLBUPMCe.woff2 +0 -0
- package/dist/client/_assets/chunks/d9047070d72a816b3dba9d40c2d85e69-LTQ42vEp.woff2 +0 -0
- package/dist/client/_assets/chunks/da04549f3f4ed28076b01b8cd710d313-CDXOeduw.woff2 +0 -0
- package/dist/client/_assets/chunks/da7af303f8c645f9a9dbae0e6e32dd35-DCkS2jqi.woff2 +0 -0
- package/dist/client/_assets/chunks/da90a9012ab2d98f759e3fa0820ef502-D0D9d1zg.woff2 +0 -0
- package/dist/client/_assets/chunks/dc6e234ded795e91f76d6647f628fbf0-IkooXcRh.woff2 +0 -0
- package/dist/client/_assets/chunks/dc8f6256445e68199540be9ade33529f-umb5tWbZ.woff2 +0 -0
- package/dist/client/_assets/chunks/dd0cfdfdac0866e66d587e2b5a9e9961-bl4ozE0c.woff2 +0 -0
- package/dist/client/_assets/chunks/de304c8a02e45ded4f8dcc479d167198-ClRu89-4.woff2 +0 -0
- package/dist/client/_assets/chunks/df10d94bea357a43313e20da5d84bd30-CmAgh2oA.woff2 +0 -0
- package/dist/client/_assets/chunks/df9568257eb29b156449fdd4bec5ec76-BMz6aLRr.woff2 +0 -0
- package/dist/client/_assets/chunks/e067cd0ed76c90cd0a93c9339253f20b-6RxCQ4jy.woff2 +0 -0
- package/dist/client/_assets/chunks/e08b07772e7bed3cec2832d43f7fd339-CQCkeKsr.woff2 +0 -0
- package/dist/client/_assets/chunks/e12150d5a39b30be8f567968c7a527b0-DjdoWzeQ.woff2 +0 -0
- package/dist/client/_assets/chunks/e34b2b141e472dc776c86fdf8eea23b0-Cjk7H-Ev.woff2 +0 -0
- package/dist/client/_assets/chunks/e444fd88f1390636e603d0d681538ac8-Bx7CPOZh.woff2 +0 -0
- package/dist/client/_assets/chunks/e53fcb2381eee345db4f6f973dd95a3e-DmkkCUWz.woff2 +0 -0
- package/dist/client/_assets/chunks/e5bd313ef81f687d398aacb11cec3069-CwBEbcji.woff2 +0 -0
- package/dist/client/_assets/chunks/e79898628283edc27180fc39d0d769c1-By2pplQz.woff2 +0 -0
- package/dist/client/_assets/chunks/e8c15be62faf978d208925e79ea6a10d-Ct5M9lTP.woff2 +0 -0
- package/dist/client/_assets/chunks/e8c364c16daa04835bf32d293d2598db-3v4Tut_d.woff2 +0 -0
- package/dist/client/_assets/chunks/e91c4d941ed9f5c2f3e27f205a3a225e-BwOJ2Lst.woff2 +0 -0
- package/dist/client/_assets/chunks/e9c566be7a5d38a9085225f7372bc82b-BCtnCVML.woff2 +0 -0
- package/dist/client/_assets/chunks/e9ebc567a711eeb29019ddae3e0ce7fe-CnNhV49O.woff2 +0 -0
- package/dist/client/_assets/chunks/eaf332445a40942928e5f5750c1c3116-qZ5LSRrj.woff2 +0 -0
- package/dist/client/_assets/chunks/eb5afb3d952b8593782caec6026514b6-BXugZF2T.woff2 +0 -0
- package/dist/client/_assets/chunks/ec86e23683052da5cfdc3b77641bd15a-BeLcbcRc.woff2 +0 -0
- package/dist/client/_assets/chunks/ecb8875a56c7b038b35432fda41ae128-BVqgNf1o.woff2 +0 -0
- package/dist/client/_assets/chunks/eda1b0cb6d1719dd9bedcf3216a9e8de-BJTfLL5z.woff2 +0 -0
- package/dist/client/_assets/chunks/edd6a4f608d04fc0351d7688cfc321e4-d3V4U40c.woff2 +0 -0
- package/dist/client/_assets/chunks/ee6fce20b420a480714607c66d7f97e5-DEtC-G5G.woff2 +0 -0
- package/dist/client/_assets/chunks/eee3836d6ac17ebb2c450bbcbc9db121-C5a2TZjQ.woff2 +0 -0
- package/dist/client/_assets/chunks/et-book-bold-line-figures-BFJr2_zv.woff +0 -0
- package/dist/client/_assets/chunks/et-book-display-italic-old-style-figures-CoeRJAe8.woff +0 -0
- package/dist/client/_assets/chunks/et-book-roman-line-figures-CaA40oOf.woff +0 -0
- package/dist/client/_assets/chunks/f1661731474b78bdf81114daf10b254f-KnMKX_U3.woff2 +0 -0
- package/dist/client/_assets/chunks/f2c58dee206ba9355046fc23d05491f7-Cm4tCWav.woff2 +0 -0
- package/dist/client/_assets/chunks/f50ac27ea4358d67fdda403c2bb52467-WCfYHde1.woff2 +0 -0
- package/dist/client/_assets/chunks/f55334112f8e8be82d65db29887a663f-vDq1zH8k.woff2 +0 -0
- package/dist/client/_assets/chunks/f5738255e92d8dd34a46d1bcdf4c4074-tpghTzL1.woff2 +0 -0
- package/dist/client/_assets/chunks/f740e93f3c0277ecc616594103bca683-C4BgW_ko.woff2 +0 -0
- package/dist/client/_assets/chunks/f7633b5af033d76ff2fb3c3c266d77c5-BCkrZxwO.woff2 +0 -0
- package/dist/client/_assets/chunks/f7d8468cba2335a83ee414ea68291bab-CVnotkZU.woff2 +0 -0
- package/dist/client/_assets/chunks/f91cd722855f4269256eae1187df64ec-DJlAx1A_.woff2 +0 -0
- package/dist/client/_assets/chunks/f9695c6c4df2bf6bc03045ff79d4f01f-DDaYGYHT.woff2 +0 -0
- package/dist/client/_assets/chunks/fa6e58ce4b52695e7ae19bbea6336ec8-B30jEpze.woff2 +0 -0
- package/dist/client/_assets/chunks/fada6eaa68ff8816afe43d2a36c5423e-DlGAWNPb.woff2 +0 -0
- package/dist/client/_assets/chunks/fbcc4bf5367218951172bdee6f77d7a6-Dax2VSCd.woff2 +0 -0
- package/dist/client/_assets/chunks/fc895f5ce66b656f4a933097bf2a8775-DjjysNdf.woff2 +0 -0
- package/dist/client/_assets/chunks/fc9ae5b600fb711f2d67e93ce768cba4-D1lx7LBz.woff2 +0 -0
- package/dist/client/_assets/chunks/fca6720fd14c467d29a90f18ef3859b9-B3btrwq3.woff2 +0 -0
- package/dist/client/_assets/chunks/fd1bb507bcbf04856eeb7f7fd47ea579-NR1nGJTt.woff2 +0 -0
- package/dist/client/_assets/chunks/fd1bceb55d3e0183ac2454b8532fec7d-D3t-v6nn.woff2 +0 -0
- package/dist/client/_assets/chunks/fd2069e6e8588c70b4f11364093b81f2-BoXhYVgw.woff2 +0 -0
- package/dist/client/_assets/chunks/{url-CG0eolsk.js → url-pLre2DM_.js} +1 -1
- package/dist/client/_assets/client-C_kImWZj.css +2 -0
- package/dist/client/_assets/client-D95FNDg5.js +272 -0
- package/dist/client/_assets/client-auth-BRFl5zQA.js +4547 -0
- package/dist/client/_assets/client-cjk-jp-DZwrTzQC.css +1 -0
- package/dist/client/_assets/client-cjk-kr-_3ZNI2ZP.css +1 -0
- package/dist/env-wCpMcNXs.js +252 -0
- package/dist/github-api-CficQztC.js +176 -0
- package/dist/github-app-F4qZ05xk.js +275 -0
- package/dist/github-sync-zohnA9qv.js +4696 -0
- package/dist/index.js +88 -2
- package/dist/node.js +71 -30
- package/dist/url-FvvgARU9.js +305 -0
- package/package.json +16 -12
- package/src/__tests__/bin/content-cli.test.ts +179 -0
- package/src/__tests__/bin/media-cli.test.ts +192 -0
- package/src/__tests__/export-hugo-build.test.ts +178 -0
- package/src/__tests__/export-import-roundtrip.test.ts +265 -0
- package/src/__tests__/export-service.test.ts +763 -510
- package/src/__tests__/helpers/export-fixtures.ts +108 -0
- package/src/__tests__/import-site-command.test.ts +358 -350
- package/src/__tests__/site-pull-media.test.ts +256 -0
- package/src/app.tsx +60 -13
- package/src/client/__tests__/collection-form-bridge.test.ts +87 -7
- package/src/client/__tests__/collection-page-actions.test.ts +4 -4
- package/src/client/__tests__/collection-picker-order.test.ts +53 -0
- package/src/client/__tests__/collection-sort-menu.test.ts +2 -2
- package/src/client/__tests__/compose-bridge.test.ts +305 -13
- package/src/client/__tests__/compose-launch.test.ts +120 -0
- package/src/client/__tests__/compose-shortcuts.test.ts +141 -0
- package/src/client/__tests__/feed-video-player.test.ts +44 -0
- package/src/client/__tests__/site-header-nav.test.ts +250 -0
- package/src/client/__tests__/sortable-list.test.ts +6 -2
- package/src/client/__tests__/thread-context.test.ts +117 -0
- package/src/client/collection-form-bridge.ts +109 -4
- package/src/client/collection-page-actions.ts +3 -1
- package/src/client/collection-picker-order.ts +104 -0
- package/src/client/components/__tests__/{jant-collection-sidebar.test.ts → jant-collection-directory.test.ts} +91 -10
- package/src/client/components/__tests__/jant-collection-form.test.ts +40 -18
- package/src/client/components/__tests__/jant-compose-dialog.test.ts +1285 -66
- package/src/client/components/__tests__/jant-compose-editor.test.ts +254 -19
- package/src/client/components/__tests__/jant-compose-fullscreen.test.ts +127 -1
- package/src/client/components/__tests__/jant-media-lightbox.test.ts +237 -0
- package/src/client/components/__tests__/jant-nav-manager.test.ts +326 -0
- package/src/client/components/__tests__/jant-post-menu.test.ts +372 -2
- package/src/client/components/__tests__/jant-settings-avatar.test.ts +61 -5
- package/src/client/components/__tests__/jant-settings-general.test.ts +29 -28
- package/src/client/components/__tests__/jant-text-preview.test.ts +206 -0
- package/src/client/components/collection-manager-types.ts +18 -1
- package/src/client/components/compose-types.ts +48 -0
- package/src/client/components/jant-collection-directory.ts +1448 -0
- package/src/client/components/jant-collection-form.ts +65 -17
- package/src/client/components/jant-command-palette.ts +544 -0
- package/src/client/components/jant-compose-dialog.ts +2490 -375
- package/src/client/components/jant-compose-editor.ts +420 -75
- package/src/client/components/jant-compose-fullscreen.ts +59 -8
- package/src/client/components/jant-media-lightbox.ts +373 -18
- package/src/client/components/jant-nav-manager.ts +624 -286
- package/src/client/components/jant-post-menu.ts +451 -126
- package/src/client/components/jant-repo-picker-types.ts +39 -0
- package/src/client/components/jant-repo-picker.ts +799 -0
- package/src/client/components/jant-settings-avatar.ts +16 -2
- package/src/client/components/jant-settings-general.ts +137 -35
- package/src/client/components/jant-text-preview.ts +239 -55
- package/src/client/components/nav-manager-types.ts +25 -9
- package/src/client/components/settings-types.ts +13 -2
- package/src/client/compose-bridge.ts +428 -68
- package/src/client/compose-launch.ts +41 -4
- package/src/client/compose-shortcuts.ts +77 -5
- package/src/client/feed-video-player.ts +374 -0
- package/src/client/image-processor.ts +53 -127
- package/src/client/media-lightbox-events.ts +1 -0
- package/src/client/media-scroll-hint.ts +66 -0
- package/src/client/multipart-upload.ts +4 -1
- package/src/client/palette-shortcuts.ts +35 -0
- package/src/client/search-rank.ts +74 -0
- package/src/client/settings-bridge.ts +7 -0
- package/src/client/site-header-nav.d.ts +1 -0
- package/src/client/site-header-nav.js +139 -51
- package/src/client/sortable-list.ts +2 -0
- package/src/client/thread-context.ts +54 -11
- package/src/client/tiptap/__tests__/block-insertion.test.ts +65 -0
- package/src/client/tiptap/__tests__/footnotes.test.ts +692 -0
- package/src/client/tiptap/__tests__/link-toolbar.test.ts +120 -4
- package/src/client/tiptap/__tests__/markdown-clipboard.test.ts +48 -0
- package/src/client/tiptap/block-insertion.ts +36 -0
- package/src/client/tiptap/bubble-menu.ts +34 -4
- package/src/client/tiptap/create-editor.ts +49 -30
- package/src/client/tiptap/embed-dialog.ts +301 -0
- package/src/client/tiptap/embed-node.ts +373 -0
- package/src/client/tiptap/embed-paste.ts +91 -0
- package/src/client/tiptap/extensions.ts +77 -28
- package/src/client/tiptap/footnotes.ts +718 -0
- package/src/client/tiptap/html-block-node.ts +222 -0
- package/src/client/tiptap/image-node.ts +49 -6
- package/src/client/tiptap/inline-image-upload.ts +200 -40
- package/src/client/tiptap/link-input-rules.ts +50 -16
- package/src/client/tiptap/link-toolbar.ts +156 -133
- package/src/client/tiptap/markdown-clipboard.ts +120 -0
- package/src/client/tiptap/more-break.ts +36 -3
- package/src/client/tiptap/paste-media.ts +6 -0
- package/src/client/tiptap/slash-commands.ts +167 -14
- package/src/client/tiptap/tab-indent.ts +184 -0
- package/src/client/tiptap/toolbar-mode.ts +15 -2
- package/src/client/tiptap/wrapping-input-rules.ts +102 -0
- package/src/client/types/sortablejs.d.ts +9 -1
- package/src/client/upload-session.ts +2 -1
- package/src/client/video-processor.ts +177 -20
- package/src/client-auth.ts +9 -1
- package/src/client-site.ts +15 -0
- package/src/client.ts +2 -0
- package/src/db/__tests__/demo-canonical-snapshot.test.ts +1 -1
- package/src/db/__tests__/migration-rehearsal.test.ts +5 -1
- package/src/db/__tests__/migrations.test.ts +25 -2
- package/src/db/backfills/0001_strip_collection_path_c_prefix.sql +11 -0
- package/src/db/backfills/0002_strip_collection_path_c_prefix_fix.sql +12 -0
- package/src/db/backfills/0003_clear_system_nav_default_labels.sql +15 -0
- package/src/db/dialect.ts +40 -0
- package/src/db/migrations/0005_busy_the_phantom.sql +39 -0
- package/src/db/migrations/0006_adorable_magdalene.sql +3 -0
- package/src/db/migrations/0007_unusual_warstar.sql +1 -0
- package/src/db/migrations/0008_nasty_lockheed.sql +1 -0
- package/src/db/migrations/0009_clear_fixer.sql +31 -0
- package/src/db/migrations/0010_futuristic_preak.sql +31 -0
- package/src/db/migrations/0011_bizarre_smasher.sql +30 -0
- package/src/db/migrations/0012_furry_thena.sql +52 -0
- package/src/db/migrations/0013_mixed_lightspeed.sql +39 -0
- package/src/db/migrations/0014_high_the_santerians.sql +41 -0
- package/src/db/migrations/0015_skinny_shinobi_shaw.sql +1 -0
- package/src/db/migrations/0016_remarkable_nicolaos.sql +17 -0
- package/src/db/migrations/0017_powerful_moonstone.sql +14 -0
- package/src/db/migrations/meta/0005_snapshot.json +1904 -0
- package/src/db/migrations/meta/0006_snapshot.json +1925 -0
- package/src/db/migrations/meta/0007_snapshot.json +1933 -0
- package/src/db/migrations/meta/0008_snapshot.json +1940 -0
- package/src/db/migrations/meta/0009_snapshot.json +1952 -0
- package/src/db/migrations/meta/0010_snapshot.json +1952 -0
- package/src/db/migrations/meta/0011_snapshot.json +1948 -0
- package/src/db/migrations/meta/0012_snapshot.json +1955 -0
- package/src/db/migrations/meta/0013_snapshot.json +1977 -0
- package/src/db/migrations/meta/0014_snapshot.json +1988 -0
- package/src/db/migrations/meta/0015_snapshot.json +1995 -0
- package/src/db/migrations/meta/0016_snapshot.json +2104 -0
- package/src/db/migrations/meta/0017_snapshot.json +2188 -0
- package/src/db/migrations/meta/_journal.json +92 -1
- package/src/db/migrations/pg/0003_motionless_norrin_radd.sql +21 -0
- package/src/db/migrations/pg/0004_nervous_captain_midlands.sql +3 -0
- package/src/db/migrations/pg/0005_romantic_mesmero.sql +1 -0
- package/src/db/migrations/pg/0006_perpetual_bruce_banner.sql +1 -0
- package/src/db/migrations/pg/0007_nav_item_placement.sql +1 -0
- package/src/db/migrations/pg/0008_yielding_frightful_four.sql +3 -0
- package/src/db/migrations/pg/0009_outstanding_ogun.sql +1 -0
- package/src/db/migrations/pg/0010_overjoyed_gertrude_yorkes.sql +28 -0
- package/src/db/migrations/pg/0011_fixed_hulk.sql +19 -0
- package/src/db/migrations/pg/0012_cute_shockwave.sql +2 -0
- package/src/db/migrations/pg/0013_bizarre_obadiah_stane.sql +1 -0
- package/src/db/migrations/pg/0014_tearful_grim_reaper.sql +16 -0
- package/src/db/migrations/pg/0015_daffy_mikhail_rasputin.sql +14 -0
- package/src/db/migrations/pg/meta/0003_snapshot.json +2482 -0
- package/src/db/migrations/pg/meta/0004_snapshot.json +2500 -0
- package/src/db/migrations/pg/meta/0005_snapshot.json +2507 -0
- package/src/db/migrations/pg/meta/0006_snapshot.json +2513 -0
- package/src/db/migrations/pg/meta/0008_snapshot.json +2524 -0
- package/src/db/migrations/pg/meta/0009_snapshot.json +2520 -0
- package/src/db/migrations/pg/meta/0010_snapshot.json +2526 -0
- package/src/db/migrations/pg/meta/0011_snapshot.json +2563 -0
- package/src/db/migrations/pg/meta/0012_snapshot.json +2573 -0
- package/src/db/migrations/pg/meta/0013_snapshot.json +2579 -0
- package/src/db/migrations/pg/meta/0014_snapshot.json +2702 -0
- package/src/db/migrations/pg/meta/0015_snapshot.json +2803 -0
- package/src/db/migrations/pg/meta/_journal.json +92 -1
- package/src/db/pg/__tests__/node.test.ts +38 -0
- package/src/db/pg/node.ts +149 -0
- package/src/db/pg/schema.ts +130 -12
- package/src/db/rehearsal-fixtures/demo-current.sql +47 -3
- package/src/db/schema.ts +139 -12
- package/src/i18n/Trans.tsx +3 -3
- package/src/i18n/__tests__/context.test.tsx +9 -6
- package/src/i18n/__tests__/detect.test.ts +65 -48
- package/src/i18n/__tests__/fallback.test.ts +40 -0
- package/src/i18n/__tests__/message-placeholders.test.ts +43 -0
- package/src/i18n/__tests__/middleware.test.ts +83 -0
- package/src/i18n/context.tsx +9 -44
- package/src/i18n/detect.ts +95 -28
- package/src/i18n/i18n.ts +41 -17
- package/src/i18n/index.ts +7 -5
- package/src/i18n/locales/{en.po → public/en.po} +284 -1151
- package/src/i18n/locales/public/en.ts +1 -0
- package/src/i18n/locales/public/zh-Hans.po +2327 -0
- package/src/i18n/locales/public/zh-Hans.ts +1 -0
- package/src/i18n/locales/public/zh-Hant.po +2327 -0
- package/src/i18n/locales/public/zh-Hant.ts +1 -0
- package/src/i18n/locales/settings/en.po +1622 -0
- package/src/i18n/locales/settings/en.ts +1 -0
- package/src/i18n/locales/settings/zh-Hans.po +1622 -0
- package/src/i18n/locales/settings/zh-Hans.ts +1 -0
- package/src/i18n/locales/settings/zh-Hant.po +1622 -0
- package/src/i18n/locales/settings/zh-Hant.ts +1 -0
- package/src/i18n/middleware.ts +37 -24
- package/src/index.ts +4 -5
- package/src/lib/__tests__/collection-groups.test.ts +17 -0
- package/src/lib/__tests__/constants.test.ts +2 -24
- package/src/lib/__tests__/csp-builder.test.ts +68 -0
- package/src/lib/__tests__/display-text.test.ts +27 -0
- package/src/lib/__tests__/embed-providers.test.ts +104 -0
- package/src/lib/__tests__/embed-render.test.ts +60 -0
- package/src/lib/__tests__/feed.test.ts +125 -63
- package/src/lib/__tests__/hosted-signin.test.ts +11 -2
- package/src/lib/__tests__/markdown-roundtrip-embed.test.ts +88 -0
- package/src/lib/__tests__/markdown-to-tiptap.test.ts +59 -0
- package/src/lib/__tests__/markdown.test.ts +47 -5
- package/src/lib/__tests__/navigation.test.ts +148 -0
- package/src/lib/__tests__/post-display.test.ts +3 -2
- package/src/lib/__tests__/post-meta.test.ts +3 -0
- package/src/lib/__tests__/resolve-config.test.ts +30 -25
- package/src/lib/__tests__/schemas.test.ts +20 -3
- package/src/lib/__tests__/summary.test.ts +400 -1
- package/src/lib/__tests__/theme.test.ts +41 -2
- package/src/lib/__tests__/timeline.test.ts +33 -27
- package/src/lib/__tests__/tiptap-render.test.ts +224 -0
- package/src/lib/__tests__/tiptap-to-markdown.test.ts +51 -0
- package/src/lib/__tests__/video-playback.test.ts +48 -0
- package/src/lib/__tests__/view.test.ts +115 -8
- package/src/lib/__tests__/worker-response-cache.test.ts +311 -0
- package/src/lib/__tests__/youtube.test.ts +99 -0
- package/src/lib/api-media.ts +55 -0
- package/src/lib/api-posts.ts +99 -0
- package/src/lib/api-search.ts +47 -0
- package/src/lib/api-settings.ts +122 -0
- package/src/lib/asset-path.ts +20 -2
- package/src/lib/collection-groups.ts +2 -1
- package/src/lib/collection-paths.ts +40 -0
- package/src/lib/constants.ts +3 -24
- package/src/lib/csp-builder.ts +99 -0
- package/src/lib/decorative-quote-mark.ts +11 -0
- package/src/lib/display-text.ts +56 -0
- package/src/lib/embed-providers.ts +289 -0
- package/src/lib/embed-render.ts +151 -0
- package/src/lib/env.ts +70 -1
- package/src/lib/excerpt.ts +11 -2
- package/src/lib/feed.ts +89 -66
- package/src/lib/footnotes.ts +146 -0
- package/src/lib/github-api.ts +423 -0
- package/src/lib/github-app-state.ts +135 -0
- package/src/lib/github-app.ts +487 -0
- package/src/lib/github-sync-queue-handler.ts +69 -0
- package/src/lib/github-sync-site-config.ts +57 -0
- package/src/lib/github-sync-status.tsx +87 -0
- package/src/lib/github-sync-trigger.ts +199 -0
- package/src/lib/github-sync-worker.ts +72 -0
- package/src/lib/hosted-signin.ts +1 -1
- package/src/lib/hugo-markdown.ts +255 -0
- package/src/lib/job-queue-cf.ts +18 -0
- package/src/lib/job-queue-db.ts +149 -0
- package/src/lib/job-queue.ts +35 -0
- package/src/lib/markdown-manager.ts +864 -0
- package/src/lib/markdown-to-tiptap.ts +4 -323
- package/src/lib/markdown.ts +16 -24
- package/src/lib/media-helpers.ts +1 -0
- package/src/lib/navigation.ts +40 -8
- package/src/lib/post-display.ts +10 -22
- package/src/lib/public-storage.ts +6 -1
- package/src/lib/render.tsx +5 -2
- package/src/lib/resolve-config.ts +23 -11
- package/src/lib/rich-image.ts +89 -0
- package/src/lib/schemas.ts +180 -13
- package/src/lib/summary.ts +125 -18
- package/src/lib/theme.ts +119 -0
- package/src/lib/timeline.ts +63 -42
- package/src/lib/tiptap-render.ts +265 -126
- package/src/lib/tiptap-to-markdown.ts +6 -329
- package/src/lib/upload.ts +97 -6
- package/src/lib/version.ts +18 -0
- package/src/lib/video-playback.ts +73 -0
- package/src/lib/view.ts +130 -90
- package/src/lib/webhook-signature.ts +65 -0
- package/src/lib/worker-response-cache.ts +220 -0
- package/src/lib/youtube.ts +119 -0
- package/src/middleware/__tests__/onboarding.test.ts +3 -3
- package/src/middleware/__tests__/secure-headers.test.ts +28 -1
- package/src/middleware/config.ts +7 -10
- package/src/middleware/cors.ts +49 -0
- package/src/middleware/secure-headers.ts +45 -55
- package/src/node/__tests__/cli-migrate.test.ts +111 -0
- package/src/node/__tests__/cli-runtime-target.test.ts +1 -1
- package/src/node/__tests__/cli-site-token-env.test.ts +2 -2
- package/src/preset.css +316 -12
- package/src/routes/__tests__/compose.test.ts +3 -1
- package/src/routes/api/__tests__/collections.test.ts +81 -27
- package/src/routes/api/__tests__/mcp.test.ts +389 -0
- package/src/routes/api/__tests__/nav-items.test.ts +163 -2
- package/src/routes/api/__tests__/posts.test.ts +190 -0
- package/src/routes/api/__tests__/settings.test.ts +99 -3
- package/src/routes/api/__tests__/upload-multipart.test.ts +10 -2
- package/src/routes/api/__tests__/upload.test.ts +132 -0
- package/src/routes/api/__tests__/uploads.test.ts +5 -3
- package/src/routes/api/collections.ts +67 -26
- package/src/routes/api/custom-urls.ts +1 -0
- package/src/routes/api/export.ts +8 -4
- package/src/routes/api/github-sync.tsx +380 -0
- package/src/routes/api/internal/__tests__/sites.test.ts +45 -2
- package/src/routes/api/internal/sites.ts +2 -0
- package/src/routes/api/internal/text-attachments.ts +60 -0
- package/src/routes/api/internal/uploads.ts +5 -1
- package/src/routes/api/mcp.ts +30 -0
- package/src/routes/api/nav-items.ts +30 -11
- package/src/routes/api/palette.ts +17 -0
- package/src/routes/api/posts.ts +37 -106
- package/src/routes/api/public/__tests__/posts.test.ts +372 -0
- package/src/routes/api/public/posts.ts +302 -0
- package/src/routes/api/search.ts +1 -47
- package/src/routes/api/settings.ts +44 -58
- package/src/routes/api/upload-multipart.ts +15 -5
- package/src/routes/api/upload.ts +62 -49
- package/src/routes/api/uploads.ts +1 -0
- package/src/routes/auth/__tests__/hosted-sso.test.ts +33 -0
- package/src/routes/auth/__tests__/setup.test.ts +29 -4
- package/src/routes/auth/hosted-sso-expired-page.tsx +89 -0
- package/src/routes/auth/hosted-sso.ts +42 -1
- package/src/routes/auth/reset.tsx +46 -32
- package/src/routes/auth/setup.tsx +53 -43
- package/src/routes/auth/signin.tsx +34 -24
- package/src/routes/compose.tsx +114 -1
- package/src/routes/dash/__tests__/font-theme.test.ts +6 -27
- package/src/routes/dash/__tests__/settings-avatar.test.ts +2 -3
- package/src/routes/dash/custom-urls.tsx +250 -112
- package/src/routes/dash/settings.tsx +1179 -70
- package/src/routes/feed/__tests__/{rss.test.ts → feed.test.ts} +62 -123
- package/src/routes/feed/feed.ts +386 -0
- package/src/routes/feed/manifest.ts +67 -0
- package/src/routes/feed/sitemap.ts +11 -2
- package/src/routes/pages/__tests__/collection-routing.test.ts +176 -0
- package/src/routes/pages/__tests__/collections.test.ts +2 -3
- package/src/routes/pages/__tests__/featured.test.ts +2 -3
- package/src/routes/pages/archive.tsx +523 -127
- package/src/routes/pages/collection.tsx +197 -43
- package/src/routes/pages/collections.tsx +5 -1
- package/src/routes/pages/home.tsx +2 -2
- package/src/routes/pages/latest.tsx +3 -3
- package/src/routes/pages/page.tsx +221 -3
- package/src/routes/pages/partials.tsx +5 -0
- package/src/routes/pages/search.tsx +17 -1
- package/src/routes/pages/theme-sample.tsx +1 -2
- package/src/runtime/__tests__/node.test.ts +1 -1
- package/src/runtime/readiness.ts +1 -1
- package/src/runtime/site.ts +9 -1
- package/src/services/__tests__/collection.test.ts +137 -54
- package/src/services/__tests__/github-app-installations.test.ts +181 -0
- package/src/services/__tests__/github-sync-classify.test.ts +189 -0
- package/src/services/__tests__/github-sync-push.test.ts +159 -0
- package/src/services/__tests__/media.test.ts +444 -7
- package/src/services/__tests__/navigation.test.ts +271 -14
- package/src/services/__tests__/post-timeline.test.ts +31 -21
- package/src/services/__tests__/post.test.ts +83 -9
- package/src/services/__tests__/settings.test.ts +43 -21
- package/src/services/auth.ts +2 -2
- package/src/services/bootstrap.ts +10 -7
- package/src/services/collection.ts +591 -193
- package/src/services/custom-url.ts +34 -7
- package/src/services/export-theme/assets/client-site.css +2 -0
- package/src/services/export-theme/assets/client-site.js +145 -0
- package/src/services/export-theme/layouts/_default/alias.html +49 -0
- package/src/services/export-theme/layouts/_default/baseof.html +21 -0
- package/src/services/export-theme/layouts/_default/list.html +104 -0
- package/src/services/export-theme/layouts/_default/rss.xml +160 -0
- package/src/services/export-theme/layouts/_default/single.html +15 -0
- package/src/services/export-theme/layouts/archive/list.html +31 -0
- package/src/services/export-theme/layouts/collection/single.html +19 -0
- package/src/services/export-theme/layouts/collections/list.html +111 -0
- package/src/services/export-theme/layouts/featured/list.html +28 -0
- package/src/services/export-theme/layouts/index.html +41 -0
- package/src/services/export-theme/layouts/partials/feed-post-content.xml +107 -0
- package/src/services/export-theme/layouts/partials/footer.html +32 -0
- package/src/services/export-theme/layouts/partials/head.html +52 -0
- package/src/services/export-theme/layouts/partials/header.html +115 -0
- package/src/services/export-theme/layouts/partials/media-gallery.html +246 -0
- package/src/services/export-theme/layouts/partials/pagination.html +55 -0
- package/src/services/export-theme/layouts/partials/post-card.html +127 -0
- package/src/services/export-theme/layouts/partials/reply.html +91 -0
- package/src/services/export-theme/layouts/partials/thread-preview.html +82 -0
- package/src/services/export-theme/layouts/post/list.html +129 -0
- package/src/services/export-theme/styles/main.css +1981 -0
- package/src/services/export-theme/theme.toml +12 -0
- package/src/services/export.ts +1365 -2412
- package/src/services/github-app-installations.ts +302 -0
- package/src/services/github-sync.ts +769 -0
- package/src/services/index.ts +15 -0
- package/src/services/mcp.ts +1324 -0
- package/src/services/media.ts +336 -25
- package/src/services/navigation.ts +115 -23
- package/src/services/path.ts +129 -28
- package/src/services/post.ts +658 -195
- package/src/services/search.ts +3 -0
- package/src/services/settings.ts +29 -15
- package/src/services/site-admin.ts +26 -6
- package/src/services/upload-session.ts +69 -34
- package/src/style-cjk-jp.css +3 -0
- package/src/style-cjk-kr.css +3 -0
- package/src/styles/components.css +160 -59
- package/src/styles/fonts/et-book/et-book-bold-line-figures.woff +0 -0
- package/src/styles/fonts/et-book/et-book-display-italic-old-style-figures.woff +0 -0
- package/src/styles/fonts/et-book/et-book-roman-line-figures.woff +0 -0
- package/src/styles/fonts/et-book.css +31 -0
- package/src/styles/fonts/latin.css +1 -0
- package/src/styles/fonts/noto-serif-jp/400/057a6a98bda7fe57105ddaa99ec82015.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/0acd1fe2b2ea1ad1bfee7ae1fa139d27.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/0c2ab6b295e55f356f8020d4e7747522.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/0c5f9492af03a4fa42c784de94649de1.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/0d9a936885a4c39077438effd3779cbd.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/0e645da524f7cfc0e8c3c03fb2b08428.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/135e83b403475c5dc9e49b43501a5b84.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/13b2c53b30e6a3e4de7132dbc18dbdfc.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/15d95680dc31cc6ce20e0a5464106dc4.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/1733f27476507ca68b68a803bc533cc2.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/188c2db794f3dd7a45889ddbc81da9bb.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/1cf737900dd49c2e88f1b3221a82a602.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/233d3a685ee18276b319363474599d47.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/284b53bbefb06924cf236d24c8ed5641.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/2a56eaf19d1d38a6b57e2a388f733676.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/2abfbab82b6a7c04426afc054d2464da.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/2e6f4bb71ef6b38765d51acc5a79f638.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/31194d303a67561926a544ed0e072aee.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/3623d96698132c61e861f984ce11cc23.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/39211b02f24c69cbd1b54f6d41b01933.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/3e24063b19abd2b051af986a6870046d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/4410f876d27c9821839474b6b1059fd7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/479329a39a3eb6c0047e6b0981919855.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/527ff336279465617aafbafe1a830632.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/56b40518ea0608e62826bd44ee27f5f9.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/5acc7f736217259db79a42bf44241c48.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/5c05f8b75c9d7f25b3c04ca711cf43b3.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/5e0124c7265f1b4cf0fc797b94efbebc.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/615a7e0d63f257109d599b2a1977de68.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/61a2c80d0c924d5a6187c02e8d1d1642.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/62fc94f8d4c5a750b7f25e7387539910.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/639c7c6b0b0c27c738702741cfa4b8c0.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/68a3a4bf337f8a0722be76676e20b850.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/6abdd163d2c4b85698db8aa1ce149361.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/73b794d885b88f7befb7ea8ab1780a15.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/743f290baf027d4626a86e22f3d44600.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/757019c01c5e2c2b0f54293cea3b5636.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/76f117a3baacda304a71965a17266a13.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/77747f17625a259fb405b7bbdd84ac4b.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/788498548ddfb710d6ef3c7bff79fa97.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/78ea6c40923ce95367e3517dd1e5a849.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/7f9a6c9286b68de9c72b0024f7beeb40.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/823d1bb11097331238d103c7f72138dc.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/888ba53510213d5d1f6a7deb60e569b6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/892aa49b2529c89bb4076d4aa51fe30f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/95ff41191c76ef893ac70a1043e95e44.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/96c75862c8cec51a7c05ff025fea86cd.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/9b21e8244f5930c48ad5073f83777b6c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/9fce02d1a09f464c36c9fcfb14a354c5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/a0a6755c7e3f9e4cf03387027dd9f16c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/a4475c442c6b259354c9eda62e64e7df.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/a4a39b5f048671aa37c2b2a9581ab08a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/a534449d67561ac9b06489f64e57ad98.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/a919a8c6fec6d7e8bb21177965f2e9d7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/ad67113f88b59582991cb0c5d33ea19f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/adb6e613fb99c6dd660b727101f554a8.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/adc4a98a89870ef984ee8f4b57c8a3a5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/b12379ab782468d725519cd07a7d15bc.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/b55486f8a459c838fe329d4e79a8c211.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/bb0b15492b8cdbbec57c0bcfa0aa9241.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/bedc74b423b7293b6ad0bdecc61c42cc.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/bfb00d4a4c48661bd0be99f300a0faae.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/c1b0df29ae41d764904df84e9ac83d1e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/c42c67070cfe99cf823df92d81c7fa6e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/c4d749e45ecd5a5aed5a0bb3ebfd355d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/c96d83978add28b356c22c4c84916733.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/cb7ccd6494256f7a2977b7c2b0225592.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/ce41d70ce6a069a498525c9e15c45cf2.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/d509ee5c8241bc7c3de2039d75564fa5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/d675d717cd329bfd4c0524f76ae1579c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/dc8f6256445e68199540be9ade33529f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/e8c15be62faf978d208925e79ea6a10d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/e91c4d941ed9f5c2f3e27f205a3a225e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/e9c566be7a5d38a9085225f7372bc82b.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/e9ebc567a711eeb29019ddae3e0ce7fe.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/ecb8875a56c7b038b35432fda41ae128.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/f1661731474b78bdf81114daf10b254f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/f2c58dee206ba9355046fc23d05491f7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/f50ac27ea4358d67fdda403c2bb52467.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/f55334112f8e8be82d65db29887a663f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/f740e93f3c0277ecc616594103bca683.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/f7633b5af033d76ff2fb3c3c266d77c5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/f7d8468cba2335a83ee414ea68291bab.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/fc9ae5b600fb711f2d67e93ce768cba4.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/fd1bceb55d3e0183ac2454b8532fec7d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/02611a045a7fe83a12014e3debc9f731.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/0f5e1a18987dbc84ca05188c129e1936.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/112743a4ab5fdd1498dfdf2b11336380.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/1211f03d3ff5759a702631445793f60e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/147b24c2f08d03bbed30887d4cb3311c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/16663e567f81f4725a1522f37e18f71f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/1824321da801e6257b902f3d1d09054c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/1a3a92c7c060a71a6c35819b9ebcdbb9.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/1fba7ec0e412e911bf31841de5a8a4e4.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/28a97af9ab9d38983d20f39ff09840e1.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/2bfaadaf3479c72286248e6de0be0ec9.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/2c8c55e4cec85ce77e95cac9d330f5cf.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/2ed9981d2e8983365bd051159b976b6d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/2f4c633e923ba30c6ba376367379fc91.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/349965eee0a9b6c984a319ab96a4ece9.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/36cda3eae13370b934bba4429fab9078.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/3aa13f1843d7c8fa16109b6395a9a29d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/3e2c06bdd9dcab29aeaeb0cfb7fa608a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/424123f9371ba357087363263eeafcda.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/43b3bbf43055c3e65db9cdf2c5b18c1c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/44704471b60c18b3ecec5300a88f1102.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/46604090efbb99c2db1800928af0a239.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/46998df85d31e629c0142f0556f5e7c5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/49902f1c41b281f8eb3771abdd4dccb8.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/4db4f31a16965baa42cb7dad667a8f04.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/572b9e28f1e6d89b765a16d809db84a1.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/5921cef36750ff6092d94b83a802823b.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/59e95d8b5332dcdae894a19946ab767d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/5ec349ff62b9ee5fe0c6dae867704dc4.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/601f812e8b1eafecd5ba585a6a6d7962.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/6025ddaf99d86cc8bfd781006b19fbd6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/60f6e28b3f0400b0e8fe32c3c7116102.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/64a404d675f1d726f0891b7a80794595.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/7158022889c6c177062ac85036e7af10.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/7e38ca789a9e76ac91d9256374e154f0.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/815a0b797465190f60d8b1a04656c42e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/8726558cf297cbda831cc19514897205.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/8855472f3c02f6c7ebb3216617ebe4af.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/8e231d707f0c4f8e3cde90a6b52a79aa.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/9064c12fd72c7aba235d8c3881755b91.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/911b9e53e9814de2998c60bf3115f560.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/92fdc376bce277874e75db666f175b44.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/930a23430f0eb64480d7fe5f82834e21.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/93bed0e5f2dd5a21cf73304fcfbc149d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/93f3ea6918533d96d9c252378b9a4af0.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/95a0951d2a2722ff773a1a45e8c474d6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/982cd1765ca6bbb3e6547e47834d5148.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/9b0c3caac458c7bc9c9133e40405ddea.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/9f3bdab4025dc7c5ba1a5871e62e8918.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/a00e94d4f04df6aa659db9c4954c7efe.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/a2b0597837e382aca19919c4b74e58c8.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/a373e85c96aa0dc303092429fb837e51.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/a6e044424780a57610833cc856c15bf5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/a7c58070d68dc1724e9d4e781afb78db.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/accc43762ad05edfbff66ba1380d192a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/ad47bcd6d4663026d648c132d969318d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/af57a2e8c72f9ba0efb1af771d32c124.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/b0ae5f374e43dac992a4a5d334cebc0b.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/b91234c10fd8b8c8abc88e03afe66a1f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/b9317198f118a1dfd8ddf2b82ec028f3.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/ba825ae79b1a6f7e0cce5215fcb5c96f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/bc6183ac08f0fac78c46f80c10cf7c92.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/bc8ccea5abf6598cf3cfa97eb59804bb.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/bc95a792cbca6639214c9b0da13392ff.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/bcfa62f35731856246c146d3a6932bf3.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/c063897793f593eb26d6ff7b7baaba18.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/c6c2971ad1f3221f6cf84028aa0f477e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/c97f41eef722121d86f55d553c056a39.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/ca62704509932d3232d62918de97af3f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/d1f064825fa5784b5c930652bd831cce.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/d2faabcedd19f016e7b21bce073c0ec8.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/d320171e57480510f87dbbc7d5264b0a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/d3a72a99d365dddfbca8d017a8011368.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/d42aafa0f246ad3b4c16fe96d3a3a432.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/d4aaf23c13ae808a4bed617afd13aa07.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/d6bb686bddfbe8f38a36a68e609a8667.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/d9020ff69a83b2a6ee1f42ae480f7db0.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/da90a9012ab2d98f759e3fa0820ef502.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/df10d94bea357a43313e20da5d84bd30.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/e34b2b141e472dc776c86fdf8eea23b0.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/e444fd88f1390636e603d0d681538ac8.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/e79898628283edc27180fc39d0d769c1.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/eaf332445a40942928e5f5750c1c3116.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/f91cd722855f4269256eae1187df64ec.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/fc895f5ce66b656f4a933097bf2a8775.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/fca6720fd14c467d29a90f18ef3859b9.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/fd1bb507bcbf04856eeb7f7fd47ea579.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/noto-serif-jp.css +3349 -0
- package/src/styles/fonts/noto-serif-kr/400/033466ef683afe931f7f520cfb42d928.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/05da12edb9d52210581dc6ec4541031f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/0968e4861204b51f62a2f8e9f15dd5e0.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/0d5dec931dc885f07fe5cd5af8bed675.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/1076f0f6f66d28d7a2f16427faad4413.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/12ef1ba76bd20b004b865266a1aa70b3.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/152395634a207579552f8cb54db88599.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/1a08931435f885e707edb85978ea3bb6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/31a197213ae61d7043c81013f52d10d6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/37dbd564820cce2f7b3331e442da0df3.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/37dc8505b20b37a2477a9121b16d44a7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/3ac0a07878cacdc98bb889f40f5970d3.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/491bbba374093fff045f55803a22bfef.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/4d38e56738156625329d93bd5c8cc74a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/57e9a86651366c1ba299e47aa7e358c2.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/57f89adeae55aa7fe2add3fc1801ce03.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/5df9ce98c75f8c50fb4fff7f01a23925.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/60020a830d809c26970cf8e48ba1eeda.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/60bcfa3ea7446eb42394aab02d28be40.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/63fafcb069520613d0ea35ad3e6b1e42.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/65a60d87c64228d258a123cbe85f5f31.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/6968e889e18891d912809fe484c2e745.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/70714891ad3fbfc3d5f10a8669dacc5a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/761d84afca8d7f34eebefe538adba827.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/76e07530323418ec723c5d7634c9bcca.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/773819b7b9b8fd404a929867c0fd677e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/7817dd16805145d8538ad57590f69f5a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/7b59f0ec7792b18458dc5a361e37884c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/7c3945788a689a69356c1a622d69d48b.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/835b6505bb9eea9678925a1fa885353d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/8b5c9ef81159f31d2ab35f45ca4373e0.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/8b91c7c2ed390f1278b9befa3aa87233.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/8fb45117a62d92dce44a80f0b729ead5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/90b7848e9b1623b77bdcf155e90b839c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/985d4d41afa0934a4eb2de35473fb9a2.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/9bb38fd05201de666b88b82d56a386f7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/9eff2ab2a9d2ced47ab761bf6fdb11ec.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/9f1963fe8d4d6878d717d872a8f3fdb5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/a0d00fe4816c95a8c7dffd445ef00b03.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/a0f199077fa1a33bc2a1e01c64de7fe6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/a7c35e42a659347e490c3cb7983b2e7f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/aa662092cf249707123ca4d575e2764b.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/aae1eda193285ab817a2d1b408440326.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/b0cb664cb2e1371efda8943c0b7dcd1c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/b0d1cdced482352cf0d3ae58638aacb9.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/b320f5d185b2cff933ac549c184031c5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/ba9c59d7dfa4494db1bb764ada81467d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/bab547459d514f46206e340c4bb2dc88.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/bcc39eda837bb7a7a3d37c8c60fffb81.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/bd73264d7f98776708d5d6f3c9b78fcc.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/bed610b217d500f5975cfc9fe6157570.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/bf8901f8f11d4f433ea17dabc9370ea6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/c4143bb9f2fe77b6ccf20088a8904650.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/c7ff3f6bbdcd5f604b7343602ab904df.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/c945c62368357d05a53206620460fb30.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/ce022e18a1377ac509443c3c3790b431.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/cf9cffe56636322f62b40d61130fbc5e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/d20d8944bc0b85f5b2aae4b24f343516.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/d36644d6502527e1fff205d0c7eca434.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/d40ab99c7a38026f411c8f112f742b48.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/dc6e234ded795e91f76d6647f628fbf0.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/dd0cfdfdac0866e66d587e2b5a9e9961.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/de304c8a02e45ded4f8dcc479d167198.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/e8c364c16daa04835bf32d293d2598db.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/ec86e23683052da5cfdc3b77641bd15a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/eda1b0cb6d1719dd9bedcf3216a9e8de.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/ee6fce20b420a480714607c66d7f97e5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/eee3836d6ac17ebb2c450bbcbc9db121.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/f5738255e92d8dd34a46d1bcdf4c4074.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/fa6e58ce4b52695e7ae19bbea6336ec8.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/fd2069e6e8588c70b4f11364093b81f2.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/04b8bde59cff68eeee74fb1914eb09c7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/05ac821472e235943ed1435e4bb8ecad.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/074c1f8483d5a4d8c45c8c5f3e4cbb32.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/1380210a722ac9aeef54005ad7b015c9.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/17c7f5d0a45e92ede0e5dec3a2c77efa.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/1be602ad456b0d75902d352116cb35fd.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/2612c60f9dc5459ac42763591240a1d6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/2ab8cf1f23a5ac7939a7876054e711a7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/34e6750e00c3a911ef87dc66b336594d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/36f3df4730cfca4fc77fe52b52e6a126.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/37887deb7a9c466cf2af6ee7ff372ea6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/3aba89c4d4281553078de4622b498cbb.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/3d88558097559d775231194d43745ba7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/3dc3e8c35524cad2d1236f3393104ccf.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/45140742dfa8686b58db2899ce89f89a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/469d2754e315e77270c12fa0ab28026c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/4c0760284569b87d8f2290c324953973.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/4cba92c942694bdc479bc54101bb1aa4.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/4e7d263c30bb48604069a3b4404d4eeb.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/5279c7e465d9871edf51fd02e691eee7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/54bfdb6f21f9d6191e5100580239e14f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/56be5e32fd01668fe5ba814d94905839.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/615b38e0b5bb3ea68426f44f47706e6f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/65e9c4585d71bf48a5c62f367010da16.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/6b75213eb0be40ce84241eb2bb438a2e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/7520ca6ca8c60eb1e62d50e71c8d30f1.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/759a647b77791b1e98f99bc0ab5317a7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/76e51630143b95b6322ef93ad330da7a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/79aa7c8c842c4a27cf57c0a3a1ffca5a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/7cc72fd2c9105560422b6a67c6162945.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/7d0ea0690e432462f4d05a23d720ec19.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/7dffa5c1bec57e0903fd62357401ff1a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/82f6ccc063960eed1cdfd1d61ada8862.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/845b4b67564d62cf5cad242f7ac08ea5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/84c8e7bc0931008ed91f44ed12dfea94.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/887a11290ba78b1e66c6d2f67043005e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/88db1d042074fb6e66821ffc10941930.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/8dc18c0cebe6aa4bf4c45dbb831048ab.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/919a879bd2d580d8491a31a449390689.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/91da6cb174bebfb96976e2f1316be2fe.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/934dfdbed2bb2c4b6129199d699a34fa.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/97d7a17234f2b5030ae9697ae00aded2.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/995c4fda62d25c3b79dd5987737df6ae.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/9d0afa53dc2f92ba42024f55f11f6779.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/9e13a47242926f1cd7d68c08d9ef8889.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/a379ada89abd750c587ded29f77e731c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/a48be56ce5d3a99dc8f8331398004412.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/a806759e72987951d6d08b7cbdf36a1b.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/a88481ec8bc7be11cb66e46781a79bb9.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/a9a016d4a93409f65278327e8a8bb38d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/ab92d41062a7644faa45f50bf384454a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/b326a8a9c33b554db570da94f60bc380.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/b6117ff2993b11bb1fdc7ea3588a010c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/b61982951bd51b724143c30dfaaa9fe9.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/c0098958e20db68cab90097b5e62516f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/c4bfaa5e50798246e3770718b7a7c84a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/c84598999133455503042e06f4ab79cb.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/ca9a533988d7019597a60d4e17127e0c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/cf2b28f90f47276f7e2688a65e88a101.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/d689b1861d7e4377dd72ad3013482612.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/d9047070d72a816b3dba9d40c2d85e69.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/da04549f3f4ed28076b01b8cd710d313.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/da7af303f8c645f9a9dbae0e6e32dd35.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/df9568257eb29b156449fdd4bec5ec76.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/e067cd0ed76c90cd0a93c9339253f20b.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/e08b07772e7bed3cec2832d43f7fd339.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/e12150d5a39b30be8f567968c7a527b0.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/e53fcb2381eee345db4f6f973dd95a3e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/e5bd313ef81f687d398aacb11cec3069.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/eb5afb3d952b8593782caec6026514b6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/edd6a4f608d04fc0351d7688cfc321e4.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/f9695c6c4df2bf6bc03045ff79d4f01f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/fada6eaa68ff8816afe43d2a36c5423e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/fbcc4bf5367218951172bdee6f77d7a6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/noto-serif-kr.css +2601 -0
- package/src/styles/site-media.css +823 -0
- package/src/styles/tokens.css +292 -21
- package/src/styles/ui.css +4188 -1857
- package/src/types/bindings.ts +3 -1
- package/src/types/config.ts +139 -9
- package/src/types/constants.ts +46 -11
- package/src/types/entities.ts +24 -8
- package/src/types/operations.ts +80 -1
- package/src/types/props.ts +10 -2
- package/src/types/raw-assets.d.ts +39 -0
- package/src/types/views.ts +28 -7
- package/src/ui/__tests__/color-themes.test.ts +7 -40
- package/src/ui/__tests__/font-themes.test.ts +30 -11
- package/src/ui/color-themes.ts +311 -270
- package/src/ui/compose/ComposeDialog.tsx +712 -394
- package/src/ui/compose/ComposePrompt.tsx +15 -10
- package/src/ui/dash/ActionButtons.tsx +27 -18
- package/src/ui/dash/DangerZone.tsx +15 -10
- package/src/ui/dash/FormatBadge.tsx +21 -8
- package/src/ui/dash/StatusBadge.tsx +33 -22
- package/src/ui/dash/appearance/AdvancedContent.tsx +44 -30
- package/src/ui/dash/appearance/CodeInjectionContent.tsx +164 -0
- package/src/ui/dash/appearance/ColorThemeContent.tsx +127 -160
- package/src/ui/dash/appearance/FontThemeContent.tsx +20 -15
- package/src/ui/dash/appearance/NavigationContent.tsx +368 -225
- package/src/ui/dash/appearance/__tests__/NavigationContent.test.tsx +54 -0
- package/src/ui/dash/settings/AccountContent.tsx +34 -23
- package/src/ui/dash/settings/AccountMenuContent.tsx +226 -137
- package/src/ui/dash/settings/ApiTokensContent.tsx +122 -79
- package/src/ui/dash/settings/AvatarContent.tsx +73 -45
- package/src/ui/dash/settings/DeleteAccountContent.tsx +106 -66
- package/src/ui/dash/settings/GeneralContent.tsx +274 -157
- package/src/ui/dash/settings/GitHubSyncContent.tsx +558 -0
- package/src/ui/dash/settings/SessionsContent.tsx +79 -38
- package/src/ui/dash/settings/SettingsRootContent.tsx +209 -109
- package/src/ui/dash/settings/__tests__/AccountMenuContent.test.tsx +67 -0
- package/src/ui/dash/settings/__tests__/GeneralContent.test.tsx +75 -0
- package/src/ui/feed/CuratedThreadPreview.tsx +24 -14
- package/src/ui/feed/LinkCard.tsx +116 -84
- package/src/ui/feed/LinkPreview.tsx +75 -0
- package/src/ui/feed/NoteCard.tsx +60 -16
- package/src/ui/feed/PostStatusBadges.tsx +15 -0
- package/src/ui/feed/QuoteCard.tsx +10 -3
- package/src/ui/feed/ThreadPreview.tsx +62 -36
- package/src/ui/feed/TimelineFeed.tsx +2 -1
- package/src/ui/feed/__tests__/thread-preview.test.ts +220 -11
- package/src/ui/feed/__tests__/timeline-cards.test.ts +186 -5
- package/src/ui/feed/thread-preview-state.ts +23 -10
- package/src/ui/font-themes.ts +57 -97
- package/src/ui/layouts/BaseLayout.tsx +221 -136
- package/src/ui/layouts/SiteLayout.tsx +431 -143
- package/src/ui/layouts/__tests__/BaseLayout.test.tsx +76 -6
- package/src/ui/layouts/__tests__/SiteLayout.test.tsx +76 -0
- package/src/ui/pages/ArchivePage.tsx +320 -207
- package/src/ui/pages/BrandPage.tsx +499 -341
- package/src/ui/pages/CollectionEditorPage.tsx +50 -30
- package/src/ui/pages/CollectionPage.tsx +151 -78
- package/src/ui/pages/CollectionsPage.tsx +27 -18
- package/src/ui/pages/ComposePage.tsx +15 -10
- package/src/ui/pages/FeaturedPage.tsx +16 -11
- package/src/ui/pages/HomePage.tsx +15 -10
- package/src/ui/pages/PostPage.tsx +5 -1
- package/src/ui/pages/SearchPage.tsx +93 -43
- package/src/ui/pages/ThemeSamplePage.tsx +758 -520
- package/src/ui/pages/__tests__/ArchivePage.test.tsx +72 -0
- package/src/ui/shared/CollectionDirectory.tsx +215 -34
- package/src/ui/shared/CollectionsManager.tsx +155 -75
- package/src/ui/shared/DecorativeQuoteMark.tsx +12 -9
- package/src/ui/shared/MediaGallery.tsx +350 -256
- package/src/ui/shared/Pagination.tsx +36 -25
- package/src/ui/shared/PostFooter.tsx +139 -96
- package/src/ui/shared/__tests__/media-gallery.test.ts +27 -0
- package/src/ui/shared/__tests__/navigation-labels.test.ts +79 -28
- package/src/ui/shared/__tests__/post-footer.test.ts +42 -17
- package/src/ui/shared/collection-management-labels.ts +109 -33
- package/src/ui/shared/navigation-labels.ts +78 -8
- package/bin/lib/site-localize-media.js +0 -427
- package/dist/client/_assets/client-auth.js +0 -3260
- package/dist/client/_assets/client.css +0 -2
- package/dist/client/_assets/client.js +0 -380
- package/src/__tests__/site-localize-media.test.ts +0 -150
- package/src/client/components/jant-collection-sidebar.ts +0 -816
- package/src/i18n/locales/en.ts +0 -1
- package/src/i18n/locales/zh-Hans.po +0 -3197
- package/src/i18n/locales/zh-Hans.ts +0 -1
- package/src/i18n/locales/zh-Hant.po +0 -3197
- package/src/i18n/locales/zh-Hant.ts +0 -1
- package/src/routes/feed/rss.ts +0 -216
- package/src/types/lingui-react-macro.d.ts +0 -34
- /package/dist/client/_assets/chunks/{heic-to-XcUDQvtx.js → heic-to-DUUaO23q.js} +0 -0
- /package/dist/client/_assets/chunks/{module-ChVQstFd.js → module-DcsAZQZ_.js} +0 -0
- /package/dist/client/_assets/chunks/{native-CR5HLOyf.js → native-DpcrFAPh.js} +0 -0
- /package/dist/client/_assets/{client-cjk.css → client-cjk-B7Z0snDu.css} +0 -0
- /package/dist/client/_assets/{client-cjk-tc.css → client-cjk-tc-BesJYrb2.css} +0 -0
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { Hono, type Context } from "hono";
|
|
10
|
+
import { getCookie, setCookie } from "hono/cookie";
|
|
10
11
|
import { msg } from "@lingui/core/macro";
|
|
11
12
|
import { z } from "zod";
|
|
12
13
|
import type { Bindings } from "../../types.js";
|
|
@@ -36,8 +37,13 @@ import { NavigationContent } from "../../ui/dash/appearance/NavigationContent.js
|
|
|
36
37
|
import { ColorThemeContent } from "../../ui/dash/appearance/ColorThemeContent.js";
|
|
37
38
|
import { FontThemeContent } from "../../ui/dash/appearance/FontThemeContent.js";
|
|
38
39
|
import { AdvancedContent } from "../../ui/dash/appearance/AdvancedContent.js";
|
|
40
|
+
import { CodeInjectionContent } from "../../ui/dash/appearance/CodeInjectionContent.js";
|
|
39
41
|
import { ApiTokensContent } from "../../ui/dash/settings/ApiTokensContent.js";
|
|
40
42
|
import { DeleteAccountContent } from "../../ui/dash/settings/DeleteAccountContent.js";
|
|
43
|
+
import {
|
|
44
|
+
GitHubSyncContent,
|
|
45
|
+
type GitHubSyncStatus,
|
|
46
|
+
} from "../../ui/dash/settings/GitHubSyncContent.js";
|
|
41
47
|
import { toAbsoluteSiteUrl, toPublicPath } from "../../lib/url.js";
|
|
42
48
|
import { parseValidated, UpdateSiteSettingsSchema } from "../../lib/schemas.js";
|
|
43
49
|
import {
|
|
@@ -47,6 +53,32 @@ import {
|
|
|
47
53
|
getHostedControlPlaneSiteDeleteUrl,
|
|
48
54
|
} from "../../lib/hosted-signin.js";
|
|
49
55
|
import { syncHostedControlPlaneSiteAvatar } from "../../lib/hosted-control-plane-sync.js";
|
|
56
|
+
import {
|
|
57
|
+
getGitHubAppConfig,
|
|
58
|
+
getHostedControlPlaneSsoSecret,
|
|
59
|
+
} from "../../lib/env.js";
|
|
60
|
+
import {
|
|
61
|
+
buildInstallUrl,
|
|
62
|
+
getInstallation,
|
|
63
|
+
listInstallationReposPage,
|
|
64
|
+
searchInstallationRepos,
|
|
65
|
+
} from "../../lib/github-app.js";
|
|
66
|
+
import {
|
|
67
|
+
isSyncPending,
|
|
68
|
+
markSyncPending,
|
|
69
|
+
runBackgroundSync,
|
|
70
|
+
} from "../../lib/github-sync-trigger.js";
|
|
71
|
+
import { buildSyncSiteConfig } from "../../lib/github-sync-site-config.js";
|
|
72
|
+
import {
|
|
73
|
+
readGitHubSyncStatus,
|
|
74
|
+
renderStatusCardHtml,
|
|
75
|
+
getSyncStatusStreamUrl,
|
|
76
|
+
} from "../../lib/github-sync-status.js";
|
|
77
|
+
import {
|
|
78
|
+
generateInstallNonce,
|
|
79
|
+
signInstallState,
|
|
80
|
+
verifyInstallState,
|
|
81
|
+
} from "../../lib/github-app-state.js";
|
|
50
82
|
|
|
51
83
|
type Env = { Bindings: Bindings; Variables: AppVariables };
|
|
52
84
|
|
|
@@ -54,6 +86,7 @@ export const settingsRoutes = new Hono<Env>();
|
|
|
54
86
|
|
|
55
87
|
const UpdateLocaleSettingsSchema = z.object({
|
|
56
88
|
siteLanguage: z.string(),
|
|
89
|
+
cjkSerifFont: z.string(),
|
|
57
90
|
timeZone: z.string(),
|
|
58
91
|
});
|
|
59
92
|
|
|
@@ -73,6 +106,100 @@ function publicPath(c: Context<Env>, path: string): string {
|
|
|
73
106
|
return toPublicPath(path, c.var.appConfig.sitePathPrefix);
|
|
74
107
|
}
|
|
75
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Breadcrumb labels for admin settings pages.
|
|
111
|
+
*
|
|
112
|
+
* These duplicate labels defined in the corresponding UI components (e.g.
|
|
113
|
+
* `SettingsRootContent`, `AccountMenuContent`) on purpose: Lingui hashes are
|
|
114
|
+
* computed from `message` alone, so identical text produces the same catalog
|
|
115
|
+
* entry and shares the translation. Keep the text in sync with the UI source
|
|
116
|
+
* of truth.
|
|
117
|
+
*/
|
|
118
|
+
function breadcrumbLabel(
|
|
119
|
+
c: Context<Env>,
|
|
120
|
+
key:
|
|
121
|
+
| "settings"
|
|
122
|
+
| "general"
|
|
123
|
+
| "avatar"
|
|
124
|
+
| "navigation"
|
|
125
|
+
| "colorTheme"
|
|
126
|
+
| "fontTheme"
|
|
127
|
+
| "customCss"
|
|
128
|
+
| "codeInjection"
|
|
129
|
+
| "account"
|
|
130
|
+
| "sessions"
|
|
131
|
+
| "password"
|
|
132
|
+
| "deleteAccount"
|
|
133
|
+
| "apiTokens"
|
|
134
|
+
| "githubSync",
|
|
135
|
+
): string {
|
|
136
|
+
const i18n = getI18n(c);
|
|
137
|
+
switch (key) {
|
|
138
|
+
case "settings":
|
|
139
|
+
return i18n._(
|
|
140
|
+
msg({ message: "Settings", comment: "@context: Breadcrumb label" }),
|
|
141
|
+
);
|
|
142
|
+
case "general":
|
|
143
|
+
return i18n._(
|
|
144
|
+
msg({ message: "General", comment: "@context: Breadcrumb label" }),
|
|
145
|
+
);
|
|
146
|
+
case "avatar":
|
|
147
|
+
return i18n._(
|
|
148
|
+
msg({ message: "Avatar", comment: "@context: Breadcrumb label" }),
|
|
149
|
+
);
|
|
150
|
+
case "navigation":
|
|
151
|
+
return i18n._(
|
|
152
|
+
msg({ message: "Navigation", comment: "@context: Breadcrumb label" }),
|
|
153
|
+
);
|
|
154
|
+
case "colorTheme":
|
|
155
|
+
return i18n._(
|
|
156
|
+
msg({ message: "Color Theme", comment: "@context: Breadcrumb label" }),
|
|
157
|
+
);
|
|
158
|
+
case "fontTheme":
|
|
159
|
+
return i18n._(
|
|
160
|
+
msg({ message: "Font Theme", comment: "@context: Breadcrumb label" }),
|
|
161
|
+
);
|
|
162
|
+
case "customCss":
|
|
163
|
+
return i18n._(
|
|
164
|
+
msg({ message: "Custom CSS", comment: "@context: Breadcrumb label" }),
|
|
165
|
+
);
|
|
166
|
+
case "codeInjection":
|
|
167
|
+
return i18n._(
|
|
168
|
+
msg({
|
|
169
|
+
message: "Code Injection",
|
|
170
|
+
comment: "@context: Breadcrumb label",
|
|
171
|
+
}),
|
|
172
|
+
);
|
|
173
|
+
case "account":
|
|
174
|
+
return i18n._(
|
|
175
|
+
msg({ message: "Account", comment: "@context: Breadcrumb label" }),
|
|
176
|
+
);
|
|
177
|
+
case "sessions":
|
|
178
|
+
return i18n._(
|
|
179
|
+
msg({ message: "Sessions", comment: "@context: Breadcrumb label" }),
|
|
180
|
+
);
|
|
181
|
+
case "password":
|
|
182
|
+
return i18n._(
|
|
183
|
+
msg({ message: "Password", comment: "@context: Breadcrumb label" }),
|
|
184
|
+
);
|
|
185
|
+
case "deleteAccount":
|
|
186
|
+
return i18n._(
|
|
187
|
+
msg({
|
|
188
|
+
message: "Delete Account",
|
|
189
|
+
comment: "@context: Breadcrumb label",
|
|
190
|
+
}),
|
|
191
|
+
);
|
|
192
|
+
case "apiTokens":
|
|
193
|
+
return i18n._(
|
|
194
|
+
msg({ message: "API Tokens", comment: "@context: Breadcrumb label" }),
|
|
195
|
+
);
|
|
196
|
+
case "githubSync":
|
|
197
|
+
return i18n._(
|
|
198
|
+
msg({ message: "GitHub Sync", comment: "@context: Breadcrumb label" }),
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
76
203
|
type DemoRestriction = "sessions" | "password" | "accountDeletion";
|
|
77
204
|
|
|
78
205
|
function getDemoRestrictionMessage(
|
|
@@ -163,14 +290,15 @@ settingsRoutes.get("/general", async (c) => {
|
|
|
163
290
|
content: (
|
|
164
291
|
<>
|
|
165
292
|
<AdminBreadcrumb
|
|
166
|
-
parent="
|
|
293
|
+
parent={breadcrumbLabel(c, "settings")}
|
|
167
294
|
parentHref={publicPath(c, "/settings")}
|
|
168
|
-
current="
|
|
295
|
+
current={breadcrumbLabel(c, "general")}
|
|
169
296
|
/>
|
|
170
297
|
<GeneralContent
|
|
171
298
|
siteName={dbSiteName || ""}
|
|
172
299
|
siteDescription={dbSiteDescription || ""}
|
|
173
300
|
siteLanguage={appConfig.siteLanguage}
|
|
301
|
+
cjkSerifFont={appConfig.cjkSerifFont}
|
|
174
302
|
siteNameFallback={appConfig.fallbacks.siteName}
|
|
175
303
|
siteDescriptionFallback={appConfig.fallbacks.siteDescription}
|
|
176
304
|
mainRssFeed={appConfig.mainRssFeed}
|
|
@@ -277,7 +405,8 @@ settingsRoutes.post("/general/language-time", async (c) => {
|
|
|
277
405
|
);
|
|
278
406
|
const { languageChanged } =
|
|
279
407
|
await c.var.services.settings.updateLocaleSettings(body, {
|
|
280
|
-
oldLanguage: c.var.
|
|
408
|
+
oldLanguage: c.var.appConfig.siteLanguage,
|
|
409
|
+
oldCjkSerifFont: c.var.appConfig.cjkSerifFont,
|
|
281
410
|
});
|
|
282
411
|
|
|
283
412
|
const wantsJson = c.req.header("accept")?.includes("application/json");
|
|
@@ -386,9 +515,9 @@ settingsRoutes.get("/avatar", async (c) => {
|
|
|
386
515
|
content: (
|
|
387
516
|
<>
|
|
388
517
|
<AdminBreadcrumb
|
|
389
|
-
parent="
|
|
518
|
+
parent={breadcrumbLabel(c, "settings")}
|
|
390
519
|
parentHref={publicPath(c, "/settings")}
|
|
391
|
-
current="
|
|
520
|
+
current={breadcrumbLabel(c, "avatar")}
|
|
392
521
|
/>
|
|
393
522
|
<AvatarContent
|
|
394
523
|
siteAvatarUrl={c.var.appConfig.siteAvatarUrl}
|
|
@@ -576,9 +705,10 @@ settingsRoutes.post("/avatar/display", async (c) => {
|
|
|
576
705
|
// ===========================================================================
|
|
577
706
|
|
|
578
707
|
settingsRoutes.get("/navigation", async (c) => {
|
|
579
|
-
const navItems = await
|
|
580
|
-
|
|
581
|
-
|
|
708
|
+
const [navItems, directoryData] = await Promise.all([
|
|
709
|
+
c.var.services.navItems.list(),
|
|
710
|
+
c.var.services.collections.listDirectoryData(),
|
|
711
|
+
]);
|
|
582
712
|
const navData = await getNavigationData(c);
|
|
583
713
|
|
|
584
714
|
return renderPublicPage(c, {
|
|
@@ -587,14 +717,13 @@ settingsRoutes.get("/navigation", async (c) => {
|
|
|
587
717
|
content: (
|
|
588
718
|
<>
|
|
589
719
|
<AdminBreadcrumb
|
|
590
|
-
parent="
|
|
720
|
+
parent={breadcrumbLabel(c, "settings")}
|
|
591
721
|
parentHref={publicPath(c, "/settings")}
|
|
592
|
-
current="
|
|
722
|
+
current={breadcrumbLabel(c, "navigation")}
|
|
593
723
|
/>
|
|
594
724
|
<NavigationContent
|
|
595
725
|
navItems={navItems}
|
|
596
|
-
|
|
597
|
-
homeDefaultView={homeDefaultView}
|
|
726
|
+
directoryData={directoryData}
|
|
598
727
|
mainRssFeed={c.var.appConfig.mainRssFeed}
|
|
599
728
|
siteName={navData.siteName}
|
|
600
729
|
sitePathPrefix={c.var.appConfig.sitePathPrefix}
|
|
@@ -604,41 +733,12 @@ settingsRoutes.get("/navigation", async (c) => {
|
|
|
604
733
|
});
|
|
605
734
|
});
|
|
606
735
|
|
|
607
|
-
settingsRoutes.post("/navigation/nav-max-visible", async (c) => {
|
|
608
|
-
const body = await c.req.json<{ value: number }>();
|
|
609
|
-
const { settings } = c.var.services;
|
|
610
|
-
|
|
611
|
-
const navMax = Math.max(0, Math.min(5, body.value ?? 3));
|
|
612
|
-
if (navMax !== 3) {
|
|
613
|
-
await settings.set("HEADER_NAV_MAX_VISIBLE", String(navMax));
|
|
614
|
-
} else {
|
|
615
|
-
await settings.remove("HEADER_NAV_MAX_VISIBLE");
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
return c.json({ ok: true });
|
|
619
|
-
});
|
|
620
|
-
|
|
621
|
-
settingsRoutes.post("/navigation/home-default-view", async (c) => {
|
|
622
|
-
const body = await c.req.json<{ value: string }>();
|
|
623
|
-
const { settings } = c.var.services;
|
|
624
|
-
|
|
625
|
-
if (body.value === "featured") {
|
|
626
|
-
await settings.set("HOME_DEFAULT_VIEW", "featured");
|
|
627
|
-
} else {
|
|
628
|
-
await settings.remove("HOME_DEFAULT_VIEW");
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
return c.json({ ok: true });
|
|
632
|
-
});
|
|
633
|
-
|
|
634
736
|
// ===========================================================================
|
|
635
737
|
// Color Theme (moved from appearance routes)
|
|
636
738
|
// ===========================================================================
|
|
637
739
|
|
|
638
740
|
settingsRoutes.get("/color-theme", async (c) => {
|
|
639
|
-
const
|
|
640
|
-
const currentThemeId =
|
|
641
|
-
c.var.allSettings[SETTINGS_KEYS.THEME] ?? defaultThemeId;
|
|
741
|
+
const currentThemeId = c.var.appConfig.themeId;
|
|
642
742
|
const currentThemeMode = c.var.appConfig.themeMode;
|
|
643
743
|
const themes = getAvailableThemes();
|
|
644
744
|
const saved = c.req.query("saved") !== undefined;
|
|
@@ -651,9 +751,9 @@ settingsRoutes.get("/color-theme", async (c) => {
|
|
|
651
751
|
content: (
|
|
652
752
|
<>
|
|
653
753
|
<AdminBreadcrumb
|
|
654
|
-
parent="
|
|
754
|
+
parent={breadcrumbLabel(c, "settings")}
|
|
655
755
|
parentHref={publicPath(c, "/settings")}
|
|
656
|
-
current=
|
|
756
|
+
current={breadcrumbLabel(c, "colorTheme")}
|
|
657
757
|
/>
|
|
658
758
|
<ColorThemeContent
|
|
659
759
|
themes={themes}
|
|
@@ -685,12 +785,7 @@ settingsRoutes.post("/color-theme", async (c) => {
|
|
|
685
785
|
);
|
|
686
786
|
}
|
|
687
787
|
|
|
688
|
-
|
|
689
|
-
if (validTheme.id === defaultThemeId) {
|
|
690
|
-
await settings.remove(SETTINGS_KEYS.THEME);
|
|
691
|
-
} else {
|
|
692
|
-
await settings.set(SETTINGS_KEYS.THEME, validTheme.id);
|
|
693
|
-
}
|
|
788
|
+
await settings.set(SETTINGS_KEYS.THEME, validTheme.id);
|
|
694
789
|
|
|
695
790
|
const themeMode: ThemeMode = THEME_MODES.includes(body.themeMode as ThemeMode)
|
|
696
791
|
? (body.themeMode as ThemeMode)
|
|
@@ -710,7 +805,7 @@ settingsRoutes.post("/color-theme", async (c) => {
|
|
|
710
805
|
// ===========================================================================
|
|
711
806
|
|
|
712
807
|
settingsRoutes.get("/font-theme", async (c) => {
|
|
713
|
-
const currentFontThemeId = c.var.
|
|
808
|
+
const currentFontThemeId = c.var.appConfig.fontThemeId;
|
|
714
809
|
const saved = c.req.query("saved") !== undefined;
|
|
715
810
|
const navData = await getNavigationData(c);
|
|
716
811
|
|
|
@@ -721,9 +816,9 @@ settingsRoutes.get("/font-theme", async (c) => {
|
|
|
721
816
|
content: (
|
|
722
817
|
<>
|
|
723
818
|
<AdminBreadcrumb
|
|
724
|
-
parent="
|
|
819
|
+
parent={breadcrumbLabel(c, "settings")}
|
|
725
820
|
parentHref={publicPath(c, "/settings")}
|
|
726
|
-
current=
|
|
821
|
+
current={breadcrumbLabel(c, "fontTheme")}
|
|
727
822
|
/>
|
|
728
823
|
<FontThemeContent
|
|
729
824
|
fontThemes={BUILTIN_FONT_THEMES}
|
|
@@ -754,11 +849,7 @@ settingsRoutes.post("/font-theme", async (c) => {
|
|
|
754
849
|
);
|
|
755
850
|
}
|
|
756
851
|
|
|
757
|
-
|
|
758
|
-
await settings.remove("FONT_THEME");
|
|
759
|
-
} else {
|
|
760
|
-
await settings.set("FONT_THEME", validFont.id);
|
|
761
|
-
}
|
|
852
|
+
await settings.set("FONT_THEME", validFont.id);
|
|
762
853
|
|
|
763
854
|
return dsRedirect(publicPath(c, "/settings/font-theme?saved"));
|
|
764
855
|
});
|
|
@@ -777,9 +868,9 @@ settingsRoutes.get("/custom-css", async (c) => {
|
|
|
777
868
|
content: (
|
|
778
869
|
<>
|
|
779
870
|
<AdminBreadcrumb
|
|
780
|
-
parent="
|
|
871
|
+
parent={breadcrumbLabel(c, "settings")}
|
|
781
872
|
parentHref={publicPath(c, "/settings")}
|
|
782
|
-
current=
|
|
873
|
+
current={breadcrumbLabel(c, "customCss")}
|
|
783
874
|
/>
|
|
784
875
|
<AdvancedContent
|
|
785
876
|
customCSS={customCSS}
|
|
@@ -813,6 +904,70 @@ settingsRoutes.post("/custom-css", async (c) => {
|
|
|
813
904
|
);
|
|
814
905
|
});
|
|
815
906
|
|
|
907
|
+
// ===========================================================================
|
|
908
|
+
// Code Injection — site-wide HTML in <head> and at end of <body>.
|
|
909
|
+
// ===========================================================================
|
|
910
|
+
|
|
911
|
+
settingsRoutes.get("/code-injection", async (c) => {
|
|
912
|
+
const customHeadHtml =
|
|
913
|
+
c.var.allSettings[SETTINGS_KEYS.CUSTOM_HEAD_HTML] ?? "";
|
|
914
|
+
const customBodyEndHtml =
|
|
915
|
+
c.var.allSettings[SETTINGS_KEYS.CUSTOM_BODY_END_HTML] ?? "";
|
|
916
|
+
const navData = await getNavigationData(c);
|
|
917
|
+
|
|
918
|
+
return renderPublicPage(c, {
|
|
919
|
+
title: buildPageTitle("Code Injection", navData.siteName),
|
|
920
|
+
navData,
|
|
921
|
+
content: (
|
|
922
|
+
<>
|
|
923
|
+
<AdminBreadcrumb
|
|
924
|
+
parent={breadcrumbLabel(c, "settings")}
|
|
925
|
+
parentHref={publicPath(c, "/settings")}
|
|
926
|
+
current={breadcrumbLabel(c, "codeInjection")}
|
|
927
|
+
/>
|
|
928
|
+
<CodeInjectionContent
|
|
929
|
+
customHeadHtml={customHeadHtml}
|
|
930
|
+
customBodyEndHtml={customBodyEndHtml}
|
|
931
|
+
sitePathPrefix={c.var.appConfig.sitePathPrefix}
|
|
932
|
+
/>
|
|
933
|
+
</>
|
|
934
|
+
),
|
|
935
|
+
});
|
|
936
|
+
});
|
|
937
|
+
|
|
938
|
+
settingsRoutes.post("/code-injection", async (c) => {
|
|
939
|
+
const i18n = getI18n(c);
|
|
940
|
+
const body = await c.req.json<{
|
|
941
|
+
customHeadHtml?: string;
|
|
942
|
+
customBodyEndHtml?: string;
|
|
943
|
+
}>();
|
|
944
|
+
const { settings } = c.var.services;
|
|
945
|
+
|
|
946
|
+
const headHtml = body.customHeadHtml?.trim() ?? "";
|
|
947
|
+
const bodyEndHtml = body.customBodyEndHtml?.trim() ?? "";
|
|
948
|
+
|
|
949
|
+
if (headHtml) {
|
|
950
|
+
await settings.set(SETTINGS_KEYS.CUSTOM_HEAD_HTML, headHtml);
|
|
951
|
+
} else {
|
|
952
|
+
await settings.remove(SETTINGS_KEYS.CUSTOM_HEAD_HTML);
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
if (bodyEndHtml) {
|
|
956
|
+
await settings.set(SETTINGS_KEYS.CUSTOM_BODY_END_HTML, bodyEndHtml);
|
|
957
|
+
} else {
|
|
958
|
+
await settings.remove(SETTINGS_KEYS.CUSTOM_BODY_END_HTML);
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
return dsToast(
|
|
962
|
+
i18n._(
|
|
963
|
+
msg({
|
|
964
|
+
message: "Code injection updated.",
|
|
965
|
+
comment: "@context: Toast after saving Code Injection settings",
|
|
966
|
+
}),
|
|
967
|
+
),
|
|
968
|
+
);
|
|
969
|
+
});
|
|
970
|
+
|
|
816
971
|
// ===========================================================================
|
|
817
972
|
// Account sub-menu
|
|
818
973
|
// ===========================================================================
|
|
@@ -834,9 +989,9 @@ settingsRoutes.get("/account", async (c) => {
|
|
|
834
989
|
content: (
|
|
835
990
|
<>
|
|
836
991
|
<AdminBreadcrumb
|
|
837
|
-
parent="
|
|
992
|
+
parent={breadcrumbLabel(c, "settings")}
|
|
838
993
|
parentHref={publicPath(c, "/settings")}
|
|
839
|
-
current="
|
|
994
|
+
current={breadcrumbLabel(c, "account")}
|
|
840
995
|
/>
|
|
841
996
|
<AccountMenuContent
|
|
842
997
|
sitePathPrefix={c.var.appConfig.sitePathPrefix}
|
|
@@ -900,9 +1055,9 @@ settingsRoutes.get("/account/sessions", async (c) => {
|
|
|
900
1055
|
content: (
|
|
901
1056
|
<>
|
|
902
1057
|
<AdminBreadcrumb
|
|
903
|
-
parent="
|
|
1058
|
+
parent={breadcrumbLabel(c, "account")}
|
|
904
1059
|
parentHref={publicPath(c, "/settings/account")}
|
|
905
|
-
current="
|
|
1060
|
+
current={breadcrumbLabel(c, "sessions")}
|
|
906
1061
|
/>
|
|
907
1062
|
<SessionsContent
|
|
908
1063
|
sessions={sessions}
|
|
@@ -955,9 +1110,9 @@ settingsRoutes.get("/account/password", async (c) => {
|
|
|
955
1110
|
content: (
|
|
956
1111
|
<>
|
|
957
1112
|
<AdminBreadcrumb
|
|
958
|
-
parent="
|
|
1113
|
+
parent={breadcrumbLabel(c, "account")}
|
|
959
1114
|
parentHref={publicPath(c, "/settings/account")}
|
|
960
|
-
current="
|
|
1115
|
+
current={breadcrumbLabel(c, "password")}
|
|
961
1116
|
/>
|
|
962
1117
|
<AccountContent sitePathPrefix={c.var.appConfig.sitePathPrefix} />
|
|
963
1118
|
</>
|
|
@@ -1066,9 +1221,9 @@ settingsRoutes.get("/account/delete-account", async (c) => {
|
|
|
1066
1221
|
content: (
|
|
1067
1222
|
<>
|
|
1068
1223
|
<AdminBreadcrumb
|
|
1069
|
-
parent="
|
|
1224
|
+
parent={breadcrumbLabel(c, "account")}
|
|
1070
1225
|
parentHref={publicPath(c, "/settings/account")}
|
|
1071
|
-
current=
|
|
1226
|
+
current={breadcrumbLabel(c, "deleteAccount")}
|
|
1072
1227
|
/>
|
|
1073
1228
|
<DeleteAccountContent
|
|
1074
1229
|
siteName={navData.siteName}
|
|
@@ -1152,9 +1307,9 @@ settingsRoutes.get("/api-tokens", async (c) => {
|
|
|
1152
1307
|
content: (
|
|
1153
1308
|
<>
|
|
1154
1309
|
<AdminBreadcrumb
|
|
1155
|
-
parent="
|
|
1310
|
+
parent={breadcrumbLabel(c, "settings")}
|
|
1156
1311
|
parentHref={publicPath(c, "/settings")}
|
|
1157
|
-
current=
|
|
1312
|
+
current={breadcrumbLabel(c, "apiTokens")}
|
|
1158
1313
|
/>
|
|
1159
1314
|
<ApiTokensContent
|
|
1160
1315
|
tokens={tokens}
|
|
@@ -1190,3 +1345,957 @@ settingsRoutes.post("/api-tokens/:id/delete", async (c) => {
|
|
|
1190
1345
|
|
|
1191
1346
|
return dsRedirect(publicPath(c, "/settings/api-tokens"));
|
|
1192
1347
|
});
|
|
1348
|
+
|
|
1349
|
+
// ===========================================================================
|
|
1350
|
+
// GitHub Sync
|
|
1351
|
+
// ===========================================================================
|
|
1352
|
+
|
|
1353
|
+
settingsRoutes.post("/github-sync/connect", async (c) => {
|
|
1354
|
+
// When a GitHub App is configured on this deployment, PAT connect is
|
|
1355
|
+
// disabled — users must go through the App install flow so we don't end
|
|
1356
|
+
// up with a mix of auth modes per site (harder to audit, easier to leak
|
|
1357
|
+
// a long-lived token by accident). The UI hides the PAT form too.
|
|
1358
|
+
if (getGitHubAppConfig(c.env)) {
|
|
1359
|
+
return dsToast(
|
|
1360
|
+
"This deployment uses GitHub App authentication. Use Install GitHub App instead.",
|
|
1361
|
+
"error",
|
|
1362
|
+
);
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
const body = await c.req.json<{ token: string; repo: string }>();
|
|
1366
|
+
|
|
1367
|
+
if (!body.token?.trim() || !body.repo?.trim()) {
|
|
1368
|
+
return dsToast("Token and repository are required.", "error");
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
const { parseRepoSlug, createGitHubClient } =
|
|
1372
|
+
await import("../../lib/github-api.js");
|
|
1373
|
+
const parsed = parseRepoSlug(body.repo);
|
|
1374
|
+
if (!parsed) {
|
|
1375
|
+
return dsToast("Invalid repository format. Use owner/repo.", "error");
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
// Validate token
|
|
1379
|
+
const client = createGitHubClient(body.token);
|
|
1380
|
+
try {
|
|
1381
|
+
await client.getRepo(parsed.owner, parsed.repo);
|
|
1382
|
+
} catch (err) {
|
|
1383
|
+
const detail = err instanceof Error ? err.message : JSON.stringify(err);
|
|
1384
|
+
process.stderr.write(
|
|
1385
|
+
`[Jant] GitHub Sync connect failed for ${body.repo}: ${detail}\n`,
|
|
1386
|
+
);
|
|
1387
|
+
return dsToast(`Could not access the repository: ${detail}`, "error");
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
// Check if this repo already has a Jant webhook
|
|
1391
|
+
try {
|
|
1392
|
+
const hooks = await client.listWebhooks(parsed.owner, parsed.repo);
|
|
1393
|
+
const existingJantHook = hooks.find((h) =>
|
|
1394
|
+
h.config.url?.includes("/api/github-sync/webhook"),
|
|
1395
|
+
);
|
|
1396
|
+
if (existingJantHook) {
|
|
1397
|
+
return dsToast(
|
|
1398
|
+
"This repository is already connected to a Jant site. Disconnect it first before connecting to a new site.",
|
|
1399
|
+
"error",
|
|
1400
|
+
);
|
|
1401
|
+
}
|
|
1402
|
+
} catch {
|
|
1403
|
+
// If listing webhooks fails (permissions), skip the check and continue
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
// Save config
|
|
1407
|
+
await c.var.services.settings.set("GITHUB_SYNC_TOKEN", body.token);
|
|
1408
|
+
await c.var.services.settings.set("GITHUB_SYNC_REPO", body.repo);
|
|
1409
|
+
await c.var.services.settings.set("GITHUB_SYNC_AUTH_MODE", "pat");
|
|
1410
|
+
await c.var.services.settings.set("GITHUB_SYNC_APP_INSTALLATION_ID", "");
|
|
1411
|
+
await c.var.services.settings.set("GITHUB_SYNC_ENABLED", "true");
|
|
1412
|
+
|
|
1413
|
+
// Create webhook
|
|
1414
|
+
const { createGitHubSyncService } =
|
|
1415
|
+
await import("../../services/github-sync.js");
|
|
1416
|
+
const syncService = createGitHubSyncService(
|
|
1417
|
+
c.var.services,
|
|
1418
|
+
c.var.currentSite.id,
|
|
1419
|
+
await buildSyncSiteConfig(c),
|
|
1420
|
+
{ storage: c.var.storage, githubApp: getGitHubAppConfig(c.env) },
|
|
1421
|
+
);
|
|
1422
|
+
const siteUrl = c.var.appConfig.siteUrl.replace(/\/+$/, "");
|
|
1423
|
+
try {
|
|
1424
|
+
await syncService.setupWebhook(`${siteUrl}/api/github-sync/webhook`);
|
|
1425
|
+
} catch {
|
|
1426
|
+
return dsToast(
|
|
1427
|
+
"Connected, but webhook creation failed. You may need to create it manually.",
|
|
1428
|
+
"error",
|
|
1429
|
+
);
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
// Kick off an initial background push so "Last sync" doesn't sit on
|
|
1433
|
+
// "Not synced yet" until the user's next edit. See the App flow's
|
|
1434
|
+
// equivalent block below for why we bypass the queue.
|
|
1435
|
+
await markSyncPending(c.var.services.settings);
|
|
1436
|
+
const initialSync = runBackgroundSync(c.var.services.settings, syncService);
|
|
1437
|
+
try {
|
|
1438
|
+
c.executionCtx?.waitUntil(initialSync);
|
|
1439
|
+
} catch {
|
|
1440
|
+
// executionCtx unavailable (tests / Node).
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
return dsRedirect(publicPath(c, "/settings/github-sync"));
|
|
1444
|
+
});
|
|
1445
|
+
|
|
1446
|
+
settingsRoutes.post("/github-sync/push", async (c) => {
|
|
1447
|
+
const { createGitHubSyncService } =
|
|
1448
|
+
await import("../../services/github-sync.js");
|
|
1449
|
+
const syncService = createGitHubSyncService(
|
|
1450
|
+
c.var.services,
|
|
1451
|
+
c.var.currentSite.id,
|
|
1452
|
+
await buildSyncSiteConfig(c),
|
|
1453
|
+
{ storage: c.var.storage, githubApp: getGitHubAppConfig(c.env) },
|
|
1454
|
+
);
|
|
1455
|
+
|
|
1456
|
+
const config = await syncService.getConfig();
|
|
1457
|
+
if (!config) {
|
|
1458
|
+
return dsToast("GitHub Sync is not configured.", "error");
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
// Run the push in the background so the status card's live "Syncing…"
|
|
1462
|
+
// indicator drives the UX instead of a toast. We patch the status card
|
|
1463
|
+
// into its pending state immediately so Datastar re-evaluates `data-init`
|
|
1464
|
+
// on the new element and opens the SSE status stream, which then polls
|
|
1465
|
+
// until the push completes.
|
|
1466
|
+
await markSyncPending(c.var.services.settings);
|
|
1467
|
+
const push = runBackgroundSync(c.var.services.settings, syncService);
|
|
1468
|
+
try {
|
|
1469
|
+
c.executionCtx?.waitUntil(push);
|
|
1470
|
+
} catch {
|
|
1471
|
+
// executionCtx unavailable (tests / Node).
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
const status = await readGitHubSyncStatus(c); // pending=true after markSyncPending
|
|
1475
|
+
const streamUrl = getSyncStatusStreamUrl(c);
|
|
1476
|
+
const html = renderStatusCardHtml(c, status, streamUrl);
|
|
1477
|
+
return sse(c, async (stream) => {
|
|
1478
|
+
stream.patchElements(html, {
|
|
1479
|
+
mode: "outer",
|
|
1480
|
+
selector: "#github-sync-status",
|
|
1481
|
+
});
|
|
1482
|
+
});
|
|
1483
|
+
});
|
|
1484
|
+
|
|
1485
|
+
settingsRoutes.post("/github-sync/disconnect", async (c) => {
|
|
1486
|
+
const { createGitHubSyncService } =
|
|
1487
|
+
await import("../../services/github-sync.js");
|
|
1488
|
+
const syncService = createGitHubSyncService(
|
|
1489
|
+
c.var.services,
|
|
1490
|
+
c.var.currentSite.id,
|
|
1491
|
+
await buildSyncSiteConfig(c),
|
|
1492
|
+
{ githubApp: getGitHubAppConfig(c.env) },
|
|
1493
|
+
);
|
|
1494
|
+
await syncService.teardownWebhook();
|
|
1495
|
+
|
|
1496
|
+
return dsRedirect(publicPath(c, "/settings/github-sync"));
|
|
1497
|
+
});
|
|
1498
|
+
|
|
1499
|
+
// ---------------------------------------------------------------------------
|
|
1500
|
+
// GitHub App install flow
|
|
1501
|
+
// ---------------------------------------------------------------------------
|
|
1502
|
+
|
|
1503
|
+
/**
|
|
1504
|
+
* Redirect the user to GitHub to install the App on their account/org.
|
|
1505
|
+
*
|
|
1506
|
+
* Only available when GitHub App env vars are configured. Uses a signed
|
|
1507
|
+
* state cookie for CSRF protection.
|
|
1508
|
+
*/
|
|
1509
|
+
settingsRoutes.get("/github-sync/app/install", async (c) => {
|
|
1510
|
+
const app = getGitHubAppConfig(c.env);
|
|
1511
|
+
if (!app) {
|
|
1512
|
+
return c.text("GitHub App is not configured on this deployment.", 404);
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1515
|
+
// Reinstall-same-account predicate (§5): when this site already has at
|
|
1516
|
+
// least one authorized installation, skip the GitHub round-trip and
|
|
1517
|
+
// render the picker directly. The GitHub redirect is a dead end when
|
|
1518
|
+
// the App is already installed on the chosen account — GitHub shows
|
|
1519
|
+
// its Configure page and never comes back with installation_id.
|
|
1520
|
+
//
|
|
1521
|
+
// `?force=new` bypasses the predicate so the picker's "Install on
|
|
1522
|
+
// another account" action can still reach GitHub to add a fresh
|
|
1523
|
+
// install.
|
|
1524
|
+
const forceNew = c.req.query("force") === "new";
|
|
1525
|
+
if (!forceNew) {
|
|
1526
|
+
const existing =
|
|
1527
|
+
await c.var.services.githubAppInstallations.listInstallationsForSite(
|
|
1528
|
+
c.var.currentSite.id,
|
|
1529
|
+
);
|
|
1530
|
+
if (existing.length > 0) {
|
|
1531
|
+
const navData = await getNavigationData(c);
|
|
1532
|
+
const base = publicPath(c, "/settings/github-sync");
|
|
1533
|
+
const labels = buildRepoPickerLabels(c);
|
|
1534
|
+
const suggestedRepoName = buildSuggestedRepoName(c);
|
|
1535
|
+
return renderPublicPage(c, {
|
|
1536
|
+
title: buildPageTitle(
|
|
1537
|
+
"GitHub Sync — Pick Repository",
|
|
1538
|
+
navData.siteName,
|
|
1539
|
+
),
|
|
1540
|
+
navData,
|
|
1541
|
+
content: (
|
|
1542
|
+
<>
|
|
1543
|
+
<AdminBreadcrumb
|
|
1544
|
+
parent={breadcrumbLabel(c, "settings")}
|
|
1545
|
+
parentHref={publicPath(c, "/settings")}
|
|
1546
|
+
current={breadcrumbLabel(c, "githubSync")}
|
|
1547
|
+
/>
|
|
1548
|
+
<jant-repo-picker
|
|
1549
|
+
labels={labels}
|
|
1550
|
+
api-base={`${base}/app`}
|
|
1551
|
+
connect-url={`${base}/app/connect`}
|
|
1552
|
+
install-url={`${base}/app/install?force=new`}
|
|
1553
|
+
cancel-url={publicPath(c, "/settings")}
|
|
1554
|
+
create-repo-name-hint={suggestedRepoName}
|
|
1555
|
+
>
|
|
1556
|
+
<div class="flex flex-col gap-6 max-w-lg">
|
|
1557
|
+
<div>
|
|
1558
|
+
<h2 class="text-lg font-medium mb-1">Pick a repository</h2>
|
|
1559
|
+
<p class="text-sm text-muted-foreground">
|
|
1560
|
+
Loading repositories…
|
|
1561
|
+
</p>
|
|
1562
|
+
</div>
|
|
1563
|
+
</div>
|
|
1564
|
+
</jant-repo-picker>
|
|
1565
|
+
</>
|
|
1566
|
+
),
|
|
1567
|
+
});
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
// Build the state token. When running behind a hosted control plane we
|
|
1572
|
+
// sign host+nonce with the shared SSO secret so the control plane can
|
|
1573
|
+
// verify and route the callback back to the correct site host. In
|
|
1574
|
+
// self-hosted single-site mode the secret is absent and a plain nonce
|
|
1575
|
+
// suffices (the App's Callback URL points directly at this site).
|
|
1576
|
+
const nonce = generateInstallNonce();
|
|
1577
|
+
const ssoSecret = getHostedControlPlaneSsoSecret(c.env);
|
|
1578
|
+
const host = new URL(c.var.appConfig.siteUrl).host;
|
|
1579
|
+
const state = ssoSecret
|
|
1580
|
+
? await signInstallState(host, nonce, ssoSecret)
|
|
1581
|
+
: nonce;
|
|
1582
|
+
|
|
1583
|
+
// Cookie is pinned to this host; compared byte-for-byte on the callback
|
|
1584
|
+
// to defeat CSRF regardless of whether the state is signed.
|
|
1585
|
+
setCookie(c, "jant_gh_app_state", state, {
|
|
1586
|
+
httpOnly: true,
|
|
1587
|
+
sameSite: "Lax",
|
|
1588
|
+
path: "/",
|
|
1589
|
+
maxAge: 600,
|
|
1590
|
+
});
|
|
1591
|
+
|
|
1592
|
+
return c.redirect(buildInstallUrl(app.slug, state));
|
|
1593
|
+
});
|
|
1594
|
+
|
|
1595
|
+
/**
|
|
1596
|
+
* Landing page after the user installs the GitHub App.
|
|
1597
|
+
*
|
|
1598
|
+
* GitHub redirects here with `installation_id`, `setup_action`, and the
|
|
1599
|
+
* `state` we sent. We verify the state, list the installation's repos, and
|
|
1600
|
+
* render a repo picker that POSTs back to `/github-sync/app/connect`.
|
|
1601
|
+
*/
|
|
1602
|
+
settingsRoutes.get("/github-sync/app/callback", async (c) => {
|
|
1603
|
+
const app = getGitHubAppConfig(c.env);
|
|
1604
|
+
if (!app) {
|
|
1605
|
+
return c.text("GitHub App is not configured on this deployment.", 404);
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
const installationId = c.req.query("installation_id");
|
|
1609
|
+
const state = c.req.query("state");
|
|
1610
|
+
if (!installationId) {
|
|
1611
|
+
return c.text("Missing installation_id.", 400);
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
const expected = getCookie(c, "jant_gh_app_state");
|
|
1615
|
+
if (!state || !expected || expected !== state) {
|
|
1616
|
+
return c.text("Invalid or expired state.", 400);
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
// Defense in depth: when an SSO secret is present the state should also
|
|
1620
|
+
// HMAC-verify and its embedded host must match the host serving this
|
|
1621
|
+
// request. This blocks a rogue control plane from redirecting a victim
|
|
1622
|
+
// to the wrong site with a replayed token.
|
|
1623
|
+
const ssoSecret = getHostedControlPlaneSsoSecret(c.env);
|
|
1624
|
+
if (ssoSecret) {
|
|
1625
|
+
const payload = await verifyInstallState(state, ssoSecret);
|
|
1626
|
+
const currentHost = new URL(c.var.appConfig.siteUrl).host;
|
|
1627
|
+
if (!payload || payload.host !== currentHost) {
|
|
1628
|
+
return c.text("State signature invalid for this host.", 400);
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
// One-shot: clear the cookie so it can't be replayed.
|
|
1633
|
+
setCookie(c, "jant_gh_app_state", "", {
|
|
1634
|
+
httpOnly: true,
|
|
1635
|
+
sameSite: "Lax",
|
|
1636
|
+
path: "/",
|
|
1637
|
+
maxAge: 0,
|
|
1638
|
+
});
|
|
1639
|
+
|
|
1640
|
+
// Fetch the account info for this installation and remember it. The
|
|
1641
|
+
// picker's owner dropdown reads the stored list, so subsequent visits
|
|
1642
|
+
// can switch between accounts without re-running the install flow.
|
|
1643
|
+
// Failures are non-fatal: this callback must still render the picker
|
|
1644
|
+
// so the user isn't locked out right after a successful install.
|
|
1645
|
+
try {
|
|
1646
|
+
const installation = await getInstallation(app, installationId);
|
|
1647
|
+
await c.var.services.githubAppInstallations.upsertInstallation(
|
|
1648
|
+
installationId,
|
|
1649
|
+
c.var.currentSite.id,
|
|
1650
|
+
installation.account,
|
|
1651
|
+
);
|
|
1652
|
+
} catch {
|
|
1653
|
+
// Swallow — the legacy inline picker still works with just the
|
|
1654
|
+
// installation_id from the URL, just without the owner list.
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
const navData = await getNavigationData(c);
|
|
1658
|
+
const base = publicPath(c, "/settings/github-sync");
|
|
1659
|
+
const labels = buildRepoPickerLabels(c);
|
|
1660
|
+
const suggestedRepoName = buildSuggestedRepoName(c);
|
|
1661
|
+
|
|
1662
|
+
return renderPublicPage(c, {
|
|
1663
|
+
title: buildPageTitle("GitHub Sync — Pick Repository", navData.siteName),
|
|
1664
|
+
navData,
|
|
1665
|
+
content: (
|
|
1666
|
+
<>
|
|
1667
|
+
<AdminBreadcrumb
|
|
1668
|
+
parent={breadcrumbLabel(c, "settings")}
|
|
1669
|
+
parentHref={publicPath(c, "/settings")}
|
|
1670
|
+
current={breadcrumbLabel(c, "githubSync")}
|
|
1671
|
+
/>
|
|
1672
|
+
<jant-repo-picker
|
|
1673
|
+
labels={labels}
|
|
1674
|
+
api-base={`${base}/app`}
|
|
1675
|
+
connect-url={`${base}/app/connect`}
|
|
1676
|
+
install-url={`${base}/app/install`}
|
|
1677
|
+
cancel-url={publicPath(c, "/settings")}
|
|
1678
|
+
create-repo-name-hint={suggestedRepoName}
|
|
1679
|
+
>
|
|
1680
|
+
{/* SSR fallback while the Lit component upgrades. */}
|
|
1681
|
+
<div class="flex flex-col gap-6 max-w-lg">
|
|
1682
|
+
<div>
|
|
1683
|
+
<h2 class="text-lg font-medium mb-1">Pick a repository</h2>
|
|
1684
|
+
<p class="text-sm text-muted-foreground">Loading repositories…</p>
|
|
1685
|
+
</div>
|
|
1686
|
+
</div>
|
|
1687
|
+
</jant-repo-picker>
|
|
1688
|
+
</>
|
|
1689
|
+
),
|
|
1690
|
+
});
|
|
1691
|
+
});
|
|
1692
|
+
|
|
1693
|
+
/**
|
|
1694
|
+
* Derive a default repository name to prefill on github.com/new.
|
|
1695
|
+
*
|
|
1696
|
+
* Uses the site's host — the first DNS label is a stable, URL-safe
|
|
1697
|
+
* identifier tied to this specific Jant instance. Fallback to
|
|
1698
|
+
* "jant-site-sync" when the host parse fails so we never hand GitHub an
|
|
1699
|
+
* empty `name=`. The `-jant-sync` suffix disambiguates the sync mirror
|
|
1700
|
+
* from a user's own `{slug}-jant` source repo.
|
|
1701
|
+
*/
|
|
1702
|
+
function buildSuggestedRepoName(c: Context<Env>): string {
|
|
1703
|
+
let firstLabel = "";
|
|
1704
|
+
try {
|
|
1705
|
+
firstLabel = new URL(c.var.appConfig.siteUrl).host.split(".")[0] ?? "";
|
|
1706
|
+
} catch {
|
|
1707
|
+
/* fall through */
|
|
1708
|
+
}
|
|
1709
|
+
const slug = firstLabel
|
|
1710
|
+
.toLowerCase()
|
|
1711
|
+
.replace(/[^a-z0-9-_]+/g, "-")
|
|
1712
|
+
.replace(/^-+|-+$/g, "");
|
|
1713
|
+
return slug ? `${slug}-jant-sync` : "jant-site-sync";
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1716
|
+
function buildRepoPickerLabels(c: Context<Env>): string {
|
|
1717
|
+
const i18n = getI18n(c);
|
|
1718
|
+
return JSON.stringify({
|
|
1719
|
+
pageTitle: i18n._(
|
|
1720
|
+
msg({
|
|
1721
|
+
message: "Pick a repository",
|
|
1722
|
+
comment: "@context: GitHub sync picker — page heading",
|
|
1723
|
+
}),
|
|
1724
|
+
),
|
|
1725
|
+
pageSubtitle: i18n._(
|
|
1726
|
+
msg({
|
|
1727
|
+
message:
|
|
1728
|
+
"Choose the GitHub account and repository to sync with this site.",
|
|
1729
|
+
comment: "@context: GitHub sync picker — page subtitle",
|
|
1730
|
+
}),
|
|
1731
|
+
),
|
|
1732
|
+
ownerLabel: i18n._(
|
|
1733
|
+
msg({
|
|
1734
|
+
message: "Account",
|
|
1735
|
+
comment: "@context: GitHub sync picker — owner dropdown label",
|
|
1736
|
+
}),
|
|
1737
|
+
),
|
|
1738
|
+
ownerPlaceholder: i18n._(
|
|
1739
|
+
msg({
|
|
1740
|
+
message: "Select an account",
|
|
1741
|
+
comment:
|
|
1742
|
+
"@context: GitHub sync picker — owner dropdown placeholder when nothing is selected",
|
|
1743
|
+
}),
|
|
1744
|
+
),
|
|
1745
|
+
ownerEmpty: i18n._(
|
|
1746
|
+
msg({
|
|
1747
|
+
message: "No accounts authorized yet",
|
|
1748
|
+
comment:
|
|
1749
|
+
"@context: GitHub sync picker — shown when the owner list is empty",
|
|
1750
|
+
}),
|
|
1751
|
+
),
|
|
1752
|
+
installAnother: i18n._(
|
|
1753
|
+
msg({
|
|
1754
|
+
message: "+ Install on another account",
|
|
1755
|
+
comment:
|
|
1756
|
+
"@context: GitHub sync picker — entry at the bottom of the owner dropdown that starts a new install flow",
|
|
1757
|
+
}),
|
|
1758
|
+
),
|
|
1759
|
+
repositoryLabel: i18n._(
|
|
1760
|
+
msg({
|
|
1761
|
+
message: "Repository",
|
|
1762
|
+
comment: "@context: GitHub sync picker — repo dropdown label",
|
|
1763
|
+
}),
|
|
1764
|
+
),
|
|
1765
|
+
repoPlaceholderNoOwner: i18n._(
|
|
1766
|
+
msg({
|
|
1767
|
+
message: "Pick an account first",
|
|
1768
|
+
comment:
|
|
1769
|
+
"@context: GitHub sync picker — repo dropdown placeholder before an owner is chosen",
|
|
1770
|
+
}),
|
|
1771
|
+
),
|
|
1772
|
+
repoPlaceholder: i18n._(
|
|
1773
|
+
msg({
|
|
1774
|
+
message: "Select a repository",
|
|
1775
|
+
comment: "@context: GitHub sync picker — repo dropdown placeholder",
|
|
1776
|
+
}),
|
|
1777
|
+
),
|
|
1778
|
+
repoSearchPlaceholder: i18n._(
|
|
1779
|
+
msg({
|
|
1780
|
+
message: "Search repositories",
|
|
1781
|
+
comment: "@context: GitHub sync picker — search input placeholder",
|
|
1782
|
+
}),
|
|
1783
|
+
),
|
|
1784
|
+
repoEmpty: i18n._(
|
|
1785
|
+
msg({
|
|
1786
|
+
message: "No repositories match.",
|
|
1787
|
+
comment:
|
|
1788
|
+
"@context: GitHub sync picker — empty state when search returns nothing",
|
|
1789
|
+
}),
|
|
1790
|
+
),
|
|
1791
|
+
repoLoading: i18n._(
|
|
1792
|
+
msg({
|
|
1793
|
+
message: "Loading…",
|
|
1794
|
+
comment: "@context: GitHub sync picker — loading state",
|
|
1795
|
+
}),
|
|
1796
|
+
),
|
|
1797
|
+
repoShowingOf: i18n._(
|
|
1798
|
+
msg({
|
|
1799
|
+
message: "Showing {shown} of {total}",
|
|
1800
|
+
comment:
|
|
1801
|
+
"@context: GitHub sync picker — paginated repo count hint. Placeholders {shown} and {total} are integers.",
|
|
1802
|
+
}),
|
|
1803
|
+
),
|
|
1804
|
+
repoSearchHint: i18n._(
|
|
1805
|
+
msg({
|
|
1806
|
+
message: "type to search all",
|
|
1807
|
+
comment:
|
|
1808
|
+
"@context: GitHub sync picker — hint telling the user typing will search the full list",
|
|
1809
|
+
}),
|
|
1810
|
+
),
|
|
1811
|
+
refreshRepos: i18n._(
|
|
1812
|
+
msg({
|
|
1813
|
+
message: "Refresh repository list",
|
|
1814
|
+
comment:
|
|
1815
|
+
"@context: GitHub sync picker — tooltip for the refresh button next to the search input",
|
|
1816
|
+
}),
|
|
1817
|
+
),
|
|
1818
|
+
createOnGitHub: i18n._(
|
|
1819
|
+
msg({
|
|
1820
|
+
message: "Create a new repository on GitHub",
|
|
1821
|
+
comment:
|
|
1822
|
+
"@context: GitHub sync picker — entry that opens github.com/new in a new tab",
|
|
1823
|
+
}),
|
|
1824
|
+
),
|
|
1825
|
+
createOnGitHubHint: i18n._(
|
|
1826
|
+
msg({
|
|
1827
|
+
message: "We'll prefill the name {name}. The list refreshes on return.",
|
|
1828
|
+
comment:
|
|
1829
|
+
"@context: GitHub sync picker — hint under the create-on-github entry. Placeholder {name} is the suggested repo name.",
|
|
1830
|
+
}),
|
|
1831
|
+
),
|
|
1832
|
+
classifyLoading: i18n._(
|
|
1833
|
+
msg({
|
|
1834
|
+
message: "Checking repository…",
|
|
1835
|
+
comment:
|
|
1836
|
+
"@context: GitHub sync picker — status text while classifying the picked repo",
|
|
1837
|
+
}),
|
|
1838
|
+
),
|
|
1839
|
+
classificationEmpty: i18n._(
|
|
1840
|
+
msg({
|
|
1841
|
+
message: "Empty repository. Ready to connect.",
|
|
1842
|
+
comment:
|
|
1843
|
+
"@context: GitHub sync picker — shown when the selected repo has no commits",
|
|
1844
|
+
}),
|
|
1845
|
+
),
|
|
1846
|
+
classificationOwned: i18n._(
|
|
1847
|
+
msg({
|
|
1848
|
+
message: "This repository is already backing up this site.",
|
|
1849
|
+
comment:
|
|
1850
|
+
"@context: GitHub sync picker — shown when the selected repo's marker matches this site",
|
|
1851
|
+
}),
|
|
1852
|
+
),
|
|
1853
|
+
classificationOwnedByOther: i18n._(
|
|
1854
|
+
msg({
|
|
1855
|
+
message:
|
|
1856
|
+
"This repository is already backing up another Jant site ({host}). Pick a different repository.",
|
|
1857
|
+
comment:
|
|
1858
|
+
"@context: GitHub sync picker — blocking message when marker belongs to another site. Placeholder {host} is the other site's host.",
|
|
1859
|
+
}),
|
|
1860
|
+
),
|
|
1861
|
+
classificationForeign: i18n._(
|
|
1862
|
+
msg({
|
|
1863
|
+
message: "This repository has existing content.",
|
|
1864
|
+
comment:
|
|
1865
|
+
"@context: GitHub sync picker — heading above the foreign-repo confirmation",
|
|
1866
|
+
}),
|
|
1867
|
+
),
|
|
1868
|
+
confirmHeading: i18n._(
|
|
1869
|
+
msg({
|
|
1870
|
+
message: "This repository already has commits",
|
|
1871
|
+
comment:
|
|
1872
|
+
"@context: GitHub sync picker — confirmation card heading for foreign repos",
|
|
1873
|
+
}),
|
|
1874
|
+
),
|
|
1875
|
+
confirmBody: i18n._(
|
|
1876
|
+
msg({
|
|
1877
|
+
message:
|
|
1878
|
+
"Connecting will sync your site onto {repo}'s default branch on top of its existing history. Existing files outside Jant's managed paths are kept. This can't be undone.",
|
|
1879
|
+
comment:
|
|
1880
|
+
"@context: GitHub sync picker — explanatory body for foreign-repo confirmation. Placeholder {repo} is the owner/repo slug.",
|
|
1881
|
+
}),
|
|
1882
|
+
),
|
|
1883
|
+
confirmInputLabel: i18n._(
|
|
1884
|
+
msg({
|
|
1885
|
+
message: "Type {repo} to confirm",
|
|
1886
|
+
comment:
|
|
1887
|
+
"@context: GitHub sync picker — label above the typed-confirmation input",
|
|
1888
|
+
}),
|
|
1889
|
+
),
|
|
1890
|
+
confirmInputPlaceholder: i18n._(
|
|
1891
|
+
msg({
|
|
1892
|
+
message: "owner/repo",
|
|
1893
|
+
comment:
|
|
1894
|
+
"@context: GitHub sync picker — placeholder text showing the expected input shape",
|
|
1895
|
+
}),
|
|
1896
|
+
),
|
|
1897
|
+
cancel: i18n._(
|
|
1898
|
+
msg({
|
|
1899
|
+
message: "Cancel",
|
|
1900
|
+
comment: "@context: GitHub sync picker — cancel action",
|
|
1901
|
+
}),
|
|
1902
|
+
),
|
|
1903
|
+
connect: i18n._(
|
|
1904
|
+
msg({
|
|
1905
|
+
message: "Connect",
|
|
1906
|
+
comment: "@context: GitHub sync picker — primary action button",
|
|
1907
|
+
}),
|
|
1908
|
+
),
|
|
1909
|
+
connecting: i18n._(
|
|
1910
|
+
msg({
|
|
1911
|
+
message: "Connecting…",
|
|
1912
|
+
comment:
|
|
1913
|
+
"@context: GitHub sync picker — primary action button while a request is in flight",
|
|
1914
|
+
}),
|
|
1915
|
+
),
|
|
1916
|
+
privateBadge: i18n._(
|
|
1917
|
+
msg({
|
|
1918
|
+
message: "Private",
|
|
1919
|
+
comment: "@context: GitHub sync picker — badge next to private repos",
|
|
1920
|
+
}),
|
|
1921
|
+
),
|
|
1922
|
+
connectionFailed: i18n._(
|
|
1923
|
+
msg({
|
|
1924
|
+
message: "Couldn't connect. Check the error and try again.",
|
|
1925
|
+
comment:
|
|
1926
|
+
"@context: GitHub sync picker — generic failure message for Connect",
|
|
1927
|
+
}),
|
|
1928
|
+
),
|
|
1929
|
+
retry: i18n._(
|
|
1930
|
+
msg({
|
|
1931
|
+
message: "Try again",
|
|
1932
|
+
comment: "@context: GitHub sync picker — retry button",
|
|
1933
|
+
}),
|
|
1934
|
+
),
|
|
1935
|
+
}).replace(/</g, "\\u003c");
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1938
|
+
/**
|
|
1939
|
+
* Finalize the App connection: validate access, classify the target repo
|
|
1940
|
+
* to gate foreign/conflicting choices behind explicit user confirmation,
|
|
1941
|
+
* then persist the installation id and chosen repo and register the webhook.
|
|
1942
|
+
*
|
|
1943
|
+
* Accepts both classic form submits (redirects on success) and JSON
|
|
1944
|
+
* requests (returns JSON). Phase 3's picker component uses the JSON path.
|
|
1945
|
+
*/
|
|
1946
|
+
settingsRoutes.post("/github-sync/app/connect", async (c) => {
|
|
1947
|
+
const app = getGitHubAppConfig(c.env);
|
|
1948
|
+
if (!app) {
|
|
1949
|
+
return c.text("GitHub App is not configured on this deployment.", 404);
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1952
|
+
const wantsJson = c.req.header("content-type")?.includes("application/json");
|
|
1953
|
+
const body = wantsJson
|
|
1954
|
+
? ((await c.req.json().catch(() => ({}))) as Record<string, unknown>)
|
|
1955
|
+
: await c.req.parseBody();
|
|
1956
|
+
|
|
1957
|
+
const installationId = String(body.installationId ?? "").trim();
|
|
1958
|
+
const repo = String(body.repo ?? "").trim();
|
|
1959
|
+
const confirmForeign =
|
|
1960
|
+
body.confirmForeign === true || body.confirmForeign === "true";
|
|
1961
|
+
if (!installationId || !repo) {
|
|
1962
|
+
return wantsJson
|
|
1963
|
+
? c.json({ error: "Missing installationId or repo." }, 400)
|
|
1964
|
+
: c.text("Missing installationId or repo.", 400);
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1967
|
+
const { parseRepoSlug, createGitHubClient } =
|
|
1968
|
+
await import("../../lib/github-api.js");
|
|
1969
|
+
const parsed = parseRepoSlug(repo);
|
|
1970
|
+
if (!parsed) {
|
|
1971
|
+
return wantsJson
|
|
1972
|
+
? c.json({ error: "Invalid repository format." }, 400)
|
|
1973
|
+
: c.text("Invalid repository format.", 400);
|
|
1974
|
+
}
|
|
1975
|
+
|
|
1976
|
+
// Classify the repo before we commit any state: empty and already-owned
|
|
1977
|
+
// targets proceed immediately; foreign or other-site targets require an
|
|
1978
|
+
// explicit `confirmForeign` flag from the caller (a typed confirmation
|
|
1979
|
+
// in the UI) to avoid silently pushing onto unrelated histories.
|
|
1980
|
+
const { classifyRepoForSync } = await import("../../services/github-sync.js");
|
|
1981
|
+
const ghClient = createGitHubClient(() =>
|
|
1982
|
+
getInstallationTokenFromApp(app, installationId),
|
|
1983
|
+
);
|
|
1984
|
+
let classification;
|
|
1985
|
+
try {
|
|
1986
|
+
classification = await classifyRepoForSync(
|
|
1987
|
+
ghClient,
|
|
1988
|
+
parsed.owner,
|
|
1989
|
+
parsed.repo,
|
|
1990
|
+
c.var.currentSite.id,
|
|
1991
|
+
);
|
|
1992
|
+
} catch (err) {
|
|
1993
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
1994
|
+
return wantsJson
|
|
1995
|
+
? c.json({ error: `Could not read repository: ${detail}` }, 500)
|
|
1996
|
+
: c.text(`Could not read repository: ${detail}`, 500);
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1999
|
+
if (classification.kind === "owned-by-other-site") {
|
|
2000
|
+
const msg = `This repository is already backing up another Jant site (${classification.marker.site_host}). Pick a different repository.`;
|
|
2001
|
+
return wantsJson
|
|
2002
|
+
? c.json(
|
|
2003
|
+
{
|
|
2004
|
+
error: msg,
|
|
2005
|
+
classification: "owned-by-other-site",
|
|
2006
|
+
marker: classification.marker,
|
|
2007
|
+
},
|
|
2008
|
+
409,
|
|
2009
|
+
)
|
|
2010
|
+
: c.text(msg, 409);
|
|
2011
|
+
}
|
|
2012
|
+
|
|
2013
|
+
if (classification.kind === "foreign" && !confirmForeign) {
|
|
2014
|
+
const msg = `${repo} already has commits but isn't managed by Jant. Re-submit with confirmation to push on top of its history.`;
|
|
2015
|
+
return wantsJson
|
|
2016
|
+
? c.json(
|
|
2017
|
+
{
|
|
2018
|
+
error: msg,
|
|
2019
|
+
classification: "foreign",
|
|
2020
|
+
defaultBranch: classification.defaultBranch,
|
|
2021
|
+
},
|
|
2022
|
+
409,
|
|
2023
|
+
)
|
|
2024
|
+
: c.text(msg, 409);
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
// Persist config before creating webhook so the sync service can load it.
|
|
2028
|
+
await c.var.services.settings.set("GITHUB_SYNC_AUTH_MODE", "app");
|
|
2029
|
+
await c.var.services.settings.set(
|
|
2030
|
+
"GITHUB_SYNC_APP_INSTALLATION_ID",
|
|
2031
|
+
installationId,
|
|
2032
|
+
);
|
|
2033
|
+
await c.var.services.settings.set("GITHUB_SYNC_REPO", repo);
|
|
2034
|
+
await c.var.services.settings.set("GITHUB_SYNC_TOKEN", "");
|
|
2035
|
+
await c.var.services.settings.set("GITHUB_SYNC_ENABLED", "true");
|
|
2036
|
+
|
|
2037
|
+
const { createGitHubSyncService } =
|
|
2038
|
+
await import("../../services/github-sync.js");
|
|
2039
|
+
const syncService = createGitHubSyncService(
|
|
2040
|
+
c.var.services,
|
|
2041
|
+
c.var.currentSite.id,
|
|
2042
|
+
await buildSyncSiteConfig(c),
|
|
2043
|
+
{ storage: c.var.storage, githubApp: app },
|
|
2044
|
+
);
|
|
2045
|
+
const siteUrl = c.var.appConfig.siteUrl.replace(/\/+$/, "");
|
|
2046
|
+
try {
|
|
2047
|
+
await syncService.setupWebhook(`${siteUrl}/api/github-sync/webhook`);
|
|
2048
|
+
} catch (err) {
|
|
2049
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
2050
|
+
const msg = `Connected, but webhook creation failed: ${detail}. You may need to create it manually.`;
|
|
2051
|
+
return wantsJson ? c.json({ error: msg }, 500) : c.text(msg, 500);
|
|
2052
|
+
}
|
|
2053
|
+
|
|
2054
|
+
// Kick off an initial push so the user's content lands in the repo
|
|
2055
|
+
// immediately. We can't use the queue path: CF Queue isn't wired up
|
|
2056
|
+
// for most self-hosted deployments, and the noop fallback would
|
|
2057
|
+
// silently drop the job. Instead, set PENDING and run the sync in
|
|
2058
|
+
// the background via waitUntil. The status page reads PENDING and
|
|
2059
|
+
// polls until it clears, so the user sees "Syncing…" right away.
|
|
2060
|
+
await markSyncPending(c.var.services.settings);
|
|
2061
|
+
const initialSync = runBackgroundSync(c.var.services.settings, syncService);
|
|
2062
|
+
try {
|
|
2063
|
+
c.executionCtx?.waitUntil(initialSync);
|
|
2064
|
+
} catch {
|
|
2065
|
+
// executionCtx unavailable (tests / Node) — let the promise resolve
|
|
2066
|
+
// on its own; response still returns immediately.
|
|
2067
|
+
}
|
|
2068
|
+
|
|
2069
|
+
const redirect = publicPath(c, "/settings/github-sync");
|
|
2070
|
+
return wantsJson ? c.json({ ok: true, redirect }) : c.redirect(redirect);
|
|
2071
|
+
});
|
|
2072
|
+
|
|
2073
|
+
// ---------------------------------------------------------------------------
|
|
2074
|
+
// GitHub App picker JSON API (consumed by the jant-repo-picker component)
|
|
2075
|
+
// ---------------------------------------------------------------------------
|
|
2076
|
+
|
|
2077
|
+
/**
|
|
2078
|
+
* Helper: load the GitHub App config or short-circuit with a 404 JSON error.
|
|
2079
|
+
*/
|
|
2080
|
+
type RequireAppResult =
|
|
2081
|
+
| { app: import("../../lib/env.js").GitHubAppEnvConfig; response: null }
|
|
2082
|
+
| { app: null; response: Response };
|
|
2083
|
+
|
|
2084
|
+
function requireGitHubApp(c: Context<Env>): RequireAppResult {
|
|
2085
|
+
const app = getGitHubAppConfig(c.env);
|
|
2086
|
+
if (!app) {
|
|
2087
|
+
return {
|
|
2088
|
+
app: null,
|
|
2089
|
+
response: c.json(
|
|
2090
|
+
{ error: "GitHub App is not configured on this deployment." },
|
|
2091
|
+
404,
|
|
2092
|
+
),
|
|
2093
|
+
};
|
|
2094
|
+
}
|
|
2095
|
+
return { app, response: null };
|
|
2096
|
+
}
|
|
2097
|
+
|
|
2098
|
+
/**
|
|
2099
|
+
* Dynamic import shim for `getInstallationToken`. Used by `/connect` to
|
|
2100
|
+
* build a client for classification without adding the helper to the
|
|
2101
|
+
* top-level imports (the module already lazy-imports github-api).
|
|
2102
|
+
*/
|
|
2103
|
+
async function getInstallationTokenFromApp(
|
|
2104
|
+
app: import("../../lib/env.js").GitHubAppEnvConfig,
|
|
2105
|
+
installationId: string,
|
|
2106
|
+
): Promise<string> {
|
|
2107
|
+
const { getInstallationToken } = await import("../../lib/github-app.js");
|
|
2108
|
+
return getInstallationToken(app, installationId);
|
|
2109
|
+
}
|
|
2110
|
+
|
|
2111
|
+
/** List GitHub App installations authorized for this site. */
|
|
2112
|
+
settingsRoutes.get("/github-sync/app/installations", async (c) => {
|
|
2113
|
+
const installations =
|
|
2114
|
+
await c.var.services.githubAppInstallations.listInstallationsForSite(
|
|
2115
|
+
c.var.currentSite.id,
|
|
2116
|
+
);
|
|
2117
|
+
return c.json({
|
|
2118
|
+
installations: installations.map((entry) => ({
|
|
2119
|
+
installationId: entry.installationId,
|
|
2120
|
+
account: entry.account,
|
|
2121
|
+
addedAt: entry.addedAt,
|
|
2122
|
+
})),
|
|
2123
|
+
});
|
|
2124
|
+
});
|
|
2125
|
+
|
|
2126
|
+
/**
|
|
2127
|
+
* List (or search) repositories accessible via an installation.
|
|
2128
|
+
*
|
|
2129
|
+
* Query params:
|
|
2130
|
+
* - installationId (required)
|
|
2131
|
+
* - page (default 1) — only used when `q` is empty
|
|
2132
|
+
* - q (optional) — when non-empty switches to GitHub search API
|
|
2133
|
+
*
|
|
2134
|
+
* 401 from GitHub indicates the installation was removed; we lazy-clean
|
|
2135
|
+
* the stored entry and return a 410 so the UI can refresh its list.
|
|
2136
|
+
*/
|
|
2137
|
+
settingsRoutes.get("/github-sync/app/repos", async (c) => {
|
|
2138
|
+
const { app, response } = requireGitHubApp(c);
|
|
2139
|
+
if (!app) return response;
|
|
2140
|
+
|
|
2141
|
+
const installationId = c.req.query("installationId")?.trim();
|
|
2142
|
+
if (!installationId) {
|
|
2143
|
+
return c.json({ error: "Missing installationId." }, 400);
|
|
2144
|
+
}
|
|
2145
|
+
const q = c.req.query("q")?.trim() ?? "";
|
|
2146
|
+
const pageParam = Number(c.req.query("page") ?? "1");
|
|
2147
|
+
const page =
|
|
2148
|
+
Number.isFinite(pageParam) && pageParam > 0 ? Math.floor(pageParam) : 1;
|
|
2149
|
+
|
|
2150
|
+
try {
|
|
2151
|
+
if (q) {
|
|
2152
|
+
const installations =
|
|
2153
|
+
await c.var.services.githubAppInstallations.listInstallationsForSite(
|
|
2154
|
+
c.var.currentSite.id,
|
|
2155
|
+
);
|
|
2156
|
+
const installation = installations.find(
|
|
2157
|
+
(i) => i.installationId === installationId,
|
|
2158
|
+
);
|
|
2159
|
+
if (!installation) {
|
|
2160
|
+
return c.json({ error: "Unknown installation." }, 404);
|
|
2161
|
+
}
|
|
2162
|
+
const result = await searchInstallationRepos(
|
|
2163
|
+
app,
|
|
2164
|
+
installationId,
|
|
2165
|
+
installation.account.login,
|
|
2166
|
+
q,
|
|
2167
|
+
);
|
|
2168
|
+
return c.json({
|
|
2169
|
+
repos: result.repos,
|
|
2170
|
+
totalCount: result.totalCount,
|
|
2171
|
+
hasMore: false, // search API doesn't page for us here
|
|
2172
|
+
nextPage: null,
|
|
2173
|
+
mode: "search",
|
|
2174
|
+
});
|
|
2175
|
+
}
|
|
2176
|
+
|
|
2177
|
+
const result = await listInstallationReposPage(app, installationId, page);
|
|
2178
|
+
return c.json({
|
|
2179
|
+
repos: result.repos,
|
|
2180
|
+
totalCount: result.totalCount,
|
|
2181
|
+
hasMore: result.hasMore,
|
|
2182
|
+
nextPage: result.nextPage,
|
|
2183
|
+
mode: "list",
|
|
2184
|
+
});
|
|
2185
|
+
} catch (err) {
|
|
2186
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
2187
|
+
// GitHub returns 401 when an installation was uninstalled on their
|
|
2188
|
+
// side. Clean up our cached entry so the UI stops showing a dead owner.
|
|
2189
|
+
if (/\b401\b/.test(detail) || /\b404\b/.test(detail)) {
|
|
2190
|
+
await c.var.services.githubAppInstallations.removeInstallation(
|
|
2191
|
+
installationId,
|
|
2192
|
+
c.var.currentSite.id,
|
|
2193
|
+
);
|
|
2194
|
+
return c.json(
|
|
2195
|
+
{ error: "Installation is no longer accessible.", removed: true },
|
|
2196
|
+
410,
|
|
2197
|
+
);
|
|
2198
|
+
}
|
|
2199
|
+
return c.json({ error: detail }, 500);
|
|
2200
|
+
}
|
|
2201
|
+
});
|
|
2202
|
+
|
|
2203
|
+
/**
|
|
2204
|
+
* Classify a repo: empty / owned / owned-by-other-site / foreign.
|
|
2205
|
+
*
|
|
2206
|
+
* The picker calls this when the user picks a repo so it can show an
|
|
2207
|
+
* appropriate confirmation UI before submitting `/connect`.
|
|
2208
|
+
*/
|
|
2209
|
+
settingsRoutes.post("/github-sync/app/classify", async (c) => {
|
|
2210
|
+
const { app, response } = requireGitHubApp(c);
|
|
2211
|
+
if (!app) return response;
|
|
2212
|
+
|
|
2213
|
+
const body = (await c.req.json().catch(() => ({}))) as Record<
|
|
2214
|
+
string,
|
|
2215
|
+
unknown
|
|
2216
|
+
>;
|
|
2217
|
+
const installationId = String(body.installationId ?? "").trim();
|
|
2218
|
+
const repo = String(body.repo ?? "").trim();
|
|
2219
|
+
if (!installationId || !repo) {
|
|
2220
|
+
return c.json({ error: "Missing installationId or repo." }, 400);
|
|
2221
|
+
}
|
|
2222
|
+
|
|
2223
|
+
const { parseRepoSlug, createGitHubClient } =
|
|
2224
|
+
await import("../../lib/github-api.js");
|
|
2225
|
+
const parsed = parseRepoSlug(repo);
|
|
2226
|
+
if (!parsed) {
|
|
2227
|
+
return c.json({ error: "Invalid repository format." }, 400);
|
|
2228
|
+
}
|
|
2229
|
+
|
|
2230
|
+
const { classifyRepoForSync } = await import("../../services/github-sync.js");
|
|
2231
|
+
const client = createGitHubClient(() =>
|
|
2232
|
+
getInstallationTokenFromApp(app, installationId),
|
|
2233
|
+
);
|
|
2234
|
+
try {
|
|
2235
|
+
const classification = await classifyRepoForSync(
|
|
2236
|
+
client,
|
|
2237
|
+
parsed.owner,
|
|
2238
|
+
parsed.repo,
|
|
2239
|
+
c.var.currentSite.id,
|
|
2240
|
+
);
|
|
2241
|
+
return c.json({ classification });
|
|
2242
|
+
} catch (err) {
|
|
2243
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
2244
|
+
return c.json({ error: detail }, 500);
|
|
2245
|
+
}
|
|
2246
|
+
});
|
|
2247
|
+
|
|
2248
|
+
settingsRoutes.get("/github-sync", async (c) => {
|
|
2249
|
+
const [
|
|
2250
|
+
enabled,
|
|
2251
|
+
repo,
|
|
2252
|
+
lastPushSha,
|
|
2253
|
+
webhookId,
|
|
2254
|
+
lastPushAt,
|
|
2255
|
+
authMode,
|
|
2256
|
+
lastError,
|
|
2257
|
+
] = await Promise.all([
|
|
2258
|
+
c.var.services.settings.get("GITHUB_SYNC_ENABLED"),
|
|
2259
|
+
c.var.services.settings.get("GITHUB_SYNC_REPO"),
|
|
2260
|
+
c.var.services.settings.get("GITHUB_SYNC_LAST_PUSH_SHA"),
|
|
2261
|
+
c.var.services.settings.get("GITHUB_SYNC_WEBHOOK_ID"),
|
|
2262
|
+
c.var.services.settings.get("GITHUB_SYNC_LAST_PUSH_AT"),
|
|
2263
|
+
c.var.services.settings.get("GITHUB_SYNC_AUTH_MODE"),
|
|
2264
|
+
c.var.services.settings.get("GITHUB_SYNC_LAST_ERROR"),
|
|
2265
|
+
]);
|
|
2266
|
+
// Use isSyncPending (not raw flag) so a worker that died mid-push
|
|
2267
|
+
// doesn't leave the UI stuck on "Syncing…" forever.
|
|
2268
|
+
const pending = await isSyncPending(c.var.services.settings);
|
|
2269
|
+
|
|
2270
|
+
const status: GitHubSyncStatus = {
|
|
2271
|
+
enabled: enabled === "true",
|
|
2272
|
+
repo: repo ?? null,
|
|
2273
|
+
lastPushSha: lastPushSha ?? null,
|
|
2274
|
+
webhookId: webhookId ?? null,
|
|
2275
|
+
lastPushAt: lastPushAt ? Number(lastPushAt) : null,
|
|
2276
|
+
authMode: authMode === "app" ? "app" : "pat",
|
|
2277
|
+
appConfigured: getGitHubAppConfig(c.env) !== null,
|
|
2278
|
+
pending,
|
|
2279
|
+
lastError: lastError || null,
|
|
2280
|
+
};
|
|
2281
|
+
|
|
2282
|
+
const navData = await getNavigationData(c);
|
|
2283
|
+
|
|
2284
|
+
return renderPublicPage(c, {
|
|
2285
|
+
title: buildPageTitle("GitHub Sync", navData.siteName),
|
|
2286
|
+
navData,
|
|
2287
|
+
content: (
|
|
2288
|
+
<>
|
|
2289
|
+
<AdminBreadcrumb
|
|
2290
|
+
parent={breadcrumbLabel(c, "settings")}
|
|
2291
|
+
parentHref={publicPath(c, "/settings")}
|
|
2292
|
+
current={breadcrumbLabel(c, "githubSync")}
|
|
2293
|
+
/>
|
|
2294
|
+
<GitHubSyncContent
|
|
2295
|
+
status={status}
|
|
2296
|
+
sitePathPrefix={c.var.appConfig.sitePathPrefix}
|
|
2297
|
+
/>
|
|
2298
|
+
</>
|
|
2299
|
+
),
|
|
2300
|
+
});
|
|
2301
|
+
});
|