@jant/core 0.3.39 → 0.3.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1163) hide show
  1. package/README.md +21 -33
  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-BfoG98VD.js → app-Cu3lveYI.js} +24017 -23072
  22. package/dist/app-DzCB4yOp.js +5 -0
  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 +65 -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} +145 -8
  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 -15
  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} +298 -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 +80 -0
  545. package/src/lib/__tests__/constants.test.ts +1 -1
  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 +35 -0
  560. package/src/lib/__tests__/slug-format.test.ts +8 -0
  561. package/src/lib/__tests__/summary.test.ts +400 -1
  562. package/src/lib/__tests__/theme.test.ts +41 -2
  563. package/src/lib/__tests__/timeline.test.ts +114 -26
  564. package/src/lib/__tests__/tiptap-render.test.ts +224 -0
  565. package/src/lib/__tests__/tiptap-to-markdown.test.ts +51 -0
  566. package/src/lib/__tests__/video-playback.test.ts +48 -0
  567. package/src/lib/__tests__/view.test.ts +115 -8
  568. package/src/lib/__tests__/worker-response-cache.test.ts +311 -0
  569. package/src/lib/__tests__/youtube.test.ts +99 -0
  570. package/src/lib/api-media.ts +55 -0
  571. package/src/lib/api-posts.ts +99 -0
  572. package/src/lib/api-search.ts +47 -0
  573. package/src/lib/api-settings.ts +122 -0
  574. package/src/lib/asset-path.ts +20 -2
  575. package/src/lib/collection-groups.ts +70 -0
  576. package/src/lib/collection-paths.ts +40 -0
  577. package/src/lib/constants.ts +3 -4
  578. package/src/lib/csp-builder.ts +99 -0
  579. package/src/lib/decorative-quote-mark.ts +11 -0
  580. package/src/lib/display-text.ts +56 -0
  581. package/src/lib/embed-providers.ts +289 -0
  582. package/src/lib/embed-render.ts +151 -0
  583. package/src/lib/env.ts +70 -1
  584. package/src/lib/excerpt.ts +11 -2
  585. package/src/lib/feed.ts +89 -66
  586. package/src/lib/footnotes.ts +146 -0
  587. package/src/lib/github-api.ts +423 -0
  588. package/src/lib/github-app-state.ts +135 -0
  589. package/src/lib/github-app.ts +487 -0
  590. package/src/lib/github-sync-queue-handler.ts +69 -0
  591. package/src/lib/github-sync-site-config.ts +57 -0
  592. package/src/lib/github-sync-status.tsx +87 -0
  593. package/src/lib/github-sync-trigger.ts +199 -0
  594. package/src/lib/github-sync-worker.ts +72 -0
  595. package/src/lib/hosted-signin.ts +1 -1
  596. package/src/lib/hugo-markdown.ts +255 -0
  597. package/src/lib/job-queue-cf.ts +18 -0
  598. package/src/lib/job-queue-db.ts +149 -0
  599. package/src/lib/job-queue.ts +35 -0
  600. package/src/lib/markdown-manager.ts +864 -0
  601. package/src/lib/markdown-to-tiptap.ts +4 -323
  602. package/src/lib/markdown.ts +16 -24
  603. package/src/lib/media-helpers.ts +1 -0
  604. package/src/lib/navigation.ts +40 -8
  605. package/src/lib/post-display.ts +10 -22
  606. package/src/lib/public-storage.ts +6 -1
  607. package/src/lib/render.tsx +5 -2
  608. package/src/lib/resolve-config.ts +23 -11
  609. package/src/lib/rich-image.ts +89 -0
  610. package/src/lib/schemas.ts +186 -12
  611. package/src/lib/slug-format.ts +8 -0
  612. package/src/lib/summary.ts +125 -18
  613. package/src/lib/theme.ts +119 -0
  614. package/src/lib/timeline.ts +91 -63
  615. package/src/lib/tiptap-render.ts +265 -126
  616. package/src/lib/tiptap-to-markdown.ts +6 -329
  617. package/src/lib/upload.ts +97 -6
  618. package/src/lib/version.ts +18 -0
  619. package/src/lib/video-playback.ts +73 -0
  620. package/src/lib/view.ts +130 -90
  621. package/src/lib/webhook-signature.ts +65 -0
  622. package/src/lib/worker-response-cache.ts +220 -0
  623. package/src/lib/youtube.ts +119 -0
  624. package/src/middleware/__tests__/onboarding.test.ts +3 -3
  625. package/src/middleware/__tests__/secure-headers.test.ts +28 -1
  626. package/src/middleware/config.ts +7 -10
  627. package/src/middleware/cors.ts +49 -0
  628. package/src/middleware/secure-headers.ts +45 -55
  629. package/src/node/__tests__/cli-migrate.test.ts +111 -0
  630. package/src/node/__tests__/cli-runtime-target.test.ts +1 -1
  631. package/src/node/__tests__/cli-site-token-env.test.ts +2 -2
  632. package/src/preset.css +316 -12
  633. package/src/routes/__tests__/compose.test.ts +3 -1
  634. package/src/routes/api/__tests__/collections.test.ts +81 -27
  635. package/src/routes/api/__tests__/mcp.test.ts +389 -0
  636. package/src/routes/api/__tests__/nav-items.test.ts +163 -2
  637. package/src/routes/api/__tests__/posts.test.ts +190 -0
  638. package/src/routes/api/__tests__/settings.test.ts +99 -3
  639. package/src/routes/api/__tests__/upload-multipart.test.ts +10 -2
  640. package/src/routes/api/__tests__/upload.test.ts +132 -0
  641. package/src/routes/api/__tests__/uploads.test.ts +5 -3
  642. package/src/routes/api/collections.ts +67 -26
  643. package/src/routes/api/custom-urls.ts +1 -0
  644. package/src/routes/api/export.ts +8 -4
  645. package/src/routes/api/github-sync.tsx +380 -0
  646. package/src/routes/api/internal/__tests__/sites.test.ts +45 -2
  647. package/src/routes/api/internal/sites.ts +2 -0
  648. package/src/routes/api/internal/text-attachments.ts +60 -0
  649. package/src/routes/api/internal/uploads.ts +5 -1
  650. package/src/routes/api/mcp.ts +30 -0
  651. package/src/routes/api/nav-items.ts +30 -11
  652. package/src/routes/api/palette.ts +17 -0
  653. package/src/routes/api/posts.ts +37 -106
  654. package/src/routes/api/public/__tests__/posts.test.ts +372 -0
  655. package/src/routes/api/public/posts.ts +302 -0
  656. package/src/routes/api/search.ts +1 -47
  657. package/src/routes/api/settings.ts +44 -58
  658. package/src/routes/api/upload-multipart.ts +15 -5
  659. package/src/routes/api/upload.ts +62 -49
  660. package/src/routes/api/uploads.ts +1 -0
  661. package/src/routes/auth/__tests__/hosted-sso.test.ts +33 -0
  662. package/src/routes/auth/__tests__/setup.test.ts +29 -4
  663. package/src/routes/auth/hosted-sso-expired-page.tsx +89 -0
  664. package/src/routes/auth/hosted-sso.ts +42 -1
  665. package/src/routes/auth/reset.tsx +46 -32
  666. package/src/routes/auth/setup.tsx +53 -43
  667. package/src/routes/auth/signin.tsx +34 -24
  668. package/src/routes/compose.tsx +114 -1
  669. package/src/routes/dash/__tests__/font-theme.test.ts +6 -27
  670. package/src/routes/dash/__tests__/settings-avatar.test.ts +2 -3
  671. package/src/routes/dash/custom-urls.tsx +250 -112
  672. package/src/routes/dash/settings.tsx +1179 -70
  673. package/src/routes/feed/__tests__/{rss.test.ts → feed.test.ts} +62 -123
  674. package/src/routes/feed/feed.ts +386 -0
  675. package/src/routes/feed/manifest.ts +67 -0
  676. package/src/routes/feed/sitemap.ts +11 -2
  677. package/src/routes/pages/__tests__/collection-routing.test.ts +176 -0
  678. package/src/routes/pages/__tests__/collections.test.ts +2 -3
  679. package/src/routes/pages/__tests__/featured.test.ts +2 -3
  680. package/src/routes/pages/archive.tsx +523 -127
  681. package/src/routes/pages/collection.tsx +265 -59
  682. package/src/routes/pages/collections.tsx +5 -1
  683. package/src/routes/pages/home.tsx +2 -2
  684. package/src/routes/pages/latest.tsx +3 -3
  685. package/src/routes/pages/page.tsx +221 -3
  686. package/src/routes/pages/partials.tsx +5 -0
  687. package/src/routes/pages/search.tsx +17 -1
  688. package/src/routes/pages/theme-sample.tsx +1 -2
  689. package/src/runtime/__tests__/node.test.ts +1 -1
  690. package/src/runtime/__tests__/readiness.test.ts +89 -0
  691. package/src/runtime/readiness.ts +129 -0
  692. package/src/runtime/site.ts +9 -1
  693. package/src/services/__tests__/collection.test.ts +181 -53
  694. package/src/services/__tests__/github-app-installations.test.ts +181 -0
  695. package/src/services/__tests__/github-sync-classify.test.ts +189 -0
  696. package/src/services/__tests__/github-sync-push.test.ts +159 -0
  697. package/src/services/__tests__/media.test.ts +444 -7
  698. package/src/services/__tests__/navigation.test.ts +271 -14
  699. package/src/services/__tests__/post-timeline.test.ts +85 -17
  700. package/src/services/__tests__/post.test.ts +118 -10
  701. package/src/services/__tests__/settings.test.ts +43 -21
  702. package/src/services/auth.ts +2 -2
  703. package/src/services/bootstrap.ts +10 -7
  704. package/src/services/collection.ts +644 -191
  705. package/src/services/custom-url.ts +34 -7
  706. package/src/services/export-theme/assets/client-site.css +2 -0
  707. package/src/services/export-theme/assets/client-site.js +145 -0
  708. package/src/services/export-theme/layouts/_default/alias.html +49 -0
  709. package/src/services/export-theme/layouts/_default/baseof.html +21 -0
  710. package/src/services/export-theme/layouts/_default/list.html +104 -0
  711. package/src/services/export-theme/layouts/_default/rss.xml +160 -0
  712. package/src/services/export-theme/layouts/_default/single.html +15 -0
  713. package/src/services/export-theme/layouts/archive/list.html +31 -0
  714. package/src/services/export-theme/layouts/collection/single.html +19 -0
  715. package/src/services/export-theme/layouts/collections/list.html +111 -0
  716. package/src/services/export-theme/layouts/featured/list.html +28 -0
  717. package/src/services/export-theme/layouts/index.html +41 -0
  718. package/src/services/export-theme/layouts/partials/feed-post-content.xml +107 -0
  719. package/src/services/export-theme/layouts/partials/footer.html +32 -0
  720. package/src/services/export-theme/layouts/partials/head.html +52 -0
  721. package/src/services/export-theme/layouts/partials/header.html +115 -0
  722. package/src/services/export-theme/layouts/partials/media-gallery.html +246 -0
  723. package/src/services/export-theme/layouts/partials/pagination.html +55 -0
  724. package/src/services/export-theme/layouts/partials/post-card.html +127 -0
  725. package/src/services/export-theme/layouts/partials/reply.html +91 -0
  726. package/src/services/export-theme/layouts/partials/thread-preview.html +82 -0
  727. package/src/services/export-theme/layouts/post/list.html +129 -0
  728. package/src/services/export-theme/styles/main.css +1981 -0
  729. package/src/services/export-theme/theme.toml +12 -0
  730. package/src/services/export.ts +1365 -2412
  731. package/src/services/github-app-installations.ts +302 -0
  732. package/src/services/github-sync.ts +769 -0
  733. package/src/services/index.ts +15 -0
  734. package/src/services/mcp.ts +1324 -0
  735. package/src/services/media.ts +336 -25
  736. package/src/services/navigation.ts +115 -23
  737. package/src/services/path.ts +129 -28
  738. package/src/services/post.ts +779 -207
  739. package/src/services/search.ts +3 -0
  740. package/src/services/settings.ts +29 -15
  741. package/src/services/site-admin.ts +26 -6
  742. package/src/services/upload-session.ts +69 -34
  743. package/src/style-cjk-jp.css +3 -0
  744. package/src/style-cjk-kr.css +3 -0
  745. package/src/styles/components.css +160 -59
  746. package/src/styles/fonts/et-book/et-book-bold-line-figures.woff +0 -0
  747. package/src/styles/fonts/et-book/et-book-display-italic-old-style-figures.woff +0 -0
  748. package/src/styles/fonts/et-book/et-book-roman-line-figures.woff +0 -0
  749. package/src/styles/fonts/et-book.css +31 -0
  750. package/src/styles/fonts/latin.css +1 -0
  751. package/src/styles/fonts/noto-serif-jp/400/057a6a98bda7fe57105ddaa99ec82015.woff2 +0 -0
  752. package/src/styles/fonts/noto-serif-jp/400/0acd1fe2b2ea1ad1bfee7ae1fa139d27.woff2 +0 -0
  753. package/src/styles/fonts/noto-serif-jp/400/0c2ab6b295e55f356f8020d4e7747522.woff2 +0 -0
  754. package/src/styles/fonts/noto-serif-jp/400/0c5f9492af03a4fa42c784de94649de1.woff2 +0 -0
  755. package/src/styles/fonts/noto-serif-jp/400/0d9a936885a4c39077438effd3779cbd.woff2 +0 -0
  756. package/src/styles/fonts/noto-serif-jp/400/0e645da524f7cfc0e8c3c03fb2b08428.woff2 +0 -0
  757. package/src/styles/fonts/noto-serif-jp/400/135e83b403475c5dc9e49b43501a5b84.woff2 +0 -0
  758. package/src/styles/fonts/noto-serif-jp/400/13b2c53b30e6a3e4de7132dbc18dbdfc.woff2 +0 -0
  759. package/src/styles/fonts/noto-serif-jp/400/15d95680dc31cc6ce20e0a5464106dc4.woff2 +0 -0
  760. package/src/styles/fonts/noto-serif-jp/400/1733f27476507ca68b68a803bc533cc2.woff2 +0 -0
  761. package/src/styles/fonts/noto-serif-jp/400/188c2db794f3dd7a45889ddbc81da9bb.woff2 +0 -0
  762. package/src/styles/fonts/noto-serif-jp/400/1cf737900dd49c2e88f1b3221a82a602.woff2 +0 -0
  763. package/src/styles/fonts/noto-serif-jp/400/233d3a685ee18276b319363474599d47.woff2 +0 -0
  764. package/src/styles/fonts/noto-serif-jp/400/284b53bbefb06924cf236d24c8ed5641.woff2 +0 -0
  765. package/src/styles/fonts/noto-serif-jp/400/2a56eaf19d1d38a6b57e2a388f733676.woff2 +0 -0
  766. package/src/styles/fonts/noto-serif-jp/400/2abfbab82b6a7c04426afc054d2464da.woff2 +0 -0
  767. package/src/styles/fonts/noto-serif-jp/400/2e6f4bb71ef6b38765d51acc5a79f638.woff2 +0 -0
  768. package/src/styles/fonts/noto-serif-jp/400/31194d303a67561926a544ed0e072aee.woff2 +0 -0
  769. package/src/styles/fonts/noto-serif-jp/400/3623d96698132c61e861f984ce11cc23.woff2 +0 -0
  770. package/src/styles/fonts/noto-serif-jp/400/39211b02f24c69cbd1b54f6d41b01933.woff2 +0 -0
  771. package/src/styles/fonts/noto-serif-jp/400/3e24063b19abd2b051af986a6870046d.woff2 +0 -0
  772. package/src/styles/fonts/noto-serif-jp/400/4410f876d27c9821839474b6b1059fd7.woff2 +0 -0
  773. package/src/styles/fonts/noto-serif-jp/400/479329a39a3eb6c0047e6b0981919855.woff2 +0 -0
  774. package/src/styles/fonts/noto-serif-jp/400/527ff336279465617aafbafe1a830632.woff2 +0 -0
  775. package/src/styles/fonts/noto-serif-jp/400/56b40518ea0608e62826bd44ee27f5f9.woff2 +0 -0
  776. package/src/styles/fonts/noto-serif-jp/400/5acc7f736217259db79a42bf44241c48.woff2 +0 -0
  777. package/src/styles/fonts/noto-serif-jp/400/5c05f8b75c9d7f25b3c04ca711cf43b3.woff2 +0 -0
  778. package/src/styles/fonts/noto-serif-jp/400/5e0124c7265f1b4cf0fc797b94efbebc.woff2 +0 -0
  779. package/src/styles/fonts/noto-serif-jp/400/615a7e0d63f257109d599b2a1977de68.woff2 +0 -0
  780. package/src/styles/fonts/noto-serif-jp/400/61a2c80d0c924d5a6187c02e8d1d1642.woff2 +0 -0
  781. package/src/styles/fonts/noto-serif-jp/400/62fc94f8d4c5a750b7f25e7387539910.woff2 +0 -0
  782. package/src/styles/fonts/noto-serif-jp/400/639c7c6b0b0c27c738702741cfa4b8c0.woff2 +0 -0
  783. package/src/styles/fonts/noto-serif-jp/400/68a3a4bf337f8a0722be76676e20b850.woff2 +0 -0
  784. package/src/styles/fonts/noto-serif-jp/400/6abdd163d2c4b85698db8aa1ce149361.woff2 +0 -0
  785. package/src/styles/fonts/noto-serif-jp/400/73b794d885b88f7befb7ea8ab1780a15.woff2 +0 -0
  786. package/src/styles/fonts/noto-serif-jp/400/743f290baf027d4626a86e22f3d44600.woff2 +0 -0
  787. package/src/styles/fonts/noto-serif-jp/400/757019c01c5e2c2b0f54293cea3b5636.woff2 +0 -0
  788. package/src/styles/fonts/noto-serif-jp/400/76f117a3baacda304a71965a17266a13.woff2 +0 -0
  789. package/src/styles/fonts/noto-serif-jp/400/77747f17625a259fb405b7bbdd84ac4b.woff2 +0 -0
  790. package/src/styles/fonts/noto-serif-jp/400/788498548ddfb710d6ef3c7bff79fa97.woff2 +0 -0
  791. package/src/styles/fonts/noto-serif-jp/400/78ea6c40923ce95367e3517dd1e5a849.woff2 +0 -0
  792. package/src/styles/fonts/noto-serif-jp/400/7f9a6c9286b68de9c72b0024f7beeb40.woff2 +0 -0
  793. package/src/styles/fonts/noto-serif-jp/400/823d1bb11097331238d103c7f72138dc.woff2 +0 -0
  794. package/src/styles/fonts/noto-serif-jp/400/888ba53510213d5d1f6a7deb60e569b6.woff2 +0 -0
  795. package/src/styles/fonts/noto-serif-jp/400/892aa49b2529c89bb4076d4aa51fe30f.woff2 +0 -0
  796. package/src/styles/fonts/noto-serif-jp/400/95ff41191c76ef893ac70a1043e95e44.woff2 +0 -0
  797. package/src/styles/fonts/noto-serif-jp/400/96c75862c8cec51a7c05ff025fea86cd.woff2 +0 -0
  798. package/src/styles/fonts/noto-serif-jp/400/9b21e8244f5930c48ad5073f83777b6c.woff2 +0 -0
  799. package/src/styles/fonts/noto-serif-jp/400/9fce02d1a09f464c36c9fcfb14a354c5.woff2 +0 -0
  800. package/src/styles/fonts/noto-serif-jp/400/a0a6755c7e3f9e4cf03387027dd9f16c.woff2 +0 -0
  801. package/src/styles/fonts/noto-serif-jp/400/a4475c442c6b259354c9eda62e64e7df.woff2 +0 -0
  802. package/src/styles/fonts/noto-serif-jp/400/a4a39b5f048671aa37c2b2a9581ab08a.woff2 +0 -0
  803. package/src/styles/fonts/noto-serif-jp/400/a534449d67561ac9b06489f64e57ad98.woff2 +0 -0
  804. package/src/styles/fonts/noto-serif-jp/400/a919a8c6fec6d7e8bb21177965f2e9d7.woff2 +0 -0
  805. package/src/styles/fonts/noto-serif-jp/400/ad67113f88b59582991cb0c5d33ea19f.woff2 +0 -0
  806. package/src/styles/fonts/noto-serif-jp/400/adb6e613fb99c6dd660b727101f554a8.woff2 +0 -0
  807. package/src/styles/fonts/noto-serif-jp/400/adc4a98a89870ef984ee8f4b57c8a3a5.woff2 +0 -0
  808. package/src/styles/fonts/noto-serif-jp/400/b12379ab782468d725519cd07a7d15bc.woff2 +0 -0
  809. package/src/styles/fonts/noto-serif-jp/400/b55486f8a459c838fe329d4e79a8c211.woff2 +0 -0
  810. package/src/styles/fonts/noto-serif-jp/400/bb0b15492b8cdbbec57c0bcfa0aa9241.woff2 +0 -0
  811. package/src/styles/fonts/noto-serif-jp/400/bedc74b423b7293b6ad0bdecc61c42cc.woff2 +0 -0
  812. package/src/styles/fonts/noto-serif-jp/400/bfb00d4a4c48661bd0be99f300a0faae.woff2 +0 -0
  813. package/src/styles/fonts/noto-serif-jp/400/c1b0df29ae41d764904df84e9ac83d1e.woff2 +0 -0
  814. package/src/styles/fonts/noto-serif-jp/400/c42c67070cfe99cf823df92d81c7fa6e.woff2 +0 -0
  815. package/src/styles/fonts/noto-serif-jp/400/c4d749e45ecd5a5aed5a0bb3ebfd355d.woff2 +0 -0
  816. package/src/styles/fonts/noto-serif-jp/400/c96d83978add28b356c22c4c84916733.woff2 +0 -0
  817. package/src/styles/fonts/noto-serif-jp/400/cb7ccd6494256f7a2977b7c2b0225592.woff2 +0 -0
  818. package/src/styles/fonts/noto-serif-jp/400/ce41d70ce6a069a498525c9e15c45cf2.woff2 +0 -0
  819. package/src/styles/fonts/noto-serif-jp/400/d509ee5c8241bc7c3de2039d75564fa5.woff2 +0 -0
  820. package/src/styles/fonts/noto-serif-jp/400/d675d717cd329bfd4c0524f76ae1579c.woff2 +0 -0
  821. package/src/styles/fonts/noto-serif-jp/400/dc8f6256445e68199540be9ade33529f.woff2 +0 -0
  822. package/src/styles/fonts/noto-serif-jp/400/e8c15be62faf978d208925e79ea6a10d.woff2 +0 -0
  823. package/src/styles/fonts/noto-serif-jp/400/e91c4d941ed9f5c2f3e27f205a3a225e.woff2 +0 -0
  824. package/src/styles/fonts/noto-serif-jp/400/e9c566be7a5d38a9085225f7372bc82b.woff2 +0 -0
  825. package/src/styles/fonts/noto-serif-jp/400/e9ebc567a711eeb29019ddae3e0ce7fe.woff2 +0 -0
  826. package/src/styles/fonts/noto-serif-jp/400/ecb8875a56c7b038b35432fda41ae128.woff2 +0 -0
  827. package/src/styles/fonts/noto-serif-jp/400/f1661731474b78bdf81114daf10b254f.woff2 +0 -0
  828. package/src/styles/fonts/noto-serif-jp/400/f2c58dee206ba9355046fc23d05491f7.woff2 +0 -0
  829. package/src/styles/fonts/noto-serif-jp/400/f50ac27ea4358d67fdda403c2bb52467.woff2 +0 -0
  830. package/src/styles/fonts/noto-serif-jp/400/f55334112f8e8be82d65db29887a663f.woff2 +0 -0
  831. package/src/styles/fonts/noto-serif-jp/400/f740e93f3c0277ecc616594103bca683.woff2 +0 -0
  832. package/src/styles/fonts/noto-serif-jp/400/f7633b5af033d76ff2fb3c3c266d77c5.woff2 +0 -0
  833. package/src/styles/fonts/noto-serif-jp/400/f7d8468cba2335a83ee414ea68291bab.woff2 +0 -0
  834. package/src/styles/fonts/noto-serif-jp/400/fc9ae5b600fb711f2d67e93ce768cba4.woff2 +0 -0
  835. package/src/styles/fonts/noto-serif-jp/400/fd1bceb55d3e0183ac2454b8532fec7d.woff2 +0 -0
  836. package/src/styles/fonts/noto-serif-jp/700/02611a045a7fe83a12014e3debc9f731.woff2 +0 -0
  837. package/src/styles/fonts/noto-serif-jp/700/0f5e1a18987dbc84ca05188c129e1936.woff2 +0 -0
  838. package/src/styles/fonts/noto-serif-jp/700/112743a4ab5fdd1498dfdf2b11336380.woff2 +0 -0
  839. package/src/styles/fonts/noto-serif-jp/700/1211f03d3ff5759a702631445793f60e.woff2 +0 -0
  840. package/src/styles/fonts/noto-serif-jp/700/147b24c2f08d03bbed30887d4cb3311c.woff2 +0 -0
  841. package/src/styles/fonts/noto-serif-jp/700/16663e567f81f4725a1522f37e18f71f.woff2 +0 -0
  842. package/src/styles/fonts/noto-serif-jp/700/1824321da801e6257b902f3d1d09054c.woff2 +0 -0
  843. package/src/styles/fonts/noto-serif-jp/700/1a3a92c7c060a71a6c35819b9ebcdbb9.woff2 +0 -0
  844. package/src/styles/fonts/noto-serif-jp/700/1fba7ec0e412e911bf31841de5a8a4e4.woff2 +0 -0
  845. package/src/styles/fonts/noto-serif-jp/700/28a97af9ab9d38983d20f39ff09840e1.woff2 +0 -0
  846. package/src/styles/fonts/noto-serif-jp/700/2bfaadaf3479c72286248e6de0be0ec9.woff2 +0 -0
  847. package/src/styles/fonts/noto-serif-jp/700/2c8c55e4cec85ce77e95cac9d330f5cf.woff2 +0 -0
  848. package/src/styles/fonts/noto-serif-jp/700/2ed9981d2e8983365bd051159b976b6d.woff2 +0 -0
  849. package/src/styles/fonts/noto-serif-jp/700/2f4c633e923ba30c6ba376367379fc91.woff2 +0 -0
  850. package/src/styles/fonts/noto-serif-jp/700/349965eee0a9b6c984a319ab96a4ece9.woff2 +0 -0
  851. package/src/styles/fonts/noto-serif-jp/700/36cda3eae13370b934bba4429fab9078.woff2 +0 -0
  852. package/src/styles/fonts/noto-serif-jp/700/3aa13f1843d7c8fa16109b6395a9a29d.woff2 +0 -0
  853. package/src/styles/fonts/noto-serif-jp/700/3e2c06bdd9dcab29aeaeb0cfb7fa608a.woff2 +0 -0
  854. package/src/styles/fonts/noto-serif-jp/700/424123f9371ba357087363263eeafcda.woff2 +0 -0
  855. package/src/styles/fonts/noto-serif-jp/700/43b3bbf43055c3e65db9cdf2c5b18c1c.woff2 +0 -0
  856. package/src/styles/fonts/noto-serif-jp/700/44704471b60c18b3ecec5300a88f1102.woff2 +0 -0
  857. package/src/styles/fonts/noto-serif-jp/700/46604090efbb99c2db1800928af0a239.woff2 +0 -0
  858. package/src/styles/fonts/noto-serif-jp/700/46998df85d31e629c0142f0556f5e7c5.woff2 +0 -0
  859. package/src/styles/fonts/noto-serif-jp/700/49902f1c41b281f8eb3771abdd4dccb8.woff2 +0 -0
  860. package/src/styles/fonts/noto-serif-jp/700/4db4f31a16965baa42cb7dad667a8f04.woff2 +0 -0
  861. package/src/styles/fonts/noto-serif-jp/700/572b9e28f1e6d89b765a16d809db84a1.woff2 +0 -0
  862. package/src/styles/fonts/noto-serif-jp/700/5921cef36750ff6092d94b83a802823b.woff2 +0 -0
  863. package/src/styles/fonts/noto-serif-jp/700/59e95d8b5332dcdae894a19946ab767d.woff2 +0 -0
  864. package/src/styles/fonts/noto-serif-jp/700/5ec349ff62b9ee5fe0c6dae867704dc4.woff2 +0 -0
  865. package/src/styles/fonts/noto-serif-jp/700/601f812e8b1eafecd5ba585a6a6d7962.woff2 +0 -0
  866. package/src/styles/fonts/noto-serif-jp/700/6025ddaf99d86cc8bfd781006b19fbd6.woff2 +0 -0
  867. package/src/styles/fonts/noto-serif-jp/700/60f6e28b3f0400b0e8fe32c3c7116102.woff2 +0 -0
  868. package/src/styles/fonts/noto-serif-jp/700/64a404d675f1d726f0891b7a80794595.woff2 +0 -0
  869. package/src/styles/fonts/noto-serif-jp/700/7158022889c6c177062ac85036e7af10.woff2 +0 -0
  870. package/src/styles/fonts/noto-serif-jp/700/7e38ca789a9e76ac91d9256374e154f0.woff2 +0 -0
  871. package/src/styles/fonts/noto-serif-jp/700/815a0b797465190f60d8b1a04656c42e.woff2 +0 -0
  872. package/src/styles/fonts/noto-serif-jp/700/8726558cf297cbda831cc19514897205.woff2 +0 -0
  873. package/src/styles/fonts/noto-serif-jp/700/8855472f3c02f6c7ebb3216617ebe4af.woff2 +0 -0
  874. package/src/styles/fonts/noto-serif-jp/700/8e231d707f0c4f8e3cde90a6b52a79aa.woff2 +0 -0
  875. package/src/styles/fonts/noto-serif-jp/700/9064c12fd72c7aba235d8c3881755b91.woff2 +0 -0
  876. package/src/styles/fonts/noto-serif-jp/700/911b9e53e9814de2998c60bf3115f560.woff2 +0 -0
  877. package/src/styles/fonts/noto-serif-jp/700/92fdc376bce277874e75db666f175b44.woff2 +0 -0
  878. package/src/styles/fonts/noto-serif-jp/700/930a23430f0eb64480d7fe5f82834e21.woff2 +0 -0
  879. package/src/styles/fonts/noto-serif-jp/700/93bed0e5f2dd5a21cf73304fcfbc149d.woff2 +0 -0
  880. package/src/styles/fonts/noto-serif-jp/700/93f3ea6918533d96d9c252378b9a4af0.woff2 +0 -0
  881. package/src/styles/fonts/noto-serif-jp/700/95a0951d2a2722ff773a1a45e8c474d6.woff2 +0 -0
  882. package/src/styles/fonts/noto-serif-jp/700/982cd1765ca6bbb3e6547e47834d5148.woff2 +0 -0
  883. package/src/styles/fonts/noto-serif-jp/700/9b0c3caac458c7bc9c9133e40405ddea.woff2 +0 -0
  884. package/src/styles/fonts/noto-serif-jp/700/9f3bdab4025dc7c5ba1a5871e62e8918.woff2 +0 -0
  885. package/src/styles/fonts/noto-serif-jp/700/a00e94d4f04df6aa659db9c4954c7efe.woff2 +0 -0
  886. package/src/styles/fonts/noto-serif-jp/700/a2b0597837e382aca19919c4b74e58c8.woff2 +0 -0
  887. package/src/styles/fonts/noto-serif-jp/700/a373e85c96aa0dc303092429fb837e51.woff2 +0 -0
  888. package/src/styles/fonts/noto-serif-jp/700/a6e044424780a57610833cc856c15bf5.woff2 +0 -0
  889. package/src/styles/fonts/noto-serif-jp/700/a7c58070d68dc1724e9d4e781afb78db.woff2 +0 -0
  890. package/src/styles/fonts/noto-serif-jp/700/accc43762ad05edfbff66ba1380d192a.woff2 +0 -0
  891. package/src/styles/fonts/noto-serif-jp/700/ad47bcd6d4663026d648c132d969318d.woff2 +0 -0
  892. package/src/styles/fonts/noto-serif-jp/700/af57a2e8c72f9ba0efb1af771d32c124.woff2 +0 -0
  893. package/src/styles/fonts/noto-serif-jp/700/b0ae5f374e43dac992a4a5d334cebc0b.woff2 +0 -0
  894. package/src/styles/fonts/noto-serif-jp/700/b91234c10fd8b8c8abc88e03afe66a1f.woff2 +0 -0
  895. package/src/styles/fonts/noto-serif-jp/700/b9317198f118a1dfd8ddf2b82ec028f3.woff2 +0 -0
  896. package/src/styles/fonts/noto-serif-jp/700/ba825ae79b1a6f7e0cce5215fcb5c96f.woff2 +0 -0
  897. package/src/styles/fonts/noto-serif-jp/700/bc6183ac08f0fac78c46f80c10cf7c92.woff2 +0 -0
  898. package/src/styles/fonts/noto-serif-jp/700/bc8ccea5abf6598cf3cfa97eb59804bb.woff2 +0 -0
  899. package/src/styles/fonts/noto-serif-jp/700/bc95a792cbca6639214c9b0da13392ff.woff2 +0 -0
  900. package/src/styles/fonts/noto-serif-jp/700/bcfa62f35731856246c146d3a6932bf3.woff2 +0 -0
  901. package/src/styles/fonts/noto-serif-jp/700/c063897793f593eb26d6ff7b7baaba18.woff2 +0 -0
  902. package/src/styles/fonts/noto-serif-jp/700/c6c2971ad1f3221f6cf84028aa0f477e.woff2 +0 -0
  903. package/src/styles/fonts/noto-serif-jp/700/c97f41eef722121d86f55d553c056a39.woff2 +0 -0
  904. package/src/styles/fonts/noto-serif-jp/700/ca62704509932d3232d62918de97af3f.woff2 +0 -0
  905. package/src/styles/fonts/noto-serif-jp/700/d1f064825fa5784b5c930652bd831cce.woff2 +0 -0
  906. package/src/styles/fonts/noto-serif-jp/700/d2faabcedd19f016e7b21bce073c0ec8.woff2 +0 -0
  907. package/src/styles/fonts/noto-serif-jp/700/d320171e57480510f87dbbc7d5264b0a.woff2 +0 -0
  908. package/src/styles/fonts/noto-serif-jp/700/d3a72a99d365dddfbca8d017a8011368.woff2 +0 -0
  909. package/src/styles/fonts/noto-serif-jp/700/d42aafa0f246ad3b4c16fe96d3a3a432.woff2 +0 -0
  910. package/src/styles/fonts/noto-serif-jp/700/d4aaf23c13ae808a4bed617afd13aa07.woff2 +0 -0
  911. package/src/styles/fonts/noto-serif-jp/700/d6bb686bddfbe8f38a36a68e609a8667.woff2 +0 -0
  912. package/src/styles/fonts/noto-serif-jp/700/d9020ff69a83b2a6ee1f42ae480f7db0.woff2 +0 -0
  913. package/src/styles/fonts/noto-serif-jp/700/da90a9012ab2d98f759e3fa0820ef502.woff2 +0 -0
  914. package/src/styles/fonts/noto-serif-jp/700/df10d94bea357a43313e20da5d84bd30.woff2 +0 -0
  915. package/src/styles/fonts/noto-serif-jp/700/e34b2b141e472dc776c86fdf8eea23b0.woff2 +0 -0
  916. package/src/styles/fonts/noto-serif-jp/700/e444fd88f1390636e603d0d681538ac8.woff2 +0 -0
  917. package/src/styles/fonts/noto-serif-jp/700/e79898628283edc27180fc39d0d769c1.woff2 +0 -0
  918. package/src/styles/fonts/noto-serif-jp/700/eaf332445a40942928e5f5750c1c3116.woff2 +0 -0
  919. package/src/styles/fonts/noto-serif-jp/700/f91cd722855f4269256eae1187df64ec.woff2 +0 -0
  920. package/src/styles/fonts/noto-serif-jp/700/fc895f5ce66b656f4a933097bf2a8775.woff2 +0 -0
  921. package/src/styles/fonts/noto-serif-jp/700/fca6720fd14c467d29a90f18ef3859b9.woff2 +0 -0
  922. package/src/styles/fonts/noto-serif-jp/700/fd1bb507bcbf04856eeb7f7fd47ea579.woff2 +0 -0
  923. package/src/styles/fonts/noto-serif-jp/noto-serif-jp.css +3349 -0
  924. package/src/styles/fonts/noto-serif-kr/400/033466ef683afe931f7f520cfb42d928.woff2 +0 -0
  925. package/src/styles/fonts/noto-serif-kr/400/05da12edb9d52210581dc6ec4541031f.woff2 +0 -0
  926. package/src/styles/fonts/noto-serif-kr/400/0968e4861204b51f62a2f8e9f15dd5e0.woff2 +0 -0
  927. package/src/styles/fonts/noto-serif-kr/400/0d5dec931dc885f07fe5cd5af8bed675.woff2 +0 -0
  928. package/src/styles/fonts/noto-serif-kr/400/1076f0f6f66d28d7a2f16427faad4413.woff2 +0 -0
  929. package/src/styles/fonts/noto-serif-kr/400/12ef1ba76bd20b004b865266a1aa70b3.woff2 +0 -0
  930. package/src/styles/fonts/noto-serif-kr/400/152395634a207579552f8cb54db88599.woff2 +0 -0
  931. package/src/styles/fonts/noto-serif-kr/400/1a08931435f885e707edb85978ea3bb6.woff2 +0 -0
  932. package/src/styles/fonts/noto-serif-kr/400/31a197213ae61d7043c81013f52d10d6.woff2 +0 -0
  933. package/src/styles/fonts/noto-serif-kr/400/37dbd564820cce2f7b3331e442da0df3.woff2 +0 -0
  934. package/src/styles/fonts/noto-serif-kr/400/37dc8505b20b37a2477a9121b16d44a7.woff2 +0 -0
  935. package/src/styles/fonts/noto-serif-kr/400/3ac0a07878cacdc98bb889f40f5970d3.woff2 +0 -0
  936. package/src/styles/fonts/noto-serif-kr/400/491bbba374093fff045f55803a22bfef.woff2 +0 -0
  937. package/src/styles/fonts/noto-serif-kr/400/4d38e56738156625329d93bd5c8cc74a.woff2 +0 -0
  938. package/src/styles/fonts/noto-serif-kr/400/57e9a86651366c1ba299e47aa7e358c2.woff2 +0 -0
  939. package/src/styles/fonts/noto-serif-kr/400/57f89adeae55aa7fe2add3fc1801ce03.woff2 +0 -0
  940. package/src/styles/fonts/noto-serif-kr/400/5df9ce98c75f8c50fb4fff7f01a23925.woff2 +0 -0
  941. package/src/styles/fonts/noto-serif-kr/400/60020a830d809c26970cf8e48ba1eeda.woff2 +0 -0
  942. package/src/styles/fonts/noto-serif-kr/400/60bcfa3ea7446eb42394aab02d28be40.woff2 +0 -0
  943. package/src/styles/fonts/noto-serif-kr/400/63fafcb069520613d0ea35ad3e6b1e42.woff2 +0 -0
  944. package/src/styles/fonts/noto-serif-kr/400/65a60d87c64228d258a123cbe85f5f31.woff2 +0 -0
  945. package/src/styles/fonts/noto-serif-kr/400/6968e889e18891d912809fe484c2e745.woff2 +0 -0
  946. package/src/styles/fonts/noto-serif-kr/400/70714891ad3fbfc3d5f10a8669dacc5a.woff2 +0 -0
  947. package/src/styles/fonts/noto-serif-kr/400/761d84afca8d7f34eebefe538adba827.woff2 +0 -0
  948. package/src/styles/fonts/noto-serif-kr/400/76e07530323418ec723c5d7634c9bcca.woff2 +0 -0
  949. package/src/styles/fonts/noto-serif-kr/400/773819b7b9b8fd404a929867c0fd677e.woff2 +0 -0
  950. package/src/styles/fonts/noto-serif-kr/400/7817dd16805145d8538ad57590f69f5a.woff2 +0 -0
  951. package/src/styles/fonts/noto-serif-kr/400/7b59f0ec7792b18458dc5a361e37884c.woff2 +0 -0
  952. package/src/styles/fonts/noto-serif-kr/400/7c3945788a689a69356c1a622d69d48b.woff2 +0 -0
  953. package/src/styles/fonts/noto-serif-kr/400/835b6505bb9eea9678925a1fa885353d.woff2 +0 -0
  954. package/src/styles/fonts/noto-serif-kr/400/8b5c9ef81159f31d2ab35f45ca4373e0.woff2 +0 -0
  955. package/src/styles/fonts/noto-serif-kr/400/8b91c7c2ed390f1278b9befa3aa87233.woff2 +0 -0
  956. package/src/styles/fonts/noto-serif-kr/400/8fb45117a62d92dce44a80f0b729ead5.woff2 +0 -0
  957. package/src/styles/fonts/noto-serif-kr/400/90b7848e9b1623b77bdcf155e90b839c.woff2 +0 -0
  958. package/src/styles/fonts/noto-serif-kr/400/985d4d41afa0934a4eb2de35473fb9a2.woff2 +0 -0
  959. package/src/styles/fonts/noto-serif-kr/400/9bb38fd05201de666b88b82d56a386f7.woff2 +0 -0
  960. package/src/styles/fonts/noto-serif-kr/400/9eff2ab2a9d2ced47ab761bf6fdb11ec.woff2 +0 -0
  961. package/src/styles/fonts/noto-serif-kr/400/9f1963fe8d4d6878d717d872a8f3fdb5.woff2 +0 -0
  962. package/src/styles/fonts/noto-serif-kr/400/a0d00fe4816c95a8c7dffd445ef00b03.woff2 +0 -0
  963. package/src/styles/fonts/noto-serif-kr/400/a0f199077fa1a33bc2a1e01c64de7fe6.woff2 +0 -0
  964. package/src/styles/fonts/noto-serif-kr/400/a7c35e42a659347e490c3cb7983b2e7f.woff2 +0 -0
  965. package/src/styles/fonts/noto-serif-kr/400/aa662092cf249707123ca4d575e2764b.woff2 +0 -0
  966. package/src/styles/fonts/noto-serif-kr/400/aae1eda193285ab817a2d1b408440326.woff2 +0 -0
  967. package/src/styles/fonts/noto-serif-kr/400/b0cb664cb2e1371efda8943c0b7dcd1c.woff2 +0 -0
  968. package/src/styles/fonts/noto-serif-kr/400/b0d1cdced482352cf0d3ae58638aacb9.woff2 +0 -0
  969. package/src/styles/fonts/noto-serif-kr/400/b320f5d185b2cff933ac549c184031c5.woff2 +0 -0
  970. package/src/styles/fonts/noto-serif-kr/400/ba9c59d7dfa4494db1bb764ada81467d.woff2 +0 -0
  971. package/src/styles/fonts/noto-serif-kr/400/bab547459d514f46206e340c4bb2dc88.woff2 +0 -0
  972. package/src/styles/fonts/noto-serif-kr/400/bcc39eda837bb7a7a3d37c8c60fffb81.woff2 +0 -0
  973. package/src/styles/fonts/noto-serif-kr/400/bd73264d7f98776708d5d6f3c9b78fcc.woff2 +0 -0
  974. package/src/styles/fonts/noto-serif-kr/400/bed610b217d500f5975cfc9fe6157570.woff2 +0 -0
  975. package/src/styles/fonts/noto-serif-kr/400/bf8901f8f11d4f433ea17dabc9370ea6.woff2 +0 -0
  976. package/src/styles/fonts/noto-serif-kr/400/c4143bb9f2fe77b6ccf20088a8904650.woff2 +0 -0
  977. package/src/styles/fonts/noto-serif-kr/400/c7ff3f6bbdcd5f604b7343602ab904df.woff2 +0 -0
  978. package/src/styles/fonts/noto-serif-kr/400/c945c62368357d05a53206620460fb30.woff2 +0 -0
  979. package/src/styles/fonts/noto-serif-kr/400/ce022e18a1377ac509443c3c3790b431.woff2 +0 -0
  980. package/src/styles/fonts/noto-serif-kr/400/cf9cffe56636322f62b40d61130fbc5e.woff2 +0 -0
  981. package/src/styles/fonts/noto-serif-kr/400/d20d8944bc0b85f5b2aae4b24f343516.woff2 +0 -0
  982. package/src/styles/fonts/noto-serif-kr/400/d36644d6502527e1fff205d0c7eca434.woff2 +0 -0
  983. package/src/styles/fonts/noto-serif-kr/400/d40ab99c7a38026f411c8f112f742b48.woff2 +0 -0
  984. package/src/styles/fonts/noto-serif-kr/400/dc6e234ded795e91f76d6647f628fbf0.woff2 +0 -0
  985. package/src/styles/fonts/noto-serif-kr/400/dd0cfdfdac0866e66d587e2b5a9e9961.woff2 +0 -0
  986. package/src/styles/fonts/noto-serif-kr/400/de304c8a02e45ded4f8dcc479d167198.woff2 +0 -0
  987. package/src/styles/fonts/noto-serif-kr/400/e8c364c16daa04835bf32d293d2598db.woff2 +0 -0
  988. package/src/styles/fonts/noto-serif-kr/400/ec86e23683052da5cfdc3b77641bd15a.woff2 +0 -0
  989. package/src/styles/fonts/noto-serif-kr/400/eda1b0cb6d1719dd9bedcf3216a9e8de.woff2 +0 -0
  990. package/src/styles/fonts/noto-serif-kr/400/ee6fce20b420a480714607c66d7f97e5.woff2 +0 -0
  991. package/src/styles/fonts/noto-serif-kr/400/eee3836d6ac17ebb2c450bbcbc9db121.woff2 +0 -0
  992. package/src/styles/fonts/noto-serif-kr/400/f5738255e92d8dd34a46d1bcdf4c4074.woff2 +0 -0
  993. package/src/styles/fonts/noto-serif-kr/400/fa6e58ce4b52695e7ae19bbea6336ec8.woff2 +0 -0
  994. package/src/styles/fonts/noto-serif-kr/400/fd2069e6e8588c70b4f11364093b81f2.woff2 +0 -0
  995. package/src/styles/fonts/noto-serif-kr/700/04b8bde59cff68eeee74fb1914eb09c7.woff2 +0 -0
  996. package/src/styles/fonts/noto-serif-kr/700/05ac821472e235943ed1435e4bb8ecad.woff2 +0 -0
  997. package/src/styles/fonts/noto-serif-kr/700/074c1f8483d5a4d8c45c8c5f3e4cbb32.woff2 +0 -0
  998. package/src/styles/fonts/noto-serif-kr/700/1380210a722ac9aeef54005ad7b015c9.woff2 +0 -0
  999. package/src/styles/fonts/noto-serif-kr/700/17c7f5d0a45e92ede0e5dec3a2c77efa.woff2 +0 -0
  1000. package/src/styles/fonts/noto-serif-kr/700/1be602ad456b0d75902d352116cb35fd.woff2 +0 -0
  1001. package/src/styles/fonts/noto-serif-kr/700/2612c60f9dc5459ac42763591240a1d6.woff2 +0 -0
  1002. package/src/styles/fonts/noto-serif-kr/700/2ab8cf1f23a5ac7939a7876054e711a7.woff2 +0 -0
  1003. package/src/styles/fonts/noto-serif-kr/700/34e6750e00c3a911ef87dc66b336594d.woff2 +0 -0
  1004. package/src/styles/fonts/noto-serif-kr/700/36f3df4730cfca4fc77fe52b52e6a126.woff2 +0 -0
  1005. package/src/styles/fonts/noto-serif-kr/700/37887deb7a9c466cf2af6ee7ff372ea6.woff2 +0 -0
  1006. package/src/styles/fonts/noto-serif-kr/700/3aba89c4d4281553078de4622b498cbb.woff2 +0 -0
  1007. package/src/styles/fonts/noto-serif-kr/700/3d88558097559d775231194d43745ba7.woff2 +0 -0
  1008. package/src/styles/fonts/noto-serif-kr/700/3dc3e8c35524cad2d1236f3393104ccf.woff2 +0 -0
  1009. package/src/styles/fonts/noto-serif-kr/700/45140742dfa8686b58db2899ce89f89a.woff2 +0 -0
  1010. package/src/styles/fonts/noto-serif-kr/700/469d2754e315e77270c12fa0ab28026c.woff2 +0 -0
  1011. package/src/styles/fonts/noto-serif-kr/700/4c0760284569b87d8f2290c324953973.woff2 +0 -0
  1012. package/src/styles/fonts/noto-serif-kr/700/4cba92c942694bdc479bc54101bb1aa4.woff2 +0 -0
  1013. package/src/styles/fonts/noto-serif-kr/700/4e7d263c30bb48604069a3b4404d4eeb.woff2 +0 -0
  1014. package/src/styles/fonts/noto-serif-kr/700/5279c7e465d9871edf51fd02e691eee7.woff2 +0 -0
  1015. package/src/styles/fonts/noto-serif-kr/700/54bfdb6f21f9d6191e5100580239e14f.woff2 +0 -0
  1016. package/src/styles/fonts/noto-serif-kr/700/56be5e32fd01668fe5ba814d94905839.woff2 +0 -0
  1017. package/src/styles/fonts/noto-serif-kr/700/615b38e0b5bb3ea68426f44f47706e6f.woff2 +0 -0
  1018. package/src/styles/fonts/noto-serif-kr/700/65e9c4585d71bf48a5c62f367010da16.woff2 +0 -0
  1019. package/src/styles/fonts/noto-serif-kr/700/6b75213eb0be40ce84241eb2bb438a2e.woff2 +0 -0
  1020. package/src/styles/fonts/noto-serif-kr/700/7520ca6ca8c60eb1e62d50e71c8d30f1.woff2 +0 -0
  1021. package/src/styles/fonts/noto-serif-kr/700/759a647b77791b1e98f99bc0ab5317a7.woff2 +0 -0
  1022. package/src/styles/fonts/noto-serif-kr/700/76e51630143b95b6322ef93ad330da7a.woff2 +0 -0
  1023. package/src/styles/fonts/noto-serif-kr/700/79aa7c8c842c4a27cf57c0a3a1ffca5a.woff2 +0 -0
  1024. package/src/styles/fonts/noto-serif-kr/700/7cc72fd2c9105560422b6a67c6162945.woff2 +0 -0
  1025. package/src/styles/fonts/noto-serif-kr/700/7d0ea0690e432462f4d05a23d720ec19.woff2 +0 -0
  1026. package/src/styles/fonts/noto-serif-kr/700/7dffa5c1bec57e0903fd62357401ff1a.woff2 +0 -0
  1027. package/src/styles/fonts/noto-serif-kr/700/82f6ccc063960eed1cdfd1d61ada8862.woff2 +0 -0
  1028. package/src/styles/fonts/noto-serif-kr/700/845b4b67564d62cf5cad242f7ac08ea5.woff2 +0 -0
  1029. package/src/styles/fonts/noto-serif-kr/700/84c8e7bc0931008ed91f44ed12dfea94.woff2 +0 -0
  1030. package/src/styles/fonts/noto-serif-kr/700/887a11290ba78b1e66c6d2f67043005e.woff2 +0 -0
  1031. package/src/styles/fonts/noto-serif-kr/700/88db1d042074fb6e66821ffc10941930.woff2 +0 -0
  1032. package/src/styles/fonts/noto-serif-kr/700/8dc18c0cebe6aa4bf4c45dbb831048ab.woff2 +0 -0
  1033. package/src/styles/fonts/noto-serif-kr/700/919a879bd2d580d8491a31a449390689.woff2 +0 -0
  1034. package/src/styles/fonts/noto-serif-kr/700/91da6cb174bebfb96976e2f1316be2fe.woff2 +0 -0
  1035. package/src/styles/fonts/noto-serif-kr/700/934dfdbed2bb2c4b6129199d699a34fa.woff2 +0 -0
  1036. package/src/styles/fonts/noto-serif-kr/700/97d7a17234f2b5030ae9697ae00aded2.woff2 +0 -0
  1037. package/src/styles/fonts/noto-serif-kr/700/995c4fda62d25c3b79dd5987737df6ae.woff2 +0 -0
  1038. package/src/styles/fonts/noto-serif-kr/700/9d0afa53dc2f92ba42024f55f11f6779.woff2 +0 -0
  1039. package/src/styles/fonts/noto-serif-kr/700/9e13a47242926f1cd7d68c08d9ef8889.woff2 +0 -0
  1040. package/src/styles/fonts/noto-serif-kr/700/a379ada89abd750c587ded29f77e731c.woff2 +0 -0
  1041. package/src/styles/fonts/noto-serif-kr/700/a48be56ce5d3a99dc8f8331398004412.woff2 +0 -0
  1042. package/src/styles/fonts/noto-serif-kr/700/a806759e72987951d6d08b7cbdf36a1b.woff2 +0 -0
  1043. package/src/styles/fonts/noto-serif-kr/700/a88481ec8bc7be11cb66e46781a79bb9.woff2 +0 -0
  1044. package/src/styles/fonts/noto-serif-kr/700/a9a016d4a93409f65278327e8a8bb38d.woff2 +0 -0
  1045. package/src/styles/fonts/noto-serif-kr/700/ab92d41062a7644faa45f50bf384454a.woff2 +0 -0
  1046. package/src/styles/fonts/noto-serif-kr/700/b326a8a9c33b554db570da94f60bc380.woff2 +0 -0
  1047. package/src/styles/fonts/noto-serif-kr/700/b6117ff2993b11bb1fdc7ea3588a010c.woff2 +0 -0
  1048. package/src/styles/fonts/noto-serif-kr/700/b61982951bd51b724143c30dfaaa9fe9.woff2 +0 -0
  1049. package/src/styles/fonts/noto-serif-kr/700/c0098958e20db68cab90097b5e62516f.woff2 +0 -0
  1050. package/src/styles/fonts/noto-serif-kr/700/c4bfaa5e50798246e3770718b7a7c84a.woff2 +0 -0
  1051. package/src/styles/fonts/noto-serif-kr/700/c84598999133455503042e06f4ab79cb.woff2 +0 -0
  1052. package/src/styles/fonts/noto-serif-kr/700/ca9a533988d7019597a60d4e17127e0c.woff2 +0 -0
  1053. package/src/styles/fonts/noto-serif-kr/700/cf2b28f90f47276f7e2688a65e88a101.woff2 +0 -0
  1054. package/src/styles/fonts/noto-serif-kr/700/d689b1861d7e4377dd72ad3013482612.woff2 +0 -0
  1055. package/src/styles/fonts/noto-serif-kr/700/d9047070d72a816b3dba9d40c2d85e69.woff2 +0 -0
  1056. package/src/styles/fonts/noto-serif-kr/700/da04549f3f4ed28076b01b8cd710d313.woff2 +0 -0
  1057. package/src/styles/fonts/noto-serif-kr/700/da7af303f8c645f9a9dbae0e6e32dd35.woff2 +0 -0
  1058. package/src/styles/fonts/noto-serif-kr/700/df9568257eb29b156449fdd4bec5ec76.woff2 +0 -0
  1059. package/src/styles/fonts/noto-serif-kr/700/e067cd0ed76c90cd0a93c9339253f20b.woff2 +0 -0
  1060. package/src/styles/fonts/noto-serif-kr/700/e08b07772e7bed3cec2832d43f7fd339.woff2 +0 -0
  1061. package/src/styles/fonts/noto-serif-kr/700/e12150d5a39b30be8f567968c7a527b0.woff2 +0 -0
  1062. package/src/styles/fonts/noto-serif-kr/700/e53fcb2381eee345db4f6f973dd95a3e.woff2 +0 -0
  1063. package/src/styles/fonts/noto-serif-kr/700/e5bd313ef81f687d398aacb11cec3069.woff2 +0 -0
  1064. package/src/styles/fonts/noto-serif-kr/700/eb5afb3d952b8593782caec6026514b6.woff2 +0 -0
  1065. package/src/styles/fonts/noto-serif-kr/700/edd6a4f608d04fc0351d7688cfc321e4.woff2 +0 -0
  1066. package/src/styles/fonts/noto-serif-kr/700/f9695c6c4df2bf6bc03045ff79d4f01f.woff2 +0 -0
  1067. package/src/styles/fonts/noto-serif-kr/700/fada6eaa68ff8816afe43d2a36c5423e.woff2 +0 -0
  1068. package/src/styles/fonts/noto-serif-kr/700/fbcc4bf5367218951172bdee6f77d7a6.woff2 +0 -0
  1069. package/src/styles/fonts/noto-serif-kr/noto-serif-kr.css +2601 -0
  1070. package/src/styles/site-media.css +823 -0
  1071. package/src/styles/tokens.css +292 -21
  1072. package/src/styles/ui.css +4217 -1869
  1073. package/src/types/bindings.ts +3 -1
  1074. package/src/types/config.ts +139 -9
  1075. package/src/types/constants.ts +46 -11
  1076. package/src/types/entities.ts +24 -8
  1077. package/src/types/operations.ts +80 -1
  1078. package/src/types/props.ts +12 -3
  1079. package/src/types/raw-assets.d.ts +39 -0
  1080. package/src/types/views.ts +28 -7
  1081. package/src/ui/__tests__/color-themes.test.ts +7 -40
  1082. package/src/ui/__tests__/font-themes.test.ts +30 -11
  1083. package/src/ui/color-themes.ts +311 -270
  1084. package/src/ui/compose/ComposeDialog.tsx +712 -394
  1085. package/src/ui/compose/ComposePrompt.tsx +15 -10
  1086. package/src/ui/dash/ActionButtons.tsx +27 -18
  1087. package/src/ui/dash/DangerZone.tsx +15 -10
  1088. package/src/ui/dash/FormatBadge.tsx +21 -8
  1089. package/src/ui/dash/StatusBadge.tsx +33 -22
  1090. package/src/ui/dash/appearance/AdvancedContent.tsx +44 -30
  1091. package/src/ui/dash/appearance/CodeInjectionContent.tsx +164 -0
  1092. package/src/ui/dash/appearance/ColorThemeContent.tsx +127 -160
  1093. package/src/ui/dash/appearance/FontThemeContent.tsx +20 -15
  1094. package/src/ui/dash/appearance/NavigationContent.tsx +368 -225
  1095. package/src/ui/dash/appearance/__tests__/NavigationContent.test.tsx +54 -0
  1096. package/src/ui/dash/settings/AccountContent.tsx +34 -23
  1097. package/src/ui/dash/settings/AccountMenuContent.tsx +226 -137
  1098. package/src/ui/dash/settings/ApiTokensContent.tsx +122 -79
  1099. package/src/ui/dash/settings/AvatarContent.tsx +73 -45
  1100. package/src/ui/dash/settings/DeleteAccountContent.tsx +106 -66
  1101. package/src/ui/dash/settings/GeneralContent.tsx +274 -157
  1102. package/src/ui/dash/settings/GitHubSyncContent.tsx +558 -0
  1103. package/src/ui/dash/settings/SessionsContent.tsx +79 -38
  1104. package/src/ui/dash/settings/SettingsRootContent.tsx +209 -109
  1105. package/src/ui/dash/settings/__tests__/AccountMenuContent.test.tsx +67 -0
  1106. package/src/ui/dash/settings/__tests__/GeneralContent.test.tsx +75 -0
  1107. package/src/ui/feed/CuratedThreadPreview.tsx +24 -14
  1108. package/src/ui/feed/LinkCard.tsx +116 -84
  1109. package/src/ui/feed/LinkPreview.tsx +75 -0
  1110. package/src/ui/feed/NoteCard.tsx +60 -16
  1111. package/src/ui/feed/PostStatusBadges.tsx +15 -0
  1112. package/src/ui/feed/QuoteCard.tsx +10 -3
  1113. package/src/ui/feed/ThreadPreview.tsx +62 -36
  1114. package/src/ui/feed/TimelineFeed.tsx +2 -1
  1115. package/src/ui/feed/__tests__/thread-preview.test.ts +220 -11
  1116. package/src/ui/feed/__tests__/timeline-cards.test.ts +186 -5
  1117. package/src/ui/feed/thread-preview-state.ts +23 -10
  1118. package/src/ui/font-themes.ts +57 -97
  1119. package/src/ui/layouts/BaseLayout.tsx +221 -136
  1120. package/src/ui/layouts/SiteLayout.tsx +431 -143
  1121. package/src/ui/layouts/__tests__/BaseLayout.test.tsx +76 -6
  1122. package/src/ui/layouts/__tests__/SiteLayout.test.tsx +76 -0
  1123. package/src/ui/pages/ArchivePage.tsx +320 -207
  1124. package/src/ui/pages/BrandPage.tsx +499 -341
  1125. package/src/ui/pages/CollectionEditorPage.tsx +50 -30
  1126. package/src/ui/pages/CollectionPage.tsx +201 -61
  1127. package/src/ui/pages/CollectionsPage.tsx +27 -18
  1128. package/src/ui/pages/ComposePage.tsx +15 -10
  1129. package/src/ui/pages/FeaturedPage.tsx +16 -11
  1130. package/src/ui/pages/HomePage.tsx +15 -10
  1131. package/src/ui/pages/PostPage.tsx +5 -1
  1132. package/src/ui/pages/SearchPage.tsx +93 -43
  1133. package/src/ui/pages/ThemeSamplePage.tsx +758 -520
  1134. package/src/ui/pages/__tests__/ArchivePage.test.tsx +72 -0
  1135. package/src/ui/shared/CollectionDirectory.tsx +232 -37
  1136. package/src/ui/shared/CollectionsManager.tsx +155 -75
  1137. package/src/ui/shared/DecorativeQuoteMark.tsx +12 -9
  1138. package/src/ui/shared/MediaGallery.tsx +350 -256
  1139. package/src/ui/shared/Pagination.tsx +36 -25
  1140. package/src/ui/shared/PostFooter.tsx +139 -96
  1141. package/src/ui/shared/__tests__/media-gallery.test.ts +27 -0
  1142. package/src/ui/shared/__tests__/navigation-labels.test.ts +79 -28
  1143. package/src/ui/shared/__tests__/post-footer.test.ts +42 -17
  1144. package/src/ui/shared/collection-management-labels.ts +109 -33
  1145. package/src/ui/shared/navigation-labels.ts +78 -8
  1146. package/bin/lib/site-localize-media.js +0 -427
  1147. package/dist/client/_assets/client-auth.js +0 -3251
  1148. package/dist/client/_assets/client.css +0 -2
  1149. package/dist/client/_assets/client.js +0 -380
  1150. package/src/__tests__/site-localize-media.test.ts +0 -150
  1151. package/src/client/components/jant-collection-sidebar.ts +0 -803
  1152. package/src/i18n/locales/en.ts +0 -1
  1153. package/src/i18n/locales/zh-Hans.po +0 -3183
  1154. package/src/i18n/locales/zh-Hans.ts +0 -1
  1155. package/src/i18n/locales/zh-Hant.po +0 -3183
  1156. package/src/i18n/locales/zh-Hant.ts +0 -1
  1157. package/src/routes/feed/rss.ts +0 -216
  1158. package/src/types/lingui-react-macro.d.ts +0 -34
  1159. /package/dist/client/_assets/chunks/{heic-to-XcUDQvtx.js → heic-to-DUUaO23q.js} +0 -0
  1160. /package/dist/client/_assets/chunks/{module-ChVQstFd.js → module-DcsAZQZ_.js} +0 -0
  1161. /package/dist/client/_assets/chunks/{native-CR5HLOyf.js → native-DpcrFAPh.js} +0 -0
  1162. /package/dist/client/_assets/{client-cjk.css → client-cjk-B7Z0snDu.css} +0 -0
  1163. /package/dist/client/_assets/{client-cjk-tc.css → client-cjk-tc-BesJYrb2.css} +0 -0
@@ -7,6 +7,7 @@
7
7
  */
8
8
 
9
9
  import { Hono, type Context } from "hono";
10
+ import { getCookie, setCookie } from "hono/cookie";
10
11
  import { msg } from "@lingui/core/macro";
11
12
  import { z } from "zod";
12
13
  import type { Bindings } from "../../types.js";
@@ -36,8 +37,13 @@ import { NavigationContent } from "../../ui/dash/appearance/NavigationContent.js
36
37
  import { ColorThemeContent } from "../../ui/dash/appearance/ColorThemeContent.js";
37
38
  import { FontThemeContent } from "../../ui/dash/appearance/FontThemeContent.js";
38
39
  import { AdvancedContent } from "../../ui/dash/appearance/AdvancedContent.js";
40
+ import { CodeInjectionContent } from "../../ui/dash/appearance/CodeInjectionContent.js";
39
41
  import { ApiTokensContent } from "../../ui/dash/settings/ApiTokensContent.js";
40
42
  import { DeleteAccountContent } from "../../ui/dash/settings/DeleteAccountContent.js";
43
+ import {
44
+ GitHubSyncContent,
45
+ type GitHubSyncStatus,
46
+ } from "../../ui/dash/settings/GitHubSyncContent.js";
41
47
  import { toAbsoluteSiteUrl, toPublicPath } from "../../lib/url.js";
42
48
  import { parseValidated, UpdateSiteSettingsSchema } from "../../lib/schemas.js";
43
49
  import {
@@ -47,6 +53,32 @@ import {
47
53
  getHostedControlPlaneSiteDeleteUrl,
48
54
  } from "../../lib/hosted-signin.js";
49
55
  import { syncHostedControlPlaneSiteAvatar } from "../../lib/hosted-control-plane-sync.js";
56
+ import {
57
+ getGitHubAppConfig,
58
+ getHostedControlPlaneSsoSecret,
59
+ } from "../../lib/env.js";
60
+ import {
61
+ buildInstallUrl,
62
+ getInstallation,
63
+ listInstallationReposPage,
64
+ searchInstallationRepos,
65
+ } from "../../lib/github-app.js";
66
+ import {
67
+ isSyncPending,
68
+ markSyncPending,
69
+ runBackgroundSync,
70
+ } from "../../lib/github-sync-trigger.js";
71
+ import { buildSyncSiteConfig } from "../../lib/github-sync-site-config.js";
72
+ import {
73
+ readGitHubSyncStatus,
74
+ renderStatusCardHtml,
75
+ getSyncStatusStreamUrl,
76
+ } from "../../lib/github-sync-status.js";
77
+ import {
78
+ generateInstallNonce,
79
+ signInstallState,
80
+ verifyInstallState,
81
+ } from "../../lib/github-app-state.js";
50
82
 
51
83
  type Env = { Bindings: Bindings; Variables: AppVariables };
52
84
 
@@ -54,6 +86,7 @@ export const settingsRoutes = new Hono<Env>();
54
86
 
55
87
  const UpdateLocaleSettingsSchema = z.object({
56
88
  siteLanguage: z.string(),
89
+ cjkSerifFont: z.string(),
57
90
  timeZone: z.string(),
58
91
  });
59
92
 
@@ -73,6 +106,100 @@ function publicPath(c: Context<Env>, path: string): string {
73
106
  return toPublicPath(path, c.var.appConfig.sitePathPrefix);
74
107
  }
75
108
 
109
+ /**
110
+ * Breadcrumb labels for admin settings pages.
111
+ *
112
+ * These duplicate labels defined in the corresponding UI components (e.g.
113
+ * `SettingsRootContent`, `AccountMenuContent`) on purpose: Lingui hashes are
114
+ * computed from `message` alone, so identical text produces the same catalog
115
+ * entry and shares the translation. Keep the text in sync with the UI source
116
+ * of truth.
117
+ */
118
+ function breadcrumbLabel(
119
+ c: Context<Env>,
120
+ key:
121
+ | "settings"
122
+ | "general"
123
+ | "avatar"
124
+ | "navigation"
125
+ | "colorTheme"
126
+ | "fontTheme"
127
+ | "customCss"
128
+ | "codeInjection"
129
+ | "account"
130
+ | "sessions"
131
+ | "password"
132
+ | "deleteAccount"
133
+ | "apiTokens"
134
+ | "githubSync",
135
+ ): string {
136
+ const i18n = getI18n(c);
137
+ switch (key) {
138
+ case "settings":
139
+ return i18n._(
140
+ msg({ message: "Settings", comment: "@context: Breadcrumb label" }),
141
+ );
142
+ case "general":
143
+ return i18n._(
144
+ msg({ message: "General", comment: "@context: Breadcrumb label" }),
145
+ );
146
+ case "avatar":
147
+ return i18n._(
148
+ msg({ message: "Avatar", comment: "@context: Breadcrumb label" }),
149
+ );
150
+ case "navigation":
151
+ return i18n._(
152
+ msg({ message: "Navigation", comment: "@context: Breadcrumb label" }),
153
+ );
154
+ case "colorTheme":
155
+ return i18n._(
156
+ msg({ message: "Color Theme", comment: "@context: Breadcrumb label" }),
157
+ );
158
+ case "fontTheme":
159
+ return i18n._(
160
+ msg({ message: "Font Theme", comment: "@context: Breadcrumb label" }),
161
+ );
162
+ case "customCss":
163
+ return i18n._(
164
+ msg({ message: "Custom CSS", comment: "@context: Breadcrumb label" }),
165
+ );
166
+ case "codeInjection":
167
+ return i18n._(
168
+ msg({
169
+ message: "Code Injection",
170
+ comment: "@context: Breadcrumb label",
171
+ }),
172
+ );
173
+ case "account":
174
+ return i18n._(
175
+ msg({ message: "Account", comment: "@context: Breadcrumb label" }),
176
+ );
177
+ case "sessions":
178
+ return i18n._(
179
+ msg({ message: "Sessions", comment: "@context: Breadcrumb label" }),
180
+ );
181
+ case "password":
182
+ return i18n._(
183
+ msg({ message: "Password", comment: "@context: Breadcrumb label" }),
184
+ );
185
+ case "deleteAccount":
186
+ return i18n._(
187
+ msg({
188
+ message: "Delete Account",
189
+ comment: "@context: Breadcrumb label",
190
+ }),
191
+ );
192
+ case "apiTokens":
193
+ return i18n._(
194
+ msg({ message: "API Tokens", comment: "@context: Breadcrumb label" }),
195
+ );
196
+ case "githubSync":
197
+ return i18n._(
198
+ msg({ message: "GitHub Sync", comment: "@context: Breadcrumb label" }),
199
+ );
200
+ }
201
+ }
202
+
76
203
  type DemoRestriction = "sessions" | "password" | "accountDeletion";
77
204
 
78
205
  function getDemoRestrictionMessage(
@@ -163,14 +290,15 @@ settingsRoutes.get("/general", async (c) => {
163
290
  content: (
164
291
  <>
165
292
  <AdminBreadcrumb
166
- parent="Settings"
293
+ parent={breadcrumbLabel(c, "settings")}
167
294
  parentHref={publicPath(c, "/settings")}
168
- current="General"
295
+ current={breadcrumbLabel(c, "general")}
169
296
  />
170
297
  <GeneralContent
171
298
  siteName={dbSiteName || ""}
172
299
  siteDescription={dbSiteDescription || ""}
173
300
  siteLanguage={appConfig.siteLanguage}
301
+ cjkSerifFont={appConfig.cjkSerifFont}
174
302
  siteNameFallback={appConfig.fallbacks.siteName}
175
303
  siteDescriptionFallback={appConfig.fallbacks.siteDescription}
176
304
  mainRssFeed={appConfig.mainRssFeed}
@@ -277,7 +405,8 @@ settingsRoutes.post("/general/language-time", async (c) => {
277
405
  );
278
406
  const { languageChanged } =
279
407
  await c.var.services.settings.updateLocaleSettings(body, {
280
- oldLanguage: c.var.allSettings["SITE_LANGUAGE"] ?? "en",
408
+ oldLanguage: c.var.appConfig.siteLanguage,
409
+ oldCjkSerifFont: c.var.appConfig.cjkSerifFont,
281
410
  });
282
411
 
283
412
  const wantsJson = c.req.header("accept")?.includes("application/json");
@@ -386,9 +515,9 @@ settingsRoutes.get("/avatar", async (c) => {
386
515
  content: (
387
516
  <>
388
517
  <AdminBreadcrumb
389
- parent="Settings"
518
+ parent={breadcrumbLabel(c, "settings")}
390
519
  parentHref={publicPath(c, "/settings")}
391
- current="Avatar"
520
+ current={breadcrumbLabel(c, "avatar")}
392
521
  />
393
522
  <AvatarContent
394
523
  siteAvatarUrl={c.var.appConfig.siteAvatarUrl}
@@ -576,9 +705,10 @@ settingsRoutes.post("/avatar/display", async (c) => {
576
705
  // ===========================================================================
577
706
 
578
707
  settingsRoutes.get("/navigation", async (c) => {
579
- const navItems = await c.var.services.navItems.list();
580
- const headerNavMaxVisible = c.var.appConfig.headerNavMaxVisible;
581
- const homeDefaultView = c.var.appConfig.homeDefaultView;
708
+ const [navItems, directoryData] = await Promise.all([
709
+ c.var.services.navItems.list(),
710
+ c.var.services.collections.listDirectoryData(),
711
+ ]);
582
712
  const navData = await getNavigationData(c);
583
713
 
584
714
  return renderPublicPage(c, {
@@ -587,14 +717,13 @@ settingsRoutes.get("/navigation", async (c) => {
587
717
  content: (
588
718
  <>
589
719
  <AdminBreadcrumb
590
- parent="Settings"
720
+ parent={breadcrumbLabel(c, "settings")}
591
721
  parentHref={publicPath(c, "/settings")}
592
- current="Navigation"
722
+ current={breadcrumbLabel(c, "navigation")}
593
723
  />
594
724
  <NavigationContent
595
725
  navItems={navItems}
596
- headerNavMaxVisible={headerNavMaxVisible}
597
- homeDefaultView={homeDefaultView}
726
+ directoryData={directoryData}
598
727
  mainRssFeed={c.var.appConfig.mainRssFeed}
599
728
  siteName={navData.siteName}
600
729
  sitePathPrefix={c.var.appConfig.sitePathPrefix}
@@ -604,41 +733,12 @@ settingsRoutes.get("/navigation", async (c) => {
604
733
  });
605
734
  });
606
735
 
607
- settingsRoutes.post("/navigation/nav-max-visible", async (c) => {
608
- const body = await c.req.json<{ value: number }>();
609
- const { settings } = c.var.services;
610
-
611
- const navMax = Math.max(0, Math.min(5, body.value ?? 3));
612
- if (navMax !== 3) {
613
- await settings.set("HEADER_NAV_MAX_VISIBLE", String(navMax));
614
- } else {
615
- await settings.remove("HEADER_NAV_MAX_VISIBLE");
616
- }
617
-
618
- return c.json({ ok: true });
619
- });
620
-
621
- settingsRoutes.post("/navigation/home-default-view", async (c) => {
622
- const body = await c.req.json<{ value: string }>();
623
- const { settings } = c.var.services;
624
-
625
- if (body.value === "featured") {
626
- await settings.set("HOME_DEFAULT_VIEW", "featured");
627
- } else {
628
- await settings.remove("HOME_DEFAULT_VIEW");
629
- }
630
-
631
- return c.json({ ok: true });
632
- });
633
-
634
736
  // ===========================================================================
635
737
  // Color Theme (moved from appearance routes)
636
738
  // ===========================================================================
637
739
 
638
740
  settingsRoutes.get("/color-theme", async (c) => {
639
- const defaultThemeId = c.var.appConfig.fallbacks.defaultTheme;
640
- const currentThemeId =
641
- c.var.allSettings[SETTINGS_KEYS.THEME] ?? defaultThemeId;
741
+ const currentThemeId = c.var.appConfig.themeId;
642
742
  const currentThemeMode = c.var.appConfig.themeMode;
643
743
  const themes = getAvailableThemes();
644
744
  const saved = c.req.query("saved") !== undefined;
@@ -651,9 +751,9 @@ settingsRoutes.get("/color-theme", async (c) => {
651
751
  content: (
652
752
  <>
653
753
  <AdminBreadcrumb
654
- parent="Settings"
754
+ parent={breadcrumbLabel(c, "settings")}
655
755
  parentHref={publicPath(c, "/settings")}
656
- current="Color Theme"
756
+ current={breadcrumbLabel(c, "colorTheme")}
657
757
  />
658
758
  <ColorThemeContent
659
759
  themes={themes}
@@ -685,12 +785,7 @@ settingsRoutes.post("/color-theme", async (c) => {
685
785
  );
686
786
  }
687
787
 
688
- 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
- }
788
+ await settings.set(SETTINGS_KEYS.THEME, validTheme.id);
694
789
 
695
790
  const themeMode: ThemeMode = THEME_MODES.includes(body.themeMode as ThemeMode)
696
791
  ? (body.themeMode as ThemeMode)
@@ -710,7 +805,7 @@ settingsRoutes.post("/color-theme", async (c) => {
710
805
  // ===========================================================================
711
806
 
712
807
  settingsRoutes.get("/font-theme", async (c) => {
713
- const currentFontThemeId = c.var.allSettings["FONT_THEME"] ?? "default";
808
+ const currentFontThemeId = c.var.appConfig.fontThemeId;
714
809
  const saved = c.req.query("saved") !== undefined;
715
810
  const navData = await getNavigationData(c);
716
811
 
@@ -721,9 +816,9 @@ settingsRoutes.get("/font-theme", async (c) => {
721
816
  content: (
722
817
  <>
723
818
  <AdminBreadcrumb
724
- parent="Settings"
819
+ parent={breadcrumbLabel(c, "settings")}
725
820
  parentHref={publicPath(c, "/settings")}
726
- current="Font Theme"
821
+ current={breadcrumbLabel(c, "fontTheme")}
727
822
  />
728
823
  <FontThemeContent
729
824
  fontThemes={BUILTIN_FONT_THEMES}
@@ -754,11 +849,7 @@ settingsRoutes.post("/font-theme", async (c) => {
754
849
  );
755
850
  }
756
851
 
757
- if (validFont.id === "default") {
758
- await settings.remove("FONT_THEME");
759
- } else {
760
- await settings.set("FONT_THEME", validFont.id);
761
- }
852
+ await settings.set("FONT_THEME", validFont.id);
762
853
 
763
854
  return dsRedirect(publicPath(c, "/settings/font-theme?saved"));
764
855
  });
@@ -777,9 +868,9 @@ settingsRoutes.get("/custom-css", async (c) => {
777
868
  content: (
778
869
  <>
779
870
  <AdminBreadcrumb
780
- parent="Settings"
871
+ parent={breadcrumbLabel(c, "settings")}
781
872
  parentHref={publicPath(c, "/settings")}
782
- current="Custom CSS"
873
+ current={breadcrumbLabel(c, "customCss")}
783
874
  />
784
875
  <AdvancedContent
785
876
  customCSS={customCSS}
@@ -813,6 +904,70 @@ settingsRoutes.post("/custom-css", async (c) => {
813
904
  );
814
905
  });
815
906
 
907
+ // ===========================================================================
908
+ // Code Injection — site-wide HTML in <head> and at end of <body>.
909
+ // ===========================================================================
910
+
911
+ settingsRoutes.get("/code-injection", async (c) => {
912
+ const customHeadHtml =
913
+ c.var.allSettings[SETTINGS_KEYS.CUSTOM_HEAD_HTML] ?? "";
914
+ const customBodyEndHtml =
915
+ c.var.allSettings[SETTINGS_KEYS.CUSTOM_BODY_END_HTML] ?? "";
916
+ const navData = await getNavigationData(c);
917
+
918
+ return renderPublicPage(c, {
919
+ title: buildPageTitle("Code Injection", navData.siteName),
920
+ navData,
921
+ content: (
922
+ <>
923
+ <AdminBreadcrumb
924
+ parent={breadcrumbLabel(c, "settings")}
925
+ parentHref={publicPath(c, "/settings")}
926
+ current={breadcrumbLabel(c, "codeInjection")}
927
+ />
928
+ <CodeInjectionContent
929
+ customHeadHtml={customHeadHtml}
930
+ customBodyEndHtml={customBodyEndHtml}
931
+ sitePathPrefix={c.var.appConfig.sitePathPrefix}
932
+ />
933
+ </>
934
+ ),
935
+ });
936
+ });
937
+
938
+ settingsRoutes.post("/code-injection", async (c) => {
939
+ const i18n = getI18n(c);
940
+ const body = await c.req.json<{
941
+ customHeadHtml?: string;
942
+ customBodyEndHtml?: string;
943
+ }>();
944
+ const { settings } = c.var.services;
945
+
946
+ const headHtml = body.customHeadHtml?.trim() ?? "";
947
+ const bodyEndHtml = body.customBodyEndHtml?.trim() ?? "";
948
+
949
+ if (headHtml) {
950
+ await settings.set(SETTINGS_KEYS.CUSTOM_HEAD_HTML, headHtml);
951
+ } else {
952
+ await settings.remove(SETTINGS_KEYS.CUSTOM_HEAD_HTML);
953
+ }
954
+
955
+ if (bodyEndHtml) {
956
+ await settings.set(SETTINGS_KEYS.CUSTOM_BODY_END_HTML, bodyEndHtml);
957
+ } else {
958
+ await settings.remove(SETTINGS_KEYS.CUSTOM_BODY_END_HTML);
959
+ }
960
+
961
+ return dsToast(
962
+ i18n._(
963
+ msg({
964
+ message: "Code injection updated.",
965
+ comment: "@context: Toast after saving Code Injection settings",
966
+ }),
967
+ ),
968
+ );
969
+ });
970
+
816
971
  // ===========================================================================
817
972
  // Account sub-menu
818
973
  // ===========================================================================
@@ -834,9 +989,9 @@ settingsRoutes.get("/account", async (c) => {
834
989
  content: (
835
990
  <>
836
991
  <AdminBreadcrumb
837
- parent="Settings"
992
+ parent={breadcrumbLabel(c, "settings")}
838
993
  parentHref={publicPath(c, "/settings")}
839
- current="Account"
994
+ current={breadcrumbLabel(c, "account")}
840
995
  />
841
996
  <AccountMenuContent
842
997
  sitePathPrefix={c.var.appConfig.sitePathPrefix}
@@ -900,9 +1055,9 @@ settingsRoutes.get("/account/sessions", async (c) => {
900
1055
  content: (
901
1056
  <>
902
1057
  <AdminBreadcrumb
903
- parent="Account"
1058
+ parent={breadcrumbLabel(c, "account")}
904
1059
  parentHref={publicPath(c, "/settings/account")}
905
- current="Sessions"
1060
+ current={breadcrumbLabel(c, "sessions")}
906
1061
  />
907
1062
  <SessionsContent
908
1063
  sessions={sessions}
@@ -955,9 +1110,9 @@ settingsRoutes.get("/account/password", async (c) => {
955
1110
  content: (
956
1111
  <>
957
1112
  <AdminBreadcrumb
958
- parent="Account"
1113
+ parent={breadcrumbLabel(c, "account")}
959
1114
  parentHref={publicPath(c, "/settings/account")}
960
- current="Password"
1115
+ current={breadcrumbLabel(c, "password")}
961
1116
  />
962
1117
  <AccountContent sitePathPrefix={c.var.appConfig.sitePathPrefix} />
963
1118
  </>
@@ -1066,9 +1221,9 @@ settingsRoutes.get("/account/delete-account", async (c) => {
1066
1221
  content: (
1067
1222
  <>
1068
1223
  <AdminBreadcrumb
1069
- parent="Account"
1224
+ parent={breadcrumbLabel(c, "account")}
1070
1225
  parentHref={publicPath(c, "/settings/account")}
1071
- current="Delete Account"
1226
+ current={breadcrumbLabel(c, "deleteAccount")}
1072
1227
  />
1073
1228
  <DeleteAccountContent
1074
1229
  siteName={navData.siteName}
@@ -1152,9 +1307,9 @@ settingsRoutes.get("/api-tokens", async (c) => {
1152
1307
  content: (
1153
1308
  <>
1154
1309
  <AdminBreadcrumb
1155
- parent="Settings"
1310
+ parent={breadcrumbLabel(c, "settings")}
1156
1311
  parentHref={publicPath(c, "/settings")}
1157
- current="API Tokens"
1312
+ current={breadcrumbLabel(c, "apiTokens")}
1158
1313
  />
1159
1314
  <ApiTokensContent
1160
1315
  tokens={tokens}
@@ -1190,3 +1345,957 @@ settingsRoutes.post("/api-tokens/:id/delete", async (c) => {
1190
1345
 
1191
1346
  return dsRedirect(publicPath(c, "/settings/api-tokens"));
1192
1347
  });
1348
+
1349
+ // ===========================================================================
1350
+ // GitHub Sync
1351
+ // ===========================================================================
1352
+
1353
+ settingsRoutes.post("/github-sync/connect", async (c) => {
1354
+ // When a GitHub App is configured on this deployment, PAT connect is
1355
+ // disabled — users must go through the App install flow so we don't end
1356
+ // up with a mix of auth modes per site (harder to audit, easier to leak
1357
+ // a long-lived token by accident). The UI hides the PAT form too.
1358
+ if (getGitHubAppConfig(c.env)) {
1359
+ return dsToast(
1360
+ "This deployment uses GitHub App authentication. Use Install GitHub App instead.",
1361
+ "error",
1362
+ );
1363
+ }
1364
+
1365
+ const body = await c.req.json<{ token: string; repo: string }>();
1366
+
1367
+ if (!body.token?.trim() || !body.repo?.trim()) {
1368
+ return dsToast("Token and repository are required.", "error");
1369
+ }
1370
+
1371
+ const { parseRepoSlug, createGitHubClient } =
1372
+ await import("../../lib/github-api.js");
1373
+ const parsed = parseRepoSlug(body.repo);
1374
+ if (!parsed) {
1375
+ return dsToast("Invalid repository format. Use owner/repo.", "error");
1376
+ }
1377
+
1378
+ // Validate token
1379
+ const client = createGitHubClient(body.token);
1380
+ try {
1381
+ await client.getRepo(parsed.owner, parsed.repo);
1382
+ } catch (err) {
1383
+ const detail = err instanceof Error ? err.message : JSON.stringify(err);
1384
+ process.stderr.write(
1385
+ `[Jant] GitHub Sync connect failed for ${body.repo}: ${detail}\n`,
1386
+ );
1387
+ return dsToast(`Could not access the repository: ${detail}`, "error");
1388
+ }
1389
+
1390
+ // Check if this repo already has a Jant webhook
1391
+ try {
1392
+ const hooks = await client.listWebhooks(parsed.owner, parsed.repo);
1393
+ const existingJantHook = hooks.find((h) =>
1394
+ h.config.url?.includes("/api/github-sync/webhook"),
1395
+ );
1396
+ if (existingJantHook) {
1397
+ return dsToast(
1398
+ "This repository is already connected to a Jant site. Disconnect it first before connecting to a new site.",
1399
+ "error",
1400
+ );
1401
+ }
1402
+ } catch {
1403
+ // If listing webhooks fails (permissions), skip the check and continue
1404
+ }
1405
+
1406
+ // Save config
1407
+ await c.var.services.settings.set("GITHUB_SYNC_TOKEN", body.token);
1408
+ await c.var.services.settings.set("GITHUB_SYNC_REPO", body.repo);
1409
+ await c.var.services.settings.set("GITHUB_SYNC_AUTH_MODE", "pat");
1410
+ await c.var.services.settings.set("GITHUB_SYNC_APP_INSTALLATION_ID", "");
1411
+ await c.var.services.settings.set("GITHUB_SYNC_ENABLED", "true");
1412
+
1413
+ // Create webhook
1414
+ const { createGitHubSyncService } =
1415
+ await import("../../services/github-sync.js");
1416
+ const syncService = createGitHubSyncService(
1417
+ c.var.services,
1418
+ c.var.currentSite.id,
1419
+ await buildSyncSiteConfig(c),
1420
+ { storage: c.var.storage, githubApp: getGitHubAppConfig(c.env) },
1421
+ );
1422
+ const siteUrl = c.var.appConfig.siteUrl.replace(/\/+$/, "");
1423
+ try {
1424
+ await syncService.setupWebhook(`${siteUrl}/api/github-sync/webhook`);
1425
+ } catch {
1426
+ return dsToast(
1427
+ "Connected, but webhook creation failed. You may need to create it manually.",
1428
+ "error",
1429
+ );
1430
+ }
1431
+
1432
+ // Kick off an initial background push so "Last sync" doesn't sit on
1433
+ // "Not synced yet" until the user's next edit. See the App flow's
1434
+ // equivalent block below for why we bypass the queue.
1435
+ await markSyncPending(c.var.services.settings);
1436
+ const initialSync = runBackgroundSync(c.var.services.settings, syncService);
1437
+ try {
1438
+ c.executionCtx?.waitUntil(initialSync);
1439
+ } catch {
1440
+ // executionCtx unavailable (tests / Node).
1441
+ }
1442
+
1443
+ return dsRedirect(publicPath(c, "/settings/github-sync"));
1444
+ });
1445
+
1446
+ settingsRoutes.post("/github-sync/push", async (c) => {
1447
+ const { createGitHubSyncService } =
1448
+ await import("../../services/github-sync.js");
1449
+ const syncService = createGitHubSyncService(
1450
+ c.var.services,
1451
+ c.var.currentSite.id,
1452
+ await buildSyncSiteConfig(c),
1453
+ { storage: c.var.storage, githubApp: getGitHubAppConfig(c.env) },
1454
+ );
1455
+
1456
+ const config = await syncService.getConfig();
1457
+ if (!config) {
1458
+ return dsToast("GitHub Sync is not configured.", "error");
1459
+ }
1460
+
1461
+ // Run the push in the background so the status card's live "Syncing…"
1462
+ // indicator drives the UX instead of a toast. We patch the status card
1463
+ // into its pending state immediately so Datastar re-evaluates `data-init`
1464
+ // on the new element and opens the SSE status stream, which then polls
1465
+ // until the push completes.
1466
+ await markSyncPending(c.var.services.settings);
1467
+ const push = runBackgroundSync(c.var.services.settings, syncService);
1468
+ try {
1469
+ c.executionCtx?.waitUntil(push);
1470
+ } catch {
1471
+ // executionCtx unavailable (tests / Node).
1472
+ }
1473
+
1474
+ const status = await readGitHubSyncStatus(c); // pending=true after markSyncPending
1475
+ const streamUrl = getSyncStatusStreamUrl(c);
1476
+ const html = renderStatusCardHtml(c, status, streamUrl);
1477
+ return sse(c, async (stream) => {
1478
+ stream.patchElements(html, {
1479
+ mode: "outer",
1480
+ selector: "#github-sync-status",
1481
+ });
1482
+ });
1483
+ });
1484
+
1485
+ settingsRoutes.post("/github-sync/disconnect", async (c) => {
1486
+ const { createGitHubSyncService } =
1487
+ await import("../../services/github-sync.js");
1488
+ const syncService = createGitHubSyncService(
1489
+ c.var.services,
1490
+ c.var.currentSite.id,
1491
+ await buildSyncSiteConfig(c),
1492
+ { githubApp: getGitHubAppConfig(c.env) },
1493
+ );
1494
+ await syncService.teardownWebhook();
1495
+
1496
+ return dsRedirect(publicPath(c, "/settings/github-sync"));
1497
+ });
1498
+
1499
+ // ---------------------------------------------------------------------------
1500
+ // GitHub App install flow
1501
+ // ---------------------------------------------------------------------------
1502
+
1503
+ /**
1504
+ * Redirect the user to GitHub to install the App on their account/org.
1505
+ *
1506
+ * Only available when GitHub App env vars are configured. Uses a signed
1507
+ * state cookie for CSRF protection.
1508
+ */
1509
+ settingsRoutes.get("/github-sync/app/install", async (c) => {
1510
+ const app = getGitHubAppConfig(c.env);
1511
+ if (!app) {
1512
+ return c.text("GitHub App is not configured on this deployment.", 404);
1513
+ }
1514
+
1515
+ // Reinstall-same-account predicate (§5): when this site already has at
1516
+ // least one authorized installation, skip the GitHub round-trip and
1517
+ // render the picker directly. The GitHub redirect is a dead end when
1518
+ // the App is already installed on the chosen account — GitHub shows
1519
+ // its Configure page and never comes back with installation_id.
1520
+ //
1521
+ // `?force=new` bypasses the predicate so the picker's "Install on
1522
+ // another account" action can still reach GitHub to add a fresh
1523
+ // install.
1524
+ const forceNew = c.req.query("force") === "new";
1525
+ if (!forceNew) {
1526
+ const existing =
1527
+ await c.var.services.githubAppInstallations.listInstallationsForSite(
1528
+ c.var.currentSite.id,
1529
+ );
1530
+ if (existing.length > 0) {
1531
+ const navData = await getNavigationData(c);
1532
+ const base = publicPath(c, "/settings/github-sync");
1533
+ const labels = buildRepoPickerLabels(c);
1534
+ const suggestedRepoName = buildSuggestedRepoName(c);
1535
+ return renderPublicPage(c, {
1536
+ title: buildPageTitle(
1537
+ "GitHub Sync — Pick Repository",
1538
+ navData.siteName,
1539
+ ),
1540
+ navData,
1541
+ content: (
1542
+ <>
1543
+ <AdminBreadcrumb
1544
+ parent={breadcrumbLabel(c, "settings")}
1545
+ parentHref={publicPath(c, "/settings")}
1546
+ current={breadcrumbLabel(c, "githubSync")}
1547
+ />
1548
+ <jant-repo-picker
1549
+ labels={labels}
1550
+ api-base={`${base}/app`}
1551
+ connect-url={`${base}/app/connect`}
1552
+ install-url={`${base}/app/install?force=new`}
1553
+ cancel-url={publicPath(c, "/settings")}
1554
+ create-repo-name-hint={suggestedRepoName}
1555
+ >
1556
+ <div class="flex flex-col gap-6 max-w-lg">
1557
+ <div>
1558
+ <h2 class="text-lg font-medium mb-1">Pick a repository</h2>
1559
+ <p class="text-sm text-muted-foreground">
1560
+ Loading repositories…
1561
+ </p>
1562
+ </div>
1563
+ </div>
1564
+ </jant-repo-picker>
1565
+ </>
1566
+ ),
1567
+ });
1568
+ }
1569
+ }
1570
+
1571
+ // Build the state token. When running behind a hosted control plane we
1572
+ // sign host+nonce with the shared SSO secret so the control plane can
1573
+ // verify and route the callback back to the correct site host. In
1574
+ // self-hosted single-site mode the secret is absent and a plain nonce
1575
+ // suffices (the App's Callback URL points directly at this site).
1576
+ const nonce = generateInstallNonce();
1577
+ const ssoSecret = getHostedControlPlaneSsoSecret(c.env);
1578
+ const host = new URL(c.var.appConfig.siteUrl).host;
1579
+ const state = ssoSecret
1580
+ ? await signInstallState(host, nonce, ssoSecret)
1581
+ : nonce;
1582
+
1583
+ // Cookie is pinned to this host; compared byte-for-byte on the callback
1584
+ // to defeat CSRF regardless of whether the state is signed.
1585
+ setCookie(c, "jant_gh_app_state", state, {
1586
+ httpOnly: true,
1587
+ sameSite: "Lax",
1588
+ path: "/",
1589
+ maxAge: 600,
1590
+ });
1591
+
1592
+ return c.redirect(buildInstallUrl(app.slug, state));
1593
+ });
1594
+
1595
+ /**
1596
+ * Landing page after the user installs the GitHub App.
1597
+ *
1598
+ * GitHub redirects here with `installation_id`, `setup_action`, and the
1599
+ * `state` we sent. We verify the state, list the installation's repos, and
1600
+ * render a repo picker that POSTs back to `/github-sync/app/connect`.
1601
+ */
1602
+ settingsRoutes.get("/github-sync/app/callback", async (c) => {
1603
+ const app = getGitHubAppConfig(c.env);
1604
+ if (!app) {
1605
+ return c.text("GitHub App is not configured on this deployment.", 404);
1606
+ }
1607
+
1608
+ const installationId = c.req.query("installation_id");
1609
+ const state = c.req.query("state");
1610
+ if (!installationId) {
1611
+ return c.text("Missing installation_id.", 400);
1612
+ }
1613
+
1614
+ const expected = getCookie(c, "jant_gh_app_state");
1615
+ if (!state || !expected || expected !== state) {
1616
+ return c.text("Invalid or expired state.", 400);
1617
+ }
1618
+
1619
+ // Defense in depth: when an SSO secret is present the state should also
1620
+ // HMAC-verify and its embedded host must match the host serving this
1621
+ // request. This blocks a rogue control plane from redirecting a victim
1622
+ // to the wrong site with a replayed token.
1623
+ const ssoSecret = getHostedControlPlaneSsoSecret(c.env);
1624
+ if (ssoSecret) {
1625
+ const payload = await verifyInstallState(state, ssoSecret);
1626
+ const currentHost = new URL(c.var.appConfig.siteUrl).host;
1627
+ if (!payload || payload.host !== currentHost) {
1628
+ return c.text("State signature invalid for this host.", 400);
1629
+ }
1630
+ }
1631
+
1632
+ // One-shot: clear the cookie so it can't be replayed.
1633
+ setCookie(c, "jant_gh_app_state", "", {
1634
+ httpOnly: true,
1635
+ sameSite: "Lax",
1636
+ path: "/",
1637
+ maxAge: 0,
1638
+ });
1639
+
1640
+ // Fetch the account info for this installation and remember it. The
1641
+ // picker's owner dropdown reads the stored list, so subsequent visits
1642
+ // can switch between accounts without re-running the install flow.
1643
+ // Failures are non-fatal: this callback must still render the picker
1644
+ // so the user isn't locked out right after a successful install.
1645
+ try {
1646
+ const installation = await getInstallation(app, installationId);
1647
+ await c.var.services.githubAppInstallations.upsertInstallation(
1648
+ installationId,
1649
+ c.var.currentSite.id,
1650
+ installation.account,
1651
+ );
1652
+ } catch {
1653
+ // Swallow — the legacy inline picker still works with just the
1654
+ // installation_id from the URL, just without the owner list.
1655
+ }
1656
+
1657
+ const navData = await getNavigationData(c);
1658
+ const base = publicPath(c, "/settings/github-sync");
1659
+ const labels = buildRepoPickerLabels(c);
1660
+ const suggestedRepoName = buildSuggestedRepoName(c);
1661
+
1662
+ return renderPublicPage(c, {
1663
+ title: buildPageTitle("GitHub Sync — Pick Repository", navData.siteName),
1664
+ navData,
1665
+ content: (
1666
+ <>
1667
+ <AdminBreadcrumb
1668
+ parent={breadcrumbLabel(c, "settings")}
1669
+ parentHref={publicPath(c, "/settings")}
1670
+ current={breadcrumbLabel(c, "githubSync")}
1671
+ />
1672
+ <jant-repo-picker
1673
+ labels={labels}
1674
+ api-base={`${base}/app`}
1675
+ connect-url={`${base}/app/connect`}
1676
+ install-url={`${base}/app/install`}
1677
+ cancel-url={publicPath(c, "/settings")}
1678
+ create-repo-name-hint={suggestedRepoName}
1679
+ >
1680
+ {/* SSR fallback while the Lit component upgrades. */}
1681
+ <div class="flex flex-col gap-6 max-w-lg">
1682
+ <div>
1683
+ <h2 class="text-lg font-medium mb-1">Pick a repository</h2>
1684
+ <p class="text-sm text-muted-foreground">Loading repositories…</p>
1685
+ </div>
1686
+ </div>
1687
+ </jant-repo-picker>
1688
+ </>
1689
+ ),
1690
+ });
1691
+ });
1692
+
1693
+ /**
1694
+ * Derive a default repository name to prefill on github.com/new.
1695
+ *
1696
+ * Uses the site's host — the first DNS label is a stable, URL-safe
1697
+ * identifier tied to this specific Jant instance. Fallback to
1698
+ * "jant-site-sync" when the host parse fails so we never hand GitHub an
1699
+ * empty `name=`. The `-jant-sync` suffix disambiguates the sync mirror
1700
+ * from a user's own `{slug}-jant` source repo.
1701
+ */
1702
+ function buildSuggestedRepoName(c: Context<Env>): string {
1703
+ let firstLabel = "";
1704
+ try {
1705
+ firstLabel = new URL(c.var.appConfig.siteUrl).host.split(".")[0] ?? "";
1706
+ } catch {
1707
+ /* fall through */
1708
+ }
1709
+ const slug = firstLabel
1710
+ .toLowerCase()
1711
+ .replace(/[^a-z0-9-_]+/g, "-")
1712
+ .replace(/^-+|-+$/g, "");
1713
+ return slug ? `${slug}-jant-sync` : "jant-site-sync";
1714
+ }
1715
+
1716
+ function buildRepoPickerLabels(c: Context<Env>): string {
1717
+ const i18n = getI18n(c);
1718
+ return JSON.stringify({
1719
+ pageTitle: i18n._(
1720
+ msg({
1721
+ message: "Pick a repository",
1722
+ comment: "@context: GitHub sync picker — page heading",
1723
+ }),
1724
+ ),
1725
+ pageSubtitle: i18n._(
1726
+ msg({
1727
+ message:
1728
+ "Choose the GitHub account and repository to sync with this site.",
1729
+ comment: "@context: GitHub sync picker — page subtitle",
1730
+ }),
1731
+ ),
1732
+ ownerLabel: i18n._(
1733
+ msg({
1734
+ message: "Account",
1735
+ comment: "@context: GitHub sync picker — owner dropdown label",
1736
+ }),
1737
+ ),
1738
+ ownerPlaceholder: i18n._(
1739
+ msg({
1740
+ message: "Select an account",
1741
+ comment:
1742
+ "@context: GitHub sync picker — owner dropdown placeholder when nothing is selected",
1743
+ }),
1744
+ ),
1745
+ ownerEmpty: i18n._(
1746
+ msg({
1747
+ message: "No accounts authorized yet",
1748
+ comment:
1749
+ "@context: GitHub sync picker — shown when the owner list is empty",
1750
+ }),
1751
+ ),
1752
+ installAnother: i18n._(
1753
+ msg({
1754
+ message: "+ Install on another account",
1755
+ comment:
1756
+ "@context: GitHub sync picker — entry at the bottom of the owner dropdown that starts a new install flow",
1757
+ }),
1758
+ ),
1759
+ repositoryLabel: i18n._(
1760
+ msg({
1761
+ message: "Repository",
1762
+ comment: "@context: GitHub sync picker — repo dropdown label",
1763
+ }),
1764
+ ),
1765
+ repoPlaceholderNoOwner: i18n._(
1766
+ msg({
1767
+ message: "Pick an account first",
1768
+ comment:
1769
+ "@context: GitHub sync picker — repo dropdown placeholder before an owner is chosen",
1770
+ }),
1771
+ ),
1772
+ repoPlaceholder: i18n._(
1773
+ msg({
1774
+ message: "Select a repository",
1775
+ comment: "@context: GitHub sync picker — repo dropdown placeholder",
1776
+ }),
1777
+ ),
1778
+ repoSearchPlaceholder: i18n._(
1779
+ msg({
1780
+ message: "Search repositories",
1781
+ comment: "@context: GitHub sync picker — search input placeholder",
1782
+ }),
1783
+ ),
1784
+ repoEmpty: i18n._(
1785
+ msg({
1786
+ message: "No repositories match.",
1787
+ comment:
1788
+ "@context: GitHub sync picker — empty state when search returns nothing",
1789
+ }),
1790
+ ),
1791
+ repoLoading: i18n._(
1792
+ msg({
1793
+ message: "Loading…",
1794
+ comment: "@context: GitHub sync picker — loading state",
1795
+ }),
1796
+ ),
1797
+ repoShowingOf: i18n._(
1798
+ msg({
1799
+ message: "Showing {shown} of {total}",
1800
+ comment:
1801
+ "@context: GitHub sync picker — paginated repo count hint. Placeholders {shown} and {total} are integers.",
1802
+ }),
1803
+ ),
1804
+ repoSearchHint: i18n._(
1805
+ msg({
1806
+ message: "type to search all",
1807
+ comment:
1808
+ "@context: GitHub sync picker — hint telling the user typing will search the full list",
1809
+ }),
1810
+ ),
1811
+ refreshRepos: i18n._(
1812
+ msg({
1813
+ message: "Refresh repository list",
1814
+ comment:
1815
+ "@context: GitHub sync picker — tooltip for the refresh button next to the search input",
1816
+ }),
1817
+ ),
1818
+ createOnGitHub: i18n._(
1819
+ msg({
1820
+ message: "Create a new repository on GitHub",
1821
+ comment:
1822
+ "@context: GitHub sync picker — entry that opens github.com/new in a new tab",
1823
+ }),
1824
+ ),
1825
+ createOnGitHubHint: i18n._(
1826
+ msg({
1827
+ message: "We'll prefill the name {name}. The list refreshes on return.",
1828
+ comment:
1829
+ "@context: GitHub sync picker — hint under the create-on-github entry. Placeholder {name} is the suggested repo name.",
1830
+ }),
1831
+ ),
1832
+ classifyLoading: i18n._(
1833
+ msg({
1834
+ message: "Checking repository…",
1835
+ comment:
1836
+ "@context: GitHub sync picker — status text while classifying the picked repo",
1837
+ }),
1838
+ ),
1839
+ classificationEmpty: i18n._(
1840
+ msg({
1841
+ message: "Empty repository. Ready to connect.",
1842
+ comment:
1843
+ "@context: GitHub sync picker — shown when the selected repo has no commits",
1844
+ }),
1845
+ ),
1846
+ classificationOwned: i18n._(
1847
+ msg({
1848
+ message: "This repository is already backing up this site.",
1849
+ comment:
1850
+ "@context: GitHub sync picker — shown when the selected repo's marker matches this site",
1851
+ }),
1852
+ ),
1853
+ classificationOwnedByOther: i18n._(
1854
+ msg({
1855
+ message:
1856
+ "This repository is already backing up another Jant site ({host}). Pick a different repository.",
1857
+ comment:
1858
+ "@context: GitHub sync picker — blocking message when marker belongs to another site. Placeholder {host} is the other site's host.",
1859
+ }),
1860
+ ),
1861
+ classificationForeign: i18n._(
1862
+ msg({
1863
+ message: "This repository has existing content.",
1864
+ comment:
1865
+ "@context: GitHub sync picker — heading above the foreign-repo confirmation",
1866
+ }),
1867
+ ),
1868
+ confirmHeading: i18n._(
1869
+ msg({
1870
+ message: "This repository already has commits",
1871
+ comment:
1872
+ "@context: GitHub sync picker — confirmation card heading for foreign repos",
1873
+ }),
1874
+ ),
1875
+ confirmBody: i18n._(
1876
+ msg({
1877
+ message:
1878
+ "Connecting will sync your site onto {repo}'s default branch on top of its existing history. Existing files outside Jant's managed paths are kept. This can't be undone.",
1879
+ comment:
1880
+ "@context: GitHub sync picker — explanatory body for foreign-repo confirmation. Placeholder {repo} is the owner/repo slug.",
1881
+ }),
1882
+ ),
1883
+ confirmInputLabel: i18n._(
1884
+ msg({
1885
+ message: "Type {repo} to confirm",
1886
+ comment:
1887
+ "@context: GitHub sync picker — label above the typed-confirmation input",
1888
+ }),
1889
+ ),
1890
+ confirmInputPlaceholder: i18n._(
1891
+ msg({
1892
+ message: "owner/repo",
1893
+ comment:
1894
+ "@context: GitHub sync picker — placeholder text showing the expected input shape",
1895
+ }),
1896
+ ),
1897
+ cancel: i18n._(
1898
+ msg({
1899
+ message: "Cancel",
1900
+ comment: "@context: GitHub sync picker — cancel action",
1901
+ }),
1902
+ ),
1903
+ connect: i18n._(
1904
+ msg({
1905
+ message: "Connect",
1906
+ comment: "@context: GitHub sync picker — primary action button",
1907
+ }),
1908
+ ),
1909
+ connecting: i18n._(
1910
+ msg({
1911
+ message: "Connecting…",
1912
+ comment:
1913
+ "@context: GitHub sync picker — primary action button while a request is in flight",
1914
+ }),
1915
+ ),
1916
+ privateBadge: i18n._(
1917
+ msg({
1918
+ message: "Private",
1919
+ comment: "@context: GitHub sync picker — badge next to private repos",
1920
+ }),
1921
+ ),
1922
+ connectionFailed: i18n._(
1923
+ msg({
1924
+ message: "Couldn't connect. Check the error and try again.",
1925
+ comment:
1926
+ "@context: GitHub sync picker — generic failure message for Connect",
1927
+ }),
1928
+ ),
1929
+ retry: i18n._(
1930
+ msg({
1931
+ message: "Try again",
1932
+ comment: "@context: GitHub sync picker — retry button",
1933
+ }),
1934
+ ),
1935
+ }).replace(/</g, "\\u003c");
1936
+ }
1937
+
1938
+ /**
1939
+ * Finalize the App connection: validate access, classify the target repo
1940
+ * to gate foreign/conflicting choices behind explicit user confirmation,
1941
+ * then persist the installation id and chosen repo and register the webhook.
1942
+ *
1943
+ * Accepts both classic form submits (redirects on success) and JSON
1944
+ * requests (returns JSON). Phase 3's picker component uses the JSON path.
1945
+ */
1946
+ settingsRoutes.post("/github-sync/app/connect", async (c) => {
1947
+ const app = getGitHubAppConfig(c.env);
1948
+ if (!app) {
1949
+ return c.text("GitHub App is not configured on this deployment.", 404);
1950
+ }
1951
+
1952
+ const wantsJson = c.req.header("content-type")?.includes("application/json");
1953
+ const body = wantsJson
1954
+ ? ((await c.req.json().catch(() => ({}))) as Record<string, unknown>)
1955
+ : await c.req.parseBody();
1956
+
1957
+ const installationId = String(body.installationId ?? "").trim();
1958
+ const repo = String(body.repo ?? "").trim();
1959
+ const confirmForeign =
1960
+ body.confirmForeign === true || body.confirmForeign === "true";
1961
+ if (!installationId || !repo) {
1962
+ return wantsJson
1963
+ ? c.json({ error: "Missing installationId or repo." }, 400)
1964
+ : c.text("Missing installationId or repo.", 400);
1965
+ }
1966
+
1967
+ const { parseRepoSlug, createGitHubClient } =
1968
+ await import("../../lib/github-api.js");
1969
+ const parsed = parseRepoSlug(repo);
1970
+ if (!parsed) {
1971
+ return wantsJson
1972
+ ? c.json({ error: "Invalid repository format." }, 400)
1973
+ : c.text("Invalid repository format.", 400);
1974
+ }
1975
+
1976
+ // Classify the repo before we commit any state: empty and already-owned
1977
+ // targets proceed immediately; foreign or other-site targets require an
1978
+ // explicit `confirmForeign` flag from the caller (a typed confirmation
1979
+ // in the UI) to avoid silently pushing onto unrelated histories.
1980
+ const { classifyRepoForSync } = await import("../../services/github-sync.js");
1981
+ const ghClient = createGitHubClient(() =>
1982
+ getInstallationTokenFromApp(app, installationId),
1983
+ );
1984
+ let classification;
1985
+ try {
1986
+ classification = await classifyRepoForSync(
1987
+ ghClient,
1988
+ parsed.owner,
1989
+ parsed.repo,
1990
+ c.var.currentSite.id,
1991
+ );
1992
+ } catch (err) {
1993
+ const detail = err instanceof Error ? err.message : String(err);
1994
+ return wantsJson
1995
+ ? c.json({ error: `Could not read repository: ${detail}` }, 500)
1996
+ : c.text(`Could not read repository: ${detail}`, 500);
1997
+ }
1998
+
1999
+ if (classification.kind === "owned-by-other-site") {
2000
+ const msg = `This repository is already backing up another Jant site (${classification.marker.site_host}). Pick a different repository.`;
2001
+ return wantsJson
2002
+ ? c.json(
2003
+ {
2004
+ error: msg,
2005
+ classification: "owned-by-other-site",
2006
+ marker: classification.marker,
2007
+ },
2008
+ 409,
2009
+ )
2010
+ : c.text(msg, 409);
2011
+ }
2012
+
2013
+ if (classification.kind === "foreign" && !confirmForeign) {
2014
+ const msg = `${repo} already has commits but isn't managed by Jant. Re-submit with confirmation to push on top of its history.`;
2015
+ return wantsJson
2016
+ ? c.json(
2017
+ {
2018
+ error: msg,
2019
+ classification: "foreign",
2020
+ defaultBranch: classification.defaultBranch,
2021
+ },
2022
+ 409,
2023
+ )
2024
+ : c.text(msg, 409);
2025
+ }
2026
+
2027
+ // Persist config before creating webhook so the sync service can load it.
2028
+ await c.var.services.settings.set("GITHUB_SYNC_AUTH_MODE", "app");
2029
+ await c.var.services.settings.set(
2030
+ "GITHUB_SYNC_APP_INSTALLATION_ID",
2031
+ installationId,
2032
+ );
2033
+ await c.var.services.settings.set("GITHUB_SYNC_REPO", repo);
2034
+ await c.var.services.settings.set("GITHUB_SYNC_TOKEN", "");
2035
+ await c.var.services.settings.set("GITHUB_SYNC_ENABLED", "true");
2036
+
2037
+ const { createGitHubSyncService } =
2038
+ await import("../../services/github-sync.js");
2039
+ const syncService = createGitHubSyncService(
2040
+ c.var.services,
2041
+ c.var.currentSite.id,
2042
+ await buildSyncSiteConfig(c),
2043
+ { storage: c.var.storage, githubApp: app },
2044
+ );
2045
+ const siteUrl = c.var.appConfig.siteUrl.replace(/\/+$/, "");
2046
+ try {
2047
+ await syncService.setupWebhook(`${siteUrl}/api/github-sync/webhook`);
2048
+ } catch (err) {
2049
+ const detail = err instanceof Error ? err.message : String(err);
2050
+ const msg = `Connected, but webhook creation failed: ${detail}. You may need to create it manually.`;
2051
+ return wantsJson ? c.json({ error: msg }, 500) : c.text(msg, 500);
2052
+ }
2053
+
2054
+ // Kick off an initial push so the user's content lands in the repo
2055
+ // immediately. We can't use the queue path: CF Queue isn't wired up
2056
+ // for most self-hosted deployments, and the noop fallback would
2057
+ // silently drop the job. Instead, set PENDING and run the sync in
2058
+ // the background via waitUntil. The status page reads PENDING and
2059
+ // polls until it clears, so the user sees "Syncing…" right away.
2060
+ await markSyncPending(c.var.services.settings);
2061
+ const initialSync = runBackgroundSync(c.var.services.settings, syncService);
2062
+ try {
2063
+ c.executionCtx?.waitUntil(initialSync);
2064
+ } catch {
2065
+ // executionCtx unavailable (tests / Node) — let the promise resolve
2066
+ // on its own; response still returns immediately.
2067
+ }
2068
+
2069
+ const redirect = publicPath(c, "/settings/github-sync");
2070
+ return wantsJson ? c.json({ ok: true, redirect }) : c.redirect(redirect);
2071
+ });
2072
+
2073
+ // ---------------------------------------------------------------------------
2074
+ // GitHub App picker JSON API (consumed by the jant-repo-picker component)
2075
+ // ---------------------------------------------------------------------------
2076
+
2077
+ /**
2078
+ * Helper: load the GitHub App config or short-circuit with a 404 JSON error.
2079
+ */
2080
+ type RequireAppResult =
2081
+ | { app: import("../../lib/env.js").GitHubAppEnvConfig; response: null }
2082
+ | { app: null; response: Response };
2083
+
2084
+ function requireGitHubApp(c: Context<Env>): RequireAppResult {
2085
+ const app = getGitHubAppConfig(c.env);
2086
+ if (!app) {
2087
+ return {
2088
+ app: null,
2089
+ response: c.json(
2090
+ { error: "GitHub App is not configured on this deployment." },
2091
+ 404,
2092
+ ),
2093
+ };
2094
+ }
2095
+ return { app, response: null };
2096
+ }
2097
+
2098
+ /**
2099
+ * Dynamic import shim for `getInstallationToken`. Used by `/connect` to
2100
+ * build a client for classification without adding the helper to the
2101
+ * top-level imports (the module already lazy-imports github-api).
2102
+ */
2103
+ async function getInstallationTokenFromApp(
2104
+ app: import("../../lib/env.js").GitHubAppEnvConfig,
2105
+ installationId: string,
2106
+ ): Promise<string> {
2107
+ const { getInstallationToken } = await import("../../lib/github-app.js");
2108
+ return getInstallationToken(app, installationId);
2109
+ }
2110
+
2111
+ /** List GitHub App installations authorized for this site. */
2112
+ settingsRoutes.get("/github-sync/app/installations", async (c) => {
2113
+ const installations =
2114
+ await c.var.services.githubAppInstallations.listInstallationsForSite(
2115
+ c.var.currentSite.id,
2116
+ );
2117
+ return c.json({
2118
+ installations: installations.map((entry) => ({
2119
+ installationId: entry.installationId,
2120
+ account: entry.account,
2121
+ addedAt: entry.addedAt,
2122
+ })),
2123
+ });
2124
+ });
2125
+
2126
+ /**
2127
+ * List (or search) repositories accessible via an installation.
2128
+ *
2129
+ * Query params:
2130
+ * - installationId (required)
2131
+ * - page (default 1) — only used when `q` is empty
2132
+ * - q (optional) — when non-empty switches to GitHub search API
2133
+ *
2134
+ * 401 from GitHub indicates the installation was removed; we lazy-clean
2135
+ * the stored entry and return a 410 so the UI can refresh its list.
2136
+ */
2137
+ settingsRoutes.get("/github-sync/app/repos", async (c) => {
2138
+ const { app, response } = requireGitHubApp(c);
2139
+ if (!app) return response;
2140
+
2141
+ const installationId = c.req.query("installationId")?.trim();
2142
+ if (!installationId) {
2143
+ return c.json({ error: "Missing installationId." }, 400);
2144
+ }
2145
+ const q = c.req.query("q")?.trim() ?? "";
2146
+ const pageParam = Number(c.req.query("page") ?? "1");
2147
+ const page =
2148
+ Number.isFinite(pageParam) && pageParam > 0 ? Math.floor(pageParam) : 1;
2149
+
2150
+ try {
2151
+ if (q) {
2152
+ const installations =
2153
+ await c.var.services.githubAppInstallations.listInstallationsForSite(
2154
+ c.var.currentSite.id,
2155
+ );
2156
+ const installation = installations.find(
2157
+ (i) => i.installationId === installationId,
2158
+ );
2159
+ if (!installation) {
2160
+ return c.json({ error: "Unknown installation." }, 404);
2161
+ }
2162
+ const result = await searchInstallationRepos(
2163
+ app,
2164
+ installationId,
2165
+ installation.account.login,
2166
+ q,
2167
+ );
2168
+ return c.json({
2169
+ repos: result.repos,
2170
+ totalCount: result.totalCount,
2171
+ hasMore: false, // search API doesn't page for us here
2172
+ nextPage: null,
2173
+ mode: "search",
2174
+ });
2175
+ }
2176
+
2177
+ const result = await listInstallationReposPage(app, installationId, page);
2178
+ return c.json({
2179
+ repos: result.repos,
2180
+ totalCount: result.totalCount,
2181
+ hasMore: result.hasMore,
2182
+ nextPage: result.nextPage,
2183
+ mode: "list",
2184
+ });
2185
+ } catch (err) {
2186
+ const detail = err instanceof Error ? err.message : String(err);
2187
+ // GitHub returns 401 when an installation was uninstalled on their
2188
+ // side. Clean up our cached entry so the UI stops showing a dead owner.
2189
+ if (/\b401\b/.test(detail) || /\b404\b/.test(detail)) {
2190
+ await c.var.services.githubAppInstallations.removeInstallation(
2191
+ installationId,
2192
+ c.var.currentSite.id,
2193
+ );
2194
+ return c.json(
2195
+ { error: "Installation is no longer accessible.", removed: true },
2196
+ 410,
2197
+ );
2198
+ }
2199
+ return c.json({ error: detail }, 500);
2200
+ }
2201
+ });
2202
+
2203
+ /**
2204
+ * Classify a repo: empty / owned / owned-by-other-site / foreign.
2205
+ *
2206
+ * The picker calls this when the user picks a repo so it can show an
2207
+ * appropriate confirmation UI before submitting `/connect`.
2208
+ */
2209
+ settingsRoutes.post("/github-sync/app/classify", async (c) => {
2210
+ const { app, response } = requireGitHubApp(c);
2211
+ if (!app) return response;
2212
+
2213
+ const body = (await c.req.json().catch(() => ({}))) as Record<
2214
+ string,
2215
+ unknown
2216
+ >;
2217
+ const installationId = String(body.installationId ?? "").trim();
2218
+ const repo = String(body.repo ?? "").trim();
2219
+ if (!installationId || !repo) {
2220
+ return c.json({ error: "Missing installationId or repo." }, 400);
2221
+ }
2222
+
2223
+ const { parseRepoSlug, createGitHubClient } =
2224
+ await import("../../lib/github-api.js");
2225
+ const parsed = parseRepoSlug(repo);
2226
+ if (!parsed) {
2227
+ return c.json({ error: "Invalid repository format." }, 400);
2228
+ }
2229
+
2230
+ const { classifyRepoForSync } = await import("../../services/github-sync.js");
2231
+ const client = createGitHubClient(() =>
2232
+ getInstallationTokenFromApp(app, installationId),
2233
+ );
2234
+ try {
2235
+ const classification = await classifyRepoForSync(
2236
+ client,
2237
+ parsed.owner,
2238
+ parsed.repo,
2239
+ c.var.currentSite.id,
2240
+ );
2241
+ return c.json({ classification });
2242
+ } catch (err) {
2243
+ const detail = err instanceof Error ? err.message : String(err);
2244
+ return c.json({ error: detail }, 500);
2245
+ }
2246
+ });
2247
+
2248
+ settingsRoutes.get("/github-sync", async (c) => {
2249
+ const [
2250
+ enabled,
2251
+ repo,
2252
+ lastPushSha,
2253
+ webhookId,
2254
+ lastPushAt,
2255
+ authMode,
2256
+ lastError,
2257
+ ] = await Promise.all([
2258
+ c.var.services.settings.get("GITHUB_SYNC_ENABLED"),
2259
+ c.var.services.settings.get("GITHUB_SYNC_REPO"),
2260
+ c.var.services.settings.get("GITHUB_SYNC_LAST_PUSH_SHA"),
2261
+ c.var.services.settings.get("GITHUB_SYNC_WEBHOOK_ID"),
2262
+ c.var.services.settings.get("GITHUB_SYNC_LAST_PUSH_AT"),
2263
+ c.var.services.settings.get("GITHUB_SYNC_AUTH_MODE"),
2264
+ c.var.services.settings.get("GITHUB_SYNC_LAST_ERROR"),
2265
+ ]);
2266
+ // Use isSyncPending (not raw flag) so a worker that died mid-push
2267
+ // doesn't leave the UI stuck on "Syncing…" forever.
2268
+ const pending = await isSyncPending(c.var.services.settings);
2269
+
2270
+ const status: GitHubSyncStatus = {
2271
+ enabled: enabled === "true",
2272
+ repo: repo ?? null,
2273
+ lastPushSha: lastPushSha ?? null,
2274
+ webhookId: webhookId ?? null,
2275
+ lastPushAt: lastPushAt ? Number(lastPushAt) : null,
2276
+ authMode: authMode === "app" ? "app" : "pat",
2277
+ appConfigured: getGitHubAppConfig(c.env) !== null,
2278
+ pending,
2279
+ lastError: lastError || null,
2280
+ };
2281
+
2282
+ const navData = await getNavigationData(c);
2283
+
2284
+ return renderPublicPage(c, {
2285
+ title: buildPageTitle("GitHub Sync", navData.siteName),
2286
+ navData,
2287
+ content: (
2288
+ <>
2289
+ <AdminBreadcrumb
2290
+ parent={breadcrumbLabel(c, "settings")}
2291
+ parentHref={publicPath(c, "/settings")}
2292
+ current={breadcrumbLabel(c, "githubSync")}
2293
+ />
2294
+ <GitHubSyncContent
2295
+ status={status}
2296
+ sitePathPrefix={c.var.appConfig.sitePathPrefix}
2297
+ />
2298
+ </>
2299
+ ),
2300
+ });
2301
+ });