@jant/core 0.3.40 → 1.0.0

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