@jant/core 0.3.40 → 0.3.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1160) hide show
  1. package/README.md +21 -34
  2. package/bin/commands/assets/upload.js +247 -0
  3. package/bin/commands/collections.js +268 -0
  4. package/bin/commands/import-site.js +1169 -343
  5. package/bin/commands/media.js +302 -0
  6. package/bin/commands/migrate-text-attachments.js +183 -0
  7. package/bin/commands/migrate.js +263 -2
  8. package/bin/commands/posts.js +262 -0
  9. package/bin/commands/search.js +53 -0
  10. package/bin/commands/settings.js +93 -0
  11. package/bin/commands/site/export.js +34 -33
  12. package/bin/commands/site/{localize-media.js → pull-media.js} +17 -17
  13. package/bin/lib/d1-query.js +15 -3
  14. package/bin/lib/http-api.js +223 -0
  15. package/bin/lib/media-upload.js +206 -0
  16. package/bin/lib/migration-runner.js +64 -0
  17. package/bin/lib/runtime-target.js +7 -2
  18. package/bin/lib/site-media-parser.js +71 -16
  19. package/bin/lib/site-pull-media.js +698 -0
  20. package/bin/lib/site-snapshot.js +0 -1
  21. package/dist/{app-CAtsuLLh.js → app-Cu3lveYI.js} +23768 -23057
  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 +60 -13
  367. package/src/client/__tests__/collection-form-bridge.test.ts +87 -7
  368. package/src/client/__tests__/collection-page-actions.test.ts +4 -4
  369. package/src/client/__tests__/collection-picker-order.test.ts +53 -0
  370. package/src/client/__tests__/collection-sort-menu.test.ts +2 -2
  371. package/src/client/__tests__/compose-bridge.test.ts +305 -13
  372. package/src/client/__tests__/compose-launch.test.ts +120 -0
  373. package/src/client/__tests__/compose-shortcuts.test.ts +141 -0
  374. package/src/client/__tests__/feed-video-player.test.ts +44 -0
  375. package/src/client/__tests__/site-header-nav.test.ts +250 -0
  376. package/src/client/__tests__/sortable-list.test.ts +6 -2
  377. package/src/client/__tests__/thread-context.test.ts +117 -0
  378. package/src/client/collection-form-bridge.ts +109 -4
  379. package/src/client/collection-page-actions.ts +3 -1
  380. package/src/client/collection-picker-order.ts +104 -0
  381. package/src/client/components/__tests__/{jant-collection-sidebar.test.ts → jant-collection-directory.test.ts} +91 -10
  382. package/src/client/components/__tests__/jant-collection-form.test.ts +40 -18
  383. package/src/client/components/__tests__/jant-compose-dialog.test.ts +1285 -66
  384. package/src/client/components/__tests__/jant-compose-editor.test.ts +254 -19
  385. package/src/client/components/__tests__/jant-compose-fullscreen.test.ts +127 -1
  386. package/src/client/components/__tests__/jant-media-lightbox.test.ts +237 -0
  387. package/src/client/components/__tests__/jant-nav-manager.test.ts +326 -0
  388. package/src/client/components/__tests__/jant-post-menu.test.ts +372 -2
  389. package/src/client/components/__tests__/jant-settings-avatar.test.ts +61 -5
  390. package/src/client/components/__tests__/jant-settings-general.test.ts +29 -28
  391. package/src/client/components/__tests__/jant-text-preview.test.ts +206 -0
  392. package/src/client/components/collection-manager-types.ts +18 -1
  393. package/src/client/components/compose-types.ts +48 -0
  394. package/src/client/components/jant-collection-directory.ts +1448 -0
  395. package/src/client/components/jant-collection-form.ts +65 -17
  396. package/src/client/components/jant-command-palette.ts +544 -0
  397. package/src/client/components/jant-compose-dialog.ts +2490 -375
  398. package/src/client/components/jant-compose-editor.ts +420 -75
  399. package/src/client/components/jant-compose-fullscreen.ts +59 -8
  400. package/src/client/components/jant-media-lightbox.ts +373 -18
  401. package/src/client/components/jant-nav-manager.ts +624 -286
  402. package/src/client/components/jant-post-menu.ts +451 -126
  403. package/src/client/components/jant-repo-picker-types.ts +39 -0
  404. package/src/client/components/jant-repo-picker.ts +799 -0
  405. package/src/client/components/jant-settings-avatar.ts +16 -2
  406. package/src/client/components/jant-settings-general.ts +137 -35
  407. package/src/client/components/jant-text-preview.ts +239 -55
  408. package/src/client/components/nav-manager-types.ts +25 -9
  409. package/src/client/components/settings-types.ts +13 -2
  410. package/src/client/compose-bridge.ts +428 -68
  411. package/src/client/compose-launch.ts +41 -4
  412. package/src/client/compose-shortcuts.ts +77 -5
  413. package/src/client/feed-video-player.ts +374 -0
  414. package/src/client/image-processor.ts +53 -127
  415. package/src/client/media-lightbox-events.ts +1 -0
  416. package/src/client/media-scroll-hint.ts +66 -0
  417. package/src/client/multipart-upload.ts +4 -1
  418. package/src/client/palette-shortcuts.ts +35 -0
  419. package/src/client/search-rank.ts +74 -0
  420. package/src/client/settings-bridge.ts +7 -0
  421. package/src/client/site-header-nav.d.ts +1 -0
  422. package/src/client/site-header-nav.js +139 -51
  423. package/src/client/sortable-list.ts +2 -0
  424. package/src/client/thread-context.ts +54 -11
  425. package/src/client/tiptap/__tests__/block-insertion.test.ts +65 -0
  426. package/src/client/tiptap/__tests__/footnotes.test.ts +692 -0
  427. package/src/client/tiptap/__tests__/link-toolbar.test.ts +120 -4
  428. package/src/client/tiptap/__tests__/markdown-clipboard.test.ts +48 -0
  429. package/src/client/tiptap/block-insertion.ts +36 -0
  430. package/src/client/tiptap/bubble-menu.ts +34 -4
  431. package/src/client/tiptap/create-editor.ts +49 -30
  432. package/src/client/tiptap/embed-dialog.ts +301 -0
  433. package/src/client/tiptap/embed-node.ts +373 -0
  434. package/src/client/tiptap/embed-paste.ts +91 -0
  435. package/src/client/tiptap/extensions.ts +77 -28
  436. package/src/client/tiptap/footnotes.ts +718 -0
  437. package/src/client/tiptap/html-block-node.ts +222 -0
  438. package/src/client/tiptap/image-node.ts +49 -6
  439. package/src/client/tiptap/inline-image-upload.ts +200 -40
  440. package/src/client/tiptap/link-input-rules.ts +50 -16
  441. package/src/client/tiptap/link-toolbar.ts +156 -133
  442. package/src/client/tiptap/markdown-clipboard.ts +120 -0
  443. package/src/client/tiptap/more-break.ts +36 -3
  444. package/src/client/tiptap/paste-media.ts +6 -0
  445. package/src/client/tiptap/slash-commands.ts +167 -14
  446. package/src/client/tiptap/tab-indent.ts +184 -0
  447. package/src/client/tiptap/toolbar-mode.ts +15 -2
  448. package/src/client/tiptap/wrapping-input-rules.ts +102 -0
  449. package/src/client/types/sortablejs.d.ts +9 -1
  450. package/src/client/upload-session.ts +2 -1
  451. package/src/client/video-processor.ts +177 -20
  452. package/src/client-auth.ts +9 -1
  453. package/src/client-site.ts +15 -0
  454. package/src/client.ts +2 -0
  455. package/src/db/__tests__/demo-canonical-snapshot.test.ts +1 -1
  456. package/src/db/__tests__/migration-rehearsal.test.ts +5 -1
  457. package/src/db/__tests__/migrations.test.ts +25 -2
  458. package/src/db/backfills/0001_strip_collection_path_c_prefix.sql +11 -0
  459. package/src/db/backfills/0002_strip_collection_path_c_prefix_fix.sql +12 -0
  460. package/src/db/backfills/0003_clear_system_nav_default_labels.sql +15 -0
  461. package/src/db/dialect.ts +40 -0
  462. package/src/db/migrations/0005_busy_the_phantom.sql +39 -0
  463. package/src/db/migrations/0006_adorable_magdalene.sql +3 -0
  464. package/src/db/migrations/0007_unusual_warstar.sql +1 -0
  465. package/src/db/migrations/0008_nasty_lockheed.sql +1 -0
  466. package/src/db/migrations/0009_clear_fixer.sql +31 -0
  467. package/src/db/migrations/0010_futuristic_preak.sql +31 -0
  468. package/src/db/migrations/0011_bizarre_smasher.sql +30 -0
  469. package/src/db/migrations/0012_furry_thena.sql +52 -0
  470. package/src/db/migrations/0013_mixed_lightspeed.sql +39 -0
  471. package/src/db/migrations/0014_high_the_santerians.sql +41 -0
  472. package/src/db/migrations/0015_skinny_shinobi_shaw.sql +1 -0
  473. package/src/db/migrations/0016_remarkable_nicolaos.sql +17 -0
  474. package/src/db/migrations/0017_powerful_moonstone.sql +14 -0
  475. package/src/db/migrations/meta/0005_snapshot.json +1904 -0
  476. package/src/db/migrations/meta/0006_snapshot.json +1925 -0
  477. package/src/db/migrations/meta/0007_snapshot.json +1933 -0
  478. package/src/db/migrations/meta/0008_snapshot.json +1940 -0
  479. package/src/db/migrations/meta/0009_snapshot.json +1952 -0
  480. package/src/db/migrations/meta/0010_snapshot.json +1952 -0
  481. package/src/db/migrations/meta/0011_snapshot.json +1948 -0
  482. package/src/db/migrations/meta/0012_snapshot.json +1955 -0
  483. package/src/db/migrations/meta/0013_snapshot.json +1977 -0
  484. package/src/db/migrations/meta/0014_snapshot.json +1988 -0
  485. package/src/db/migrations/meta/0015_snapshot.json +1995 -0
  486. package/src/db/migrations/meta/0016_snapshot.json +2104 -0
  487. package/src/db/migrations/meta/0017_snapshot.json +2188 -0
  488. package/src/db/migrations/meta/_journal.json +92 -1
  489. package/src/db/migrations/pg/0003_motionless_norrin_radd.sql +21 -0
  490. package/src/db/migrations/pg/0004_nervous_captain_midlands.sql +3 -0
  491. package/src/db/migrations/pg/0005_romantic_mesmero.sql +1 -0
  492. package/src/db/migrations/pg/0006_perpetual_bruce_banner.sql +1 -0
  493. package/src/db/migrations/pg/0007_nav_item_placement.sql +1 -0
  494. package/src/db/migrations/pg/0008_yielding_frightful_four.sql +3 -0
  495. package/src/db/migrations/pg/0009_outstanding_ogun.sql +1 -0
  496. package/src/db/migrations/pg/0010_overjoyed_gertrude_yorkes.sql +28 -0
  497. package/src/db/migrations/pg/0011_fixed_hulk.sql +19 -0
  498. package/src/db/migrations/pg/0012_cute_shockwave.sql +2 -0
  499. package/src/db/migrations/pg/0013_bizarre_obadiah_stane.sql +1 -0
  500. package/src/db/migrations/pg/0014_tearful_grim_reaper.sql +16 -0
  501. package/src/db/migrations/pg/0015_daffy_mikhail_rasputin.sql +14 -0
  502. package/src/db/migrations/pg/meta/0003_snapshot.json +2482 -0
  503. package/src/db/migrations/pg/meta/0004_snapshot.json +2500 -0
  504. package/src/db/migrations/pg/meta/0005_snapshot.json +2507 -0
  505. package/src/db/migrations/pg/meta/0006_snapshot.json +2513 -0
  506. package/src/db/migrations/pg/meta/0008_snapshot.json +2524 -0
  507. package/src/db/migrations/pg/meta/0009_snapshot.json +2520 -0
  508. package/src/db/migrations/pg/meta/0010_snapshot.json +2526 -0
  509. package/src/db/migrations/pg/meta/0011_snapshot.json +2563 -0
  510. package/src/db/migrations/pg/meta/0012_snapshot.json +2573 -0
  511. package/src/db/migrations/pg/meta/0013_snapshot.json +2579 -0
  512. package/src/db/migrations/pg/meta/0014_snapshot.json +2702 -0
  513. package/src/db/migrations/pg/meta/0015_snapshot.json +2803 -0
  514. package/src/db/migrations/pg/meta/_journal.json +92 -1
  515. package/src/db/pg/__tests__/node.test.ts +38 -0
  516. package/src/db/pg/node.ts +149 -0
  517. package/src/db/pg/schema.ts +130 -12
  518. package/src/db/rehearsal-fixtures/demo-current.sql +47 -3
  519. package/src/db/schema.ts +139 -12
  520. package/src/i18n/Trans.tsx +3 -3
  521. package/src/i18n/__tests__/context.test.tsx +9 -6
  522. package/src/i18n/__tests__/detect.test.ts +65 -48
  523. package/src/i18n/__tests__/fallback.test.ts +40 -0
  524. package/src/i18n/__tests__/message-placeholders.test.ts +43 -0
  525. package/src/i18n/__tests__/middleware.test.ts +83 -0
  526. package/src/i18n/context.tsx +9 -44
  527. package/src/i18n/detect.ts +95 -28
  528. package/src/i18n/i18n.ts +41 -17
  529. package/src/i18n/index.ts +7 -5
  530. package/src/i18n/locales/{en.po → public/en.po} +284 -1151
  531. package/src/i18n/locales/public/en.ts +1 -0
  532. package/src/i18n/locales/public/zh-Hans.po +2327 -0
  533. package/src/i18n/locales/public/zh-Hans.ts +1 -0
  534. package/src/i18n/locales/public/zh-Hant.po +2327 -0
  535. package/src/i18n/locales/public/zh-Hant.ts +1 -0
  536. package/src/i18n/locales/settings/en.po +1622 -0
  537. package/src/i18n/locales/settings/en.ts +1 -0
  538. package/src/i18n/locales/settings/zh-Hans.po +1622 -0
  539. package/src/i18n/locales/settings/zh-Hans.ts +1 -0
  540. package/src/i18n/locales/settings/zh-Hant.po +1622 -0
  541. package/src/i18n/locales/settings/zh-Hant.ts +1 -0
  542. package/src/i18n/middleware.ts +37 -24
  543. package/src/index.ts +4 -5
  544. package/src/lib/__tests__/collection-groups.test.ts +17 -0
  545. package/src/lib/__tests__/constants.test.ts +2 -24
  546. package/src/lib/__tests__/csp-builder.test.ts +68 -0
  547. package/src/lib/__tests__/display-text.test.ts +27 -0
  548. package/src/lib/__tests__/embed-providers.test.ts +104 -0
  549. package/src/lib/__tests__/embed-render.test.ts +60 -0
  550. package/src/lib/__tests__/feed.test.ts +125 -63
  551. package/src/lib/__tests__/hosted-signin.test.ts +11 -2
  552. package/src/lib/__tests__/markdown-roundtrip-embed.test.ts +88 -0
  553. package/src/lib/__tests__/markdown-to-tiptap.test.ts +59 -0
  554. package/src/lib/__tests__/markdown.test.ts +47 -5
  555. package/src/lib/__tests__/navigation.test.ts +148 -0
  556. package/src/lib/__tests__/post-display.test.ts +3 -2
  557. package/src/lib/__tests__/post-meta.test.ts +3 -0
  558. package/src/lib/__tests__/resolve-config.test.ts +30 -25
  559. package/src/lib/__tests__/schemas.test.ts +20 -3
  560. package/src/lib/__tests__/summary.test.ts +400 -1
  561. package/src/lib/__tests__/theme.test.ts +41 -2
  562. package/src/lib/__tests__/timeline.test.ts +33 -27
  563. package/src/lib/__tests__/tiptap-render.test.ts +224 -0
  564. package/src/lib/__tests__/tiptap-to-markdown.test.ts +51 -0
  565. package/src/lib/__tests__/video-playback.test.ts +48 -0
  566. package/src/lib/__tests__/view.test.ts +115 -8
  567. package/src/lib/__tests__/worker-response-cache.test.ts +311 -0
  568. package/src/lib/__tests__/youtube.test.ts +99 -0
  569. package/src/lib/api-media.ts +55 -0
  570. package/src/lib/api-posts.ts +99 -0
  571. package/src/lib/api-search.ts +47 -0
  572. package/src/lib/api-settings.ts +122 -0
  573. package/src/lib/asset-path.ts +20 -2
  574. package/src/lib/collection-groups.ts +2 -1
  575. package/src/lib/collection-paths.ts +40 -0
  576. package/src/lib/constants.ts +3 -24
  577. package/src/lib/csp-builder.ts +99 -0
  578. package/src/lib/decorative-quote-mark.ts +11 -0
  579. package/src/lib/display-text.ts +56 -0
  580. package/src/lib/embed-providers.ts +289 -0
  581. package/src/lib/embed-render.ts +151 -0
  582. package/src/lib/env.ts +70 -1
  583. package/src/lib/excerpt.ts +11 -2
  584. package/src/lib/feed.ts +89 -66
  585. package/src/lib/footnotes.ts +146 -0
  586. package/src/lib/github-api.ts +423 -0
  587. package/src/lib/github-app-state.ts +135 -0
  588. package/src/lib/github-app.ts +487 -0
  589. package/src/lib/github-sync-queue-handler.ts +69 -0
  590. package/src/lib/github-sync-site-config.ts +57 -0
  591. package/src/lib/github-sync-status.tsx +87 -0
  592. package/src/lib/github-sync-trigger.ts +199 -0
  593. package/src/lib/github-sync-worker.ts +72 -0
  594. package/src/lib/hosted-signin.ts +1 -1
  595. package/src/lib/hugo-markdown.ts +255 -0
  596. package/src/lib/job-queue-cf.ts +18 -0
  597. package/src/lib/job-queue-db.ts +149 -0
  598. package/src/lib/job-queue.ts +35 -0
  599. package/src/lib/markdown-manager.ts +864 -0
  600. package/src/lib/markdown-to-tiptap.ts +4 -323
  601. package/src/lib/markdown.ts +16 -24
  602. package/src/lib/media-helpers.ts +1 -0
  603. package/src/lib/navigation.ts +40 -8
  604. package/src/lib/post-display.ts +10 -22
  605. package/src/lib/public-storage.ts +6 -1
  606. package/src/lib/render.tsx +5 -2
  607. package/src/lib/resolve-config.ts +23 -11
  608. package/src/lib/rich-image.ts +89 -0
  609. package/src/lib/schemas.ts +180 -13
  610. package/src/lib/summary.ts +125 -18
  611. package/src/lib/theme.ts +119 -0
  612. package/src/lib/timeline.ts +63 -42
  613. package/src/lib/tiptap-render.ts +265 -126
  614. package/src/lib/tiptap-to-markdown.ts +6 -329
  615. package/src/lib/upload.ts +97 -6
  616. package/src/lib/version.ts +18 -0
  617. package/src/lib/video-playback.ts +73 -0
  618. package/src/lib/view.ts +130 -90
  619. package/src/lib/webhook-signature.ts +65 -0
  620. package/src/lib/worker-response-cache.ts +220 -0
  621. package/src/lib/youtube.ts +119 -0
  622. package/src/middleware/__tests__/onboarding.test.ts +3 -3
  623. package/src/middleware/__tests__/secure-headers.test.ts +28 -1
  624. package/src/middleware/config.ts +7 -10
  625. package/src/middleware/cors.ts +49 -0
  626. package/src/middleware/secure-headers.ts +45 -55
  627. package/src/node/__tests__/cli-migrate.test.ts +111 -0
  628. package/src/node/__tests__/cli-runtime-target.test.ts +1 -1
  629. package/src/node/__tests__/cli-site-token-env.test.ts +2 -2
  630. package/src/preset.css +316 -12
  631. package/src/routes/__tests__/compose.test.ts +3 -1
  632. package/src/routes/api/__tests__/collections.test.ts +81 -27
  633. package/src/routes/api/__tests__/mcp.test.ts +389 -0
  634. package/src/routes/api/__tests__/nav-items.test.ts +163 -2
  635. package/src/routes/api/__tests__/posts.test.ts +190 -0
  636. package/src/routes/api/__tests__/settings.test.ts +99 -3
  637. package/src/routes/api/__tests__/upload-multipart.test.ts +10 -2
  638. package/src/routes/api/__tests__/upload.test.ts +132 -0
  639. package/src/routes/api/__tests__/uploads.test.ts +5 -3
  640. package/src/routes/api/collections.ts +67 -26
  641. package/src/routes/api/custom-urls.ts +1 -0
  642. package/src/routes/api/export.ts +8 -4
  643. package/src/routes/api/github-sync.tsx +380 -0
  644. package/src/routes/api/internal/__tests__/sites.test.ts +45 -2
  645. package/src/routes/api/internal/sites.ts +2 -0
  646. package/src/routes/api/internal/text-attachments.ts +60 -0
  647. package/src/routes/api/internal/uploads.ts +5 -1
  648. package/src/routes/api/mcp.ts +30 -0
  649. package/src/routes/api/nav-items.ts +30 -11
  650. package/src/routes/api/palette.ts +17 -0
  651. package/src/routes/api/posts.ts +37 -106
  652. package/src/routes/api/public/__tests__/posts.test.ts +372 -0
  653. package/src/routes/api/public/posts.ts +302 -0
  654. package/src/routes/api/search.ts +1 -47
  655. package/src/routes/api/settings.ts +44 -58
  656. package/src/routes/api/upload-multipart.ts +15 -5
  657. package/src/routes/api/upload.ts +62 -49
  658. package/src/routes/api/uploads.ts +1 -0
  659. package/src/routes/auth/__tests__/hosted-sso.test.ts +33 -0
  660. package/src/routes/auth/__tests__/setup.test.ts +29 -4
  661. package/src/routes/auth/hosted-sso-expired-page.tsx +89 -0
  662. package/src/routes/auth/hosted-sso.ts +42 -1
  663. package/src/routes/auth/reset.tsx +46 -32
  664. package/src/routes/auth/setup.tsx +53 -43
  665. package/src/routes/auth/signin.tsx +34 -24
  666. package/src/routes/compose.tsx +114 -1
  667. package/src/routes/dash/__tests__/font-theme.test.ts +6 -27
  668. package/src/routes/dash/__tests__/settings-avatar.test.ts +2 -3
  669. package/src/routes/dash/custom-urls.tsx +250 -112
  670. package/src/routes/dash/settings.tsx +1179 -70
  671. package/src/routes/feed/__tests__/{rss.test.ts → feed.test.ts} +62 -123
  672. package/src/routes/feed/feed.ts +386 -0
  673. package/src/routes/feed/manifest.ts +67 -0
  674. package/src/routes/feed/sitemap.ts +11 -2
  675. package/src/routes/pages/__tests__/collection-routing.test.ts +176 -0
  676. package/src/routes/pages/__tests__/collections.test.ts +2 -3
  677. package/src/routes/pages/__tests__/featured.test.ts +2 -3
  678. package/src/routes/pages/archive.tsx +523 -127
  679. package/src/routes/pages/collection.tsx +197 -43
  680. package/src/routes/pages/collections.tsx +5 -1
  681. package/src/routes/pages/home.tsx +2 -2
  682. package/src/routes/pages/latest.tsx +3 -3
  683. package/src/routes/pages/page.tsx +221 -3
  684. package/src/routes/pages/partials.tsx +5 -0
  685. package/src/routes/pages/search.tsx +17 -1
  686. package/src/routes/pages/theme-sample.tsx +1 -2
  687. package/src/runtime/__tests__/node.test.ts +1 -1
  688. package/src/runtime/readiness.ts +1 -1
  689. package/src/runtime/site.ts +9 -1
  690. package/src/services/__tests__/collection.test.ts +137 -54
  691. package/src/services/__tests__/github-app-installations.test.ts +181 -0
  692. package/src/services/__tests__/github-sync-classify.test.ts +189 -0
  693. package/src/services/__tests__/github-sync-push.test.ts +159 -0
  694. package/src/services/__tests__/media.test.ts +444 -7
  695. package/src/services/__tests__/navigation.test.ts +271 -14
  696. package/src/services/__tests__/post-timeline.test.ts +31 -21
  697. package/src/services/__tests__/post.test.ts +83 -9
  698. package/src/services/__tests__/settings.test.ts +43 -21
  699. package/src/services/auth.ts +2 -2
  700. package/src/services/bootstrap.ts +10 -7
  701. package/src/services/collection.ts +591 -193
  702. package/src/services/custom-url.ts +34 -7
  703. package/src/services/export-theme/assets/client-site.css +2 -0
  704. package/src/services/export-theme/assets/client-site.js +145 -0
  705. package/src/services/export-theme/layouts/_default/alias.html +49 -0
  706. package/src/services/export-theme/layouts/_default/baseof.html +21 -0
  707. package/src/services/export-theme/layouts/_default/list.html +104 -0
  708. package/src/services/export-theme/layouts/_default/rss.xml +160 -0
  709. package/src/services/export-theme/layouts/_default/single.html +15 -0
  710. package/src/services/export-theme/layouts/archive/list.html +31 -0
  711. package/src/services/export-theme/layouts/collection/single.html +19 -0
  712. package/src/services/export-theme/layouts/collections/list.html +111 -0
  713. package/src/services/export-theme/layouts/featured/list.html +28 -0
  714. package/src/services/export-theme/layouts/index.html +41 -0
  715. package/src/services/export-theme/layouts/partials/feed-post-content.xml +107 -0
  716. package/src/services/export-theme/layouts/partials/footer.html +32 -0
  717. package/src/services/export-theme/layouts/partials/head.html +52 -0
  718. package/src/services/export-theme/layouts/partials/header.html +115 -0
  719. package/src/services/export-theme/layouts/partials/media-gallery.html +246 -0
  720. package/src/services/export-theme/layouts/partials/pagination.html +55 -0
  721. package/src/services/export-theme/layouts/partials/post-card.html +127 -0
  722. package/src/services/export-theme/layouts/partials/reply.html +91 -0
  723. package/src/services/export-theme/layouts/partials/thread-preview.html +82 -0
  724. package/src/services/export-theme/layouts/post/list.html +129 -0
  725. package/src/services/export-theme/styles/main.css +1981 -0
  726. package/src/services/export-theme/theme.toml +12 -0
  727. package/src/services/export.ts +1365 -2412
  728. package/src/services/github-app-installations.ts +302 -0
  729. package/src/services/github-sync.ts +769 -0
  730. package/src/services/index.ts +15 -0
  731. package/src/services/mcp.ts +1324 -0
  732. package/src/services/media.ts +336 -25
  733. package/src/services/navigation.ts +115 -23
  734. package/src/services/path.ts +129 -28
  735. package/src/services/post.ts +658 -195
  736. package/src/services/search.ts +3 -0
  737. package/src/services/settings.ts +29 -15
  738. package/src/services/site-admin.ts +26 -6
  739. package/src/services/upload-session.ts +69 -34
  740. package/src/style-cjk-jp.css +3 -0
  741. package/src/style-cjk-kr.css +3 -0
  742. package/src/styles/components.css +160 -59
  743. package/src/styles/fonts/et-book/et-book-bold-line-figures.woff +0 -0
  744. package/src/styles/fonts/et-book/et-book-display-italic-old-style-figures.woff +0 -0
  745. package/src/styles/fonts/et-book/et-book-roman-line-figures.woff +0 -0
  746. package/src/styles/fonts/et-book.css +31 -0
  747. package/src/styles/fonts/latin.css +1 -0
  748. package/src/styles/fonts/noto-serif-jp/400/057a6a98bda7fe57105ddaa99ec82015.woff2 +0 -0
  749. package/src/styles/fonts/noto-serif-jp/400/0acd1fe2b2ea1ad1bfee7ae1fa139d27.woff2 +0 -0
  750. package/src/styles/fonts/noto-serif-jp/400/0c2ab6b295e55f356f8020d4e7747522.woff2 +0 -0
  751. package/src/styles/fonts/noto-serif-jp/400/0c5f9492af03a4fa42c784de94649de1.woff2 +0 -0
  752. package/src/styles/fonts/noto-serif-jp/400/0d9a936885a4c39077438effd3779cbd.woff2 +0 -0
  753. package/src/styles/fonts/noto-serif-jp/400/0e645da524f7cfc0e8c3c03fb2b08428.woff2 +0 -0
  754. package/src/styles/fonts/noto-serif-jp/400/135e83b403475c5dc9e49b43501a5b84.woff2 +0 -0
  755. package/src/styles/fonts/noto-serif-jp/400/13b2c53b30e6a3e4de7132dbc18dbdfc.woff2 +0 -0
  756. package/src/styles/fonts/noto-serif-jp/400/15d95680dc31cc6ce20e0a5464106dc4.woff2 +0 -0
  757. package/src/styles/fonts/noto-serif-jp/400/1733f27476507ca68b68a803bc533cc2.woff2 +0 -0
  758. package/src/styles/fonts/noto-serif-jp/400/188c2db794f3dd7a45889ddbc81da9bb.woff2 +0 -0
  759. package/src/styles/fonts/noto-serif-jp/400/1cf737900dd49c2e88f1b3221a82a602.woff2 +0 -0
  760. package/src/styles/fonts/noto-serif-jp/400/233d3a685ee18276b319363474599d47.woff2 +0 -0
  761. package/src/styles/fonts/noto-serif-jp/400/284b53bbefb06924cf236d24c8ed5641.woff2 +0 -0
  762. package/src/styles/fonts/noto-serif-jp/400/2a56eaf19d1d38a6b57e2a388f733676.woff2 +0 -0
  763. package/src/styles/fonts/noto-serif-jp/400/2abfbab82b6a7c04426afc054d2464da.woff2 +0 -0
  764. package/src/styles/fonts/noto-serif-jp/400/2e6f4bb71ef6b38765d51acc5a79f638.woff2 +0 -0
  765. package/src/styles/fonts/noto-serif-jp/400/31194d303a67561926a544ed0e072aee.woff2 +0 -0
  766. package/src/styles/fonts/noto-serif-jp/400/3623d96698132c61e861f984ce11cc23.woff2 +0 -0
  767. package/src/styles/fonts/noto-serif-jp/400/39211b02f24c69cbd1b54f6d41b01933.woff2 +0 -0
  768. package/src/styles/fonts/noto-serif-jp/400/3e24063b19abd2b051af986a6870046d.woff2 +0 -0
  769. package/src/styles/fonts/noto-serif-jp/400/4410f876d27c9821839474b6b1059fd7.woff2 +0 -0
  770. package/src/styles/fonts/noto-serif-jp/400/479329a39a3eb6c0047e6b0981919855.woff2 +0 -0
  771. package/src/styles/fonts/noto-serif-jp/400/527ff336279465617aafbafe1a830632.woff2 +0 -0
  772. package/src/styles/fonts/noto-serif-jp/400/56b40518ea0608e62826bd44ee27f5f9.woff2 +0 -0
  773. package/src/styles/fonts/noto-serif-jp/400/5acc7f736217259db79a42bf44241c48.woff2 +0 -0
  774. package/src/styles/fonts/noto-serif-jp/400/5c05f8b75c9d7f25b3c04ca711cf43b3.woff2 +0 -0
  775. package/src/styles/fonts/noto-serif-jp/400/5e0124c7265f1b4cf0fc797b94efbebc.woff2 +0 -0
  776. package/src/styles/fonts/noto-serif-jp/400/615a7e0d63f257109d599b2a1977de68.woff2 +0 -0
  777. package/src/styles/fonts/noto-serif-jp/400/61a2c80d0c924d5a6187c02e8d1d1642.woff2 +0 -0
  778. package/src/styles/fonts/noto-serif-jp/400/62fc94f8d4c5a750b7f25e7387539910.woff2 +0 -0
  779. package/src/styles/fonts/noto-serif-jp/400/639c7c6b0b0c27c738702741cfa4b8c0.woff2 +0 -0
  780. package/src/styles/fonts/noto-serif-jp/400/68a3a4bf337f8a0722be76676e20b850.woff2 +0 -0
  781. package/src/styles/fonts/noto-serif-jp/400/6abdd163d2c4b85698db8aa1ce149361.woff2 +0 -0
  782. package/src/styles/fonts/noto-serif-jp/400/73b794d885b88f7befb7ea8ab1780a15.woff2 +0 -0
  783. package/src/styles/fonts/noto-serif-jp/400/743f290baf027d4626a86e22f3d44600.woff2 +0 -0
  784. package/src/styles/fonts/noto-serif-jp/400/757019c01c5e2c2b0f54293cea3b5636.woff2 +0 -0
  785. package/src/styles/fonts/noto-serif-jp/400/76f117a3baacda304a71965a17266a13.woff2 +0 -0
  786. package/src/styles/fonts/noto-serif-jp/400/77747f17625a259fb405b7bbdd84ac4b.woff2 +0 -0
  787. package/src/styles/fonts/noto-serif-jp/400/788498548ddfb710d6ef3c7bff79fa97.woff2 +0 -0
  788. package/src/styles/fonts/noto-serif-jp/400/78ea6c40923ce95367e3517dd1e5a849.woff2 +0 -0
  789. package/src/styles/fonts/noto-serif-jp/400/7f9a6c9286b68de9c72b0024f7beeb40.woff2 +0 -0
  790. package/src/styles/fonts/noto-serif-jp/400/823d1bb11097331238d103c7f72138dc.woff2 +0 -0
  791. package/src/styles/fonts/noto-serif-jp/400/888ba53510213d5d1f6a7deb60e569b6.woff2 +0 -0
  792. package/src/styles/fonts/noto-serif-jp/400/892aa49b2529c89bb4076d4aa51fe30f.woff2 +0 -0
  793. package/src/styles/fonts/noto-serif-jp/400/95ff41191c76ef893ac70a1043e95e44.woff2 +0 -0
  794. package/src/styles/fonts/noto-serif-jp/400/96c75862c8cec51a7c05ff025fea86cd.woff2 +0 -0
  795. package/src/styles/fonts/noto-serif-jp/400/9b21e8244f5930c48ad5073f83777b6c.woff2 +0 -0
  796. package/src/styles/fonts/noto-serif-jp/400/9fce02d1a09f464c36c9fcfb14a354c5.woff2 +0 -0
  797. package/src/styles/fonts/noto-serif-jp/400/a0a6755c7e3f9e4cf03387027dd9f16c.woff2 +0 -0
  798. package/src/styles/fonts/noto-serif-jp/400/a4475c442c6b259354c9eda62e64e7df.woff2 +0 -0
  799. package/src/styles/fonts/noto-serif-jp/400/a4a39b5f048671aa37c2b2a9581ab08a.woff2 +0 -0
  800. package/src/styles/fonts/noto-serif-jp/400/a534449d67561ac9b06489f64e57ad98.woff2 +0 -0
  801. package/src/styles/fonts/noto-serif-jp/400/a919a8c6fec6d7e8bb21177965f2e9d7.woff2 +0 -0
  802. package/src/styles/fonts/noto-serif-jp/400/ad67113f88b59582991cb0c5d33ea19f.woff2 +0 -0
  803. package/src/styles/fonts/noto-serif-jp/400/adb6e613fb99c6dd660b727101f554a8.woff2 +0 -0
  804. package/src/styles/fonts/noto-serif-jp/400/adc4a98a89870ef984ee8f4b57c8a3a5.woff2 +0 -0
  805. package/src/styles/fonts/noto-serif-jp/400/b12379ab782468d725519cd07a7d15bc.woff2 +0 -0
  806. package/src/styles/fonts/noto-serif-jp/400/b55486f8a459c838fe329d4e79a8c211.woff2 +0 -0
  807. package/src/styles/fonts/noto-serif-jp/400/bb0b15492b8cdbbec57c0bcfa0aa9241.woff2 +0 -0
  808. package/src/styles/fonts/noto-serif-jp/400/bedc74b423b7293b6ad0bdecc61c42cc.woff2 +0 -0
  809. package/src/styles/fonts/noto-serif-jp/400/bfb00d4a4c48661bd0be99f300a0faae.woff2 +0 -0
  810. package/src/styles/fonts/noto-serif-jp/400/c1b0df29ae41d764904df84e9ac83d1e.woff2 +0 -0
  811. package/src/styles/fonts/noto-serif-jp/400/c42c67070cfe99cf823df92d81c7fa6e.woff2 +0 -0
  812. package/src/styles/fonts/noto-serif-jp/400/c4d749e45ecd5a5aed5a0bb3ebfd355d.woff2 +0 -0
  813. package/src/styles/fonts/noto-serif-jp/400/c96d83978add28b356c22c4c84916733.woff2 +0 -0
  814. package/src/styles/fonts/noto-serif-jp/400/cb7ccd6494256f7a2977b7c2b0225592.woff2 +0 -0
  815. package/src/styles/fonts/noto-serif-jp/400/ce41d70ce6a069a498525c9e15c45cf2.woff2 +0 -0
  816. package/src/styles/fonts/noto-serif-jp/400/d509ee5c8241bc7c3de2039d75564fa5.woff2 +0 -0
  817. package/src/styles/fonts/noto-serif-jp/400/d675d717cd329bfd4c0524f76ae1579c.woff2 +0 -0
  818. package/src/styles/fonts/noto-serif-jp/400/dc8f6256445e68199540be9ade33529f.woff2 +0 -0
  819. package/src/styles/fonts/noto-serif-jp/400/e8c15be62faf978d208925e79ea6a10d.woff2 +0 -0
  820. package/src/styles/fonts/noto-serif-jp/400/e91c4d941ed9f5c2f3e27f205a3a225e.woff2 +0 -0
  821. package/src/styles/fonts/noto-serif-jp/400/e9c566be7a5d38a9085225f7372bc82b.woff2 +0 -0
  822. package/src/styles/fonts/noto-serif-jp/400/e9ebc567a711eeb29019ddae3e0ce7fe.woff2 +0 -0
  823. package/src/styles/fonts/noto-serif-jp/400/ecb8875a56c7b038b35432fda41ae128.woff2 +0 -0
  824. package/src/styles/fonts/noto-serif-jp/400/f1661731474b78bdf81114daf10b254f.woff2 +0 -0
  825. package/src/styles/fonts/noto-serif-jp/400/f2c58dee206ba9355046fc23d05491f7.woff2 +0 -0
  826. package/src/styles/fonts/noto-serif-jp/400/f50ac27ea4358d67fdda403c2bb52467.woff2 +0 -0
  827. package/src/styles/fonts/noto-serif-jp/400/f55334112f8e8be82d65db29887a663f.woff2 +0 -0
  828. package/src/styles/fonts/noto-serif-jp/400/f740e93f3c0277ecc616594103bca683.woff2 +0 -0
  829. package/src/styles/fonts/noto-serif-jp/400/f7633b5af033d76ff2fb3c3c266d77c5.woff2 +0 -0
  830. package/src/styles/fonts/noto-serif-jp/400/f7d8468cba2335a83ee414ea68291bab.woff2 +0 -0
  831. package/src/styles/fonts/noto-serif-jp/400/fc9ae5b600fb711f2d67e93ce768cba4.woff2 +0 -0
  832. package/src/styles/fonts/noto-serif-jp/400/fd1bceb55d3e0183ac2454b8532fec7d.woff2 +0 -0
  833. package/src/styles/fonts/noto-serif-jp/700/02611a045a7fe83a12014e3debc9f731.woff2 +0 -0
  834. package/src/styles/fonts/noto-serif-jp/700/0f5e1a18987dbc84ca05188c129e1936.woff2 +0 -0
  835. package/src/styles/fonts/noto-serif-jp/700/112743a4ab5fdd1498dfdf2b11336380.woff2 +0 -0
  836. package/src/styles/fonts/noto-serif-jp/700/1211f03d3ff5759a702631445793f60e.woff2 +0 -0
  837. package/src/styles/fonts/noto-serif-jp/700/147b24c2f08d03bbed30887d4cb3311c.woff2 +0 -0
  838. package/src/styles/fonts/noto-serif-jp/700/16663e567f81f4725a1522f37e18f71f.woff2 +0 -0
  839. package/src/styles/fonts/noto-serif-jp/700/1824321da801e6257b902f3d1d09054c.woff2 +0 -0
  840. package/src/styles/fonts/noto-serif-jp/700/1a3a92c7c060a71a6c35819b9ebcdbb9.woff2 +0 -0
  841. package/src/styles/fonts/noto-serif-jp/700/1fba7ec0e412e911bf31841de5a8a4e4.woff2 +0 -0
  842. package/src/styles/fonts/noto-serif-jp/700/28a97af9ab9d38983d20f39ff09840e1.woff2 +0 -0
  843. package/src/styles/fonts/noto-serif-jp/700/2bfaadaf3479c72286248e6de0be0ec9.woff2 +0 -0
  844. package/src/styles/fonts/noto-serif-jp/700/2c8c55e4cec85ce77e95cac9d330f5cf.woff2 +0 -0
  845. package/src/styles/fonts/noto-serif-jp/700/2ed9981d2e8983365bd051159b976b6d.woff2 +0 -0
  846. package/src/styles/fonts/noto-serif-jp/700/2f4c633e923ba30c6ba376367379fc91.woff2 +0 -0
  847. package/src/styles/fonts/noto-serif-jp/700/349965eee0a9b6c984a319ab96a4ece9.woff2 +0 -0
  848. package/src/styles/fonts/noto-serif-jp/700/36cda3eae13370b934bba4429fab9078.woff2 +0 -0
  849. package/src/styles/fonts/noto-serif-jp/700/3aa13f1843d7c8fa16109b6395a9a29d.woff2 +0 -0
  850. package/src/styles/fonts/noto-serif-jp/700/3e2c06bdd9dcab29aeaeb0cfb7fa608a.woff2 +0 -0
  851. package/src/styles/fonts/noto-serif-jp/700/424123f9371ba357087363263eeafcda.woff2 +0 -0
  852. package/src/styles/fonts/noto-serif-jp/700/43b3bbf43055c3e65db9cdf2c5b18c1c.woff2 +0 -0
  853. package/src/styles/fonts/noto-serif-jp/700/44704471b60c18b3ecec5300a88f1102.woff2 +0 -0
  854. package/src/styles/fonts/noto-serif-jp/700/46604090efbb99c2db1800928af0a239.woff2 +0 -0
  855. package/src/styles/fonts/noto-serif-jp/700/46998df85d31e629c0142f0556f5e7c5.woff2 +0 -0
  856. package/src/styles/fonts/noto-serif-jp/700/49902f1c41b281f8eb3771abdd4dccb8.woff2 +0 -0
  857. package/src/styles/fonts/noto-serif-jp/700/4db4f31a16965baa42cb7dad667a8f04.woff2 +0 -0
  858. package/src/styles/fonts/noto-serif-jp/700/572b9e28f1e6d89b765a16d809db84a1.woff2 +0 -0
  859. package/src/styles/fonts/noto-serif-jp/700/5921cef36750ff6092d94b83a802823b.woff2 +0 -0
  860. package/src/styles/fonts/noto-serif-jp/700/59e95d8b5332dcdae894a19946ab767d.woff2 +0 -0
  861. package/src/styles/fonts/noto-serif-jp/700/5ec349ff62b9ee5fe0c6dae867704dc4.woff2 +0 -0
  862. package/src/styles/fonts/noto-serif-jp/700/601f812e8b1eafecd5ba585a6a6d7962.woff2 +0 -0
  863. package/src/styles/fonts/noto-serif-jp/700/6025ddaf99d86cc8bfd781006b19fbd6.woff2 +0 -0
  864. package/src/styles/fonts/noto-serif-jp/700/60f6e28b3f0400b0e8fe32c3c7116102.woff2 +0 -0
  865. package/src/styles/fonts/noto-serif-jp/700/64a404d675f1d726f0891b7a80794595.woff2 +0 -0
  866. package/src/styles/fonts/noto-serif-jp/700/7158022889c6c177062ac85036e7af10.woff2 +0 -0
  867. package/src/styles/fonts/noto-serif-jp/700/7e38ca789a9e76ac91d9256374e154f0.woff2 +0 -0
  868. package/src/styles/fonts/noto-serif-jp/700/815a0b797465190f60d8b1a04656c42e.woff2 +0 -0
  869. package/src/styles/fonts/noto-serif-jp/700/8726558cf297cbda831cc19514897205.woff2 +0 -0
  870. package/src/styles/fonts/noto-serif-jp/700/8855472f3c02f6c7ebb3216617ebe4af.woff2 +0 -0
  871. package/src/styles/fonts/noto-serif-jp/700/8e231d707f0c4f8e3cde90a6b52a79aa.woff2 +0 -0
  872. package/src/styles/fonts/noto-serif-jp/700/9064c12fd72c7aba235d8c3881755b91.woff2 +0 -0
  873. package/src/styles/fonts/noto-serif-jp/700/911b9e53e9814de2998c60bf3115f560.woff2 +0 -0
  874. package/src/styles/fonts/noto-serif-jp/700/92fdc376bce277874e75db666f175b44.woff2 +0 -0
  875. package/src/styles/fonts/noto-serif-jp/700/930a23430f0eb64480d7fe5f82834e21.woff2 +0 -0
  876. package/src/styles/fonts/noto-serif-jp/700/93bed0e5f2dd5a21cf73304fcfbc149d.woff2 +0 -0
  877. package/src/styles/fonts/noto-serif-jp/700/93f3ea6918533d96d9c252378b9a4af0.woff2 +0 -0
  878. package/src/styles/fonts/noto-serif-jp/700/95a0951d2a2722ff773a1a45e8c474d6.woff2 +0 -0
  879. package/src/styles/fonts/noto-serif-jp/700/982cd1765ca6bbb3e6547e47834d5148.woff2 +0 -0
  880. package/src/styles/fonts/noto-serif-jp/700/9b0c3caac458c7bc9c9133e40405ddea.woff2 +0 -0
  881. package/src/styles/fonts/noto-serif-jp/700/9f3bdab4025dc7c5ba1a5871e62e8918.woff2 +0 -0
  882. package/src/styles/fonts/noto-serif-jp/700/a00e94d4f04df6aa659db9c4954c7efe.woff2 +0 -0
  883. package/src/styles/fonts/noto-serif-jp/700/a2b0597837e382aca19919c4b74e58c8.woff2 +0 -0
  884. package/src/styles/fonts/noto-serif-jp/700/a373e85c96aa0dc303092429fb837e51.woff2 +0 -0
  885. package/src/styles/fonts/noto-serif-jp/700/a6e044424780a57610833cc856c15bf5.woff2 +0 -0
  886. package/src/styles/fonts/noto-serif-jp/700/a7c58070d68dc1724e9d4e781afb78db.woff2 +0 -0
  887. package/src/styles/fonts/noto-serif-jp/700/accc43762ad05edfbff66ba1380d192a.woff2 +0 -0
  888. package/src/styles/fonts/noto-serif-jp/700/ad47bcd6d4663026d648c132d969318d.woff2 +0 -0
  889. package/src/styles/fonts/noto-serif-jp/700/af57a2e8c72f9ba0efb1af771d32c124.woff2 +0 -0
  890. package/src/styles/fonts/noto-serif-jp/700/b0ae5f374e43dac992a4a5d334cebc0b.woff2 +0 -0
  891. package/src/styles/fonts/noto-serif-jp/700/b91234c10fd8b8c8abc88e03afe66a1f.woff2 +0 -0
  892. package/src/styles/fonts/noto-serif-jp/700/b9317198f118a1dfd8ddf2b82ec028f3.woff2 +0 -0
  893. package/src/styles/fonts/noto-serif-jp/700/ba825ae79b1a6f7e0cce5215fcb5c96f.woff2 +0 -0
  894. package/src/styles/fonts/noto-serif-jp/700/bc6183ac08f0fac78c46f80c10cf7c92.woff2 +0 -0
  895. package/src/styles/fonts/noto-serif-jp/700/bc8ccea5abf6598cf3cfa97eb59804bb.woff2 +0 -0
  896. package/src/styles/fonts/noto-serif-jp/700/bc95a792cbca6639214c9b0da13392ff.woff2 +0 -0
  897. package/src/styles/fonts/noto-serif-jp/700/bcfa62f35731856246c146d3a6932bf3.woff2 +0 -0
  898. package/src/styles/fonts/noto-serif-jp/700/c063897793f593eb26d6ff7b7baaba18.woff2 +0 -0
  899. package/src/styles/fonts/noto-serif-jp/700/c6c2971ad1f3221f6cf84028aa0f477e.woff2 +0 -0
  900. package/src/styles/fonts/noto-serif-jp/700/c97f41eef722121d86f55d553c056a39.woff2 +0 -0
  901. package/src/styles/fonts/noto-serif-jp/700/ca62704509932d3232d62918de97af3f.woff2 +0 -0
  902. package/src/styles/fonts/noto-serif-jp/700/d1f064825fa5784b5c930652bd831cce.woff2 +0 -0
  903. package/src/styles/fonts/noto-serif-jp/700/d2faabcedd19f016e7b21bce073c0ec8.woff2 +0 -0
  904. package/src/styles/fonts/noto-serif-jp/700/d320171e57480510f87dbbc7d5264b0a.woff2 +0 -0
  905. package/src/styles/fonts/noto-serif-jp/700/d3a72a99d365dddfbca8d017a8011368.woff2 +0 -0
  906. package/src/styles/fonts/noto-serif-jp/700/d42aafa0f246ad3b4c16fe96d3a3a432.woff2 +0 -0
  907. package/src/styles/fonts/noto-serif-jp/700/d4aaf23c13ae808a4bed617afd13aa07.woff2 +0 -0
  908. package/src/styles/fonts/noto-serif-jp/700/d6bb686bddfbe8f38a36a68e609a8667.woff2 +0 -0
  909. package/src/styles/fonts/noto-serif-jp/700/d9020ff69a83b2a6ee1f42ae480f7db0.woff2 +0 -0
  910. package/src/styles/fonts/noto-serif-jp/700/da90a9012ab2d98f759e3fa0820ef502.woff2 +0 -0
  911. package/src/styles/fonts/noto-serif-jp/700/df10d94bea357a43313e20da5d84bd30.woff2 +0 -0
  912. package/src/styles/fonts/noto-serif-jp/700/e34b2b141e472dc776c86fdf8eea23b0.woff2 +0 -0
  913. package/src/styles/fonts/noto-serif-jp/700/e444fd88f1390636e603d0d681538ac8.woff2 +0 -0
  914. package/src/styles/fonts/noto-serif-jp/700/e79898628283edc27180fc39d0d769c1.woff2 +0 -0
  915. package/src/styles/fonts/noto-serif-jp/700/eaf332445a40942928e5f5750c1c3116.woff2 +0 -0
  916. package/src/styles/fonts/noto-serif-jp/700/f91cd722855f4269256eae1187df64ec.woff2 +0 -0
  917. package/src/styles/fonts/noto-serif-jp/700/fc895f5ce66b656f4a933097bf2a8775.woff2 +0 -0
  918. package/src/styles/fonts/noto-serif-jp/700/fca6720fd14c467d29a90f18ef3859b9.woff2 +0 -0
  919. package/src/styles/fonts/noto-serif-jp/700/fd1bb507bcbf04856eeb7f7fd47ea579.woff2 +0 -0
  920. package/src/styles/fonts/noto-serif-jp/noto-serif-jp.css +3349 -0
  921. package/src/styles/fonts/noto-serif-kr/400/033466ef683afe931f7f520cfb42d928.woff2 +0 -0
  922. package/src/styles/fonts/noto-serif-kr/400/05da12edb9d52210581dc6ec4541031f.woff2 +0 -0
  923. package/src/styles/fonts/noto-serif-kr/400/0968e4861204b51f62a2f8e9f15dd5e0.woff2 +0 -0
  924. package/src/styles/fonts/noto-serif-kr/400/0d5dec931dc885f07fe5cd5af8bed675.woff2 +0 -0
  925. package/src/styles/fonts/noto-serif-kr/400/1076f0f6f66d28d7a2f16427faad4413.woff2 +0 -0
  926. package/src/styles/fonts/noto-serif-kr/400/12ef1ba76bd20b004b865266a1aa70b3.woff2 +0 -0
  927. package/src/styles/fonts/noto-serif-kr/400/152395634a207579552f8cb54db88599.woff2 +0 -0
  928. package/src/styles/fonts/noto-serif-kr/400/1a08931435f885e707edb85978ea3bb6.woff2 +0 -0
  929. package/src/styles/fonts/noto-serif-kr/400/31a197213ae61d7043c81013f52d10d6.woff2 +0 -0
  930. package/src/styles/fonts/noto-serif-kr/400/37dbd564820cce2f7b3331e442da0df3.woff2 +0 -0
  931. package/src/styles/fonts/noto-serif-kr/400/37dc8505b20b37a2477a9121b16d44a7.woff2 +0 -0
  932. package/src/styles/fonts/noto-serif-kr/400/3ac0a07878cacdc98bb889f40f5970d3.woff2 +0 -0
  933. package/src/styles/fonts/noto-serif-kr/400/491bbba374093fff045f55803a22bfef.woff2 +0 -0
  934. package/src/styles/fonts/noto-serif-kr/400/4d38e56738156625329d93bd5c8cc74a.woff2 +0 -0
  935. package/src/styles/fonts/noto-serif-kr/400/57e9a86651366c1ba299e47aa7e358c2.woff2 +0 -0
  936. package/src/styles/fonts/noto-serif-kr/400/57f89adeae55aa7fe2add3fc1801ce03.woff2 +0 -0
  937. package/src/styles/fonts/noto-serif-kr/400/5df9ce98c75f8c50fb4fff7f01a23925.woff2 +0 -0
  938. package/src/styles/fonts/noto-serif-kr/400/60020a830d809c26970cf8e48ba1eeda.woff2 +0 -0
  939. package/src/styles/fonts/noto-serif-kr/400/60bcfa3ea7446eb42394aab02d28be40.woff2 +0 -0
  940. package/src/styles/fonts/noto-serif-kr/400/63fafcb069520613d0ea35ad3e6b1e42.woff2 +0 -0
  941. package/src/styles/fonts/noto-serif-kr/400/65a60d87c64228d258a123cbe85f5f31.woff2 +0 -0
  942. package/src/styles/fonts/noto-serif-kr/400/6968e889e18891d912809fe484c2e745.woff2 +0 -0
  943. package/src/styles/fonts/noto-serif-kr/400/70714891ad3fbfc3d5f10a8669dacc5a.woff2 +0 -0
  944. package/src/styles/fonts/noto-serif-kr/400/761d84afca8d7f34eebefe538adba827.woff2 +0 -0
  945. package/src/styles/fonts/noto-serif-kr/400/76e07530323418ec723c5d7634c9bcca.woff2 +0 -0
  946. package/src/styles/fonts/noto-serif-kr/400/773819b7b9b8fd404a929867c0fd677e.woff2 +0 -0
  947. package/src/styles/fonts/noto-serif-kr/400/7817dd16805145d8538ad57590f69f5a.woff2 +0 -0
  948. package/src/styles/fonts/noto-serif-kr/400/7b59f0ec7792b18458dc5a361e37884c.woff2 +0 -0
  949. package/src/styles/fonts/noto-serif-kr/400/7c3945788a689a69356c1a622d69d48b.woff2 +0 -0
  950. package/src/styles/fonts/noto-serif-kr/400/835b6505bb9eea9678925a1fa885353d.woff2 +0 -0
  951. package/src/styles/fonts/noto-serif-kr/400/8b5c9ef81159f31d2ab35f45ca4373e0.woff2 +0 -0
  952. package/src/styles/fonts/noto-serif-kr/400/8b91c7c2ed390f1278b9befa3aa87233.woff2 +0 -0
  953. package/src/styles/fonts/noto-serif-kr/400/8fb45117a62d92dce44a80f0b729ead5.woff2 +0 -0
  954. package/src/styles/fonts/noto-serif-kr/400/90b7848e9b1623b77bdcf155e90b839c.woff2 +0 -0
  955. package/src/styles/fonts/noto-serif-kr/400/985d4d41afa0934a4eb2de35473fb9a2.woff2 +0 -0
  956. package/src/styles/fonts/noto-serif-kr/400/9bb38fd05201de666b88b82d56a386f7.woff2 +0 -0
  957. package/src/styles/fonts/noto-serif-kr/400/9eff2ab2a9d2ced47ab761bf6fdb11ec.woff2 +0 -0
  958. package/src/styles/fonts/noto-serif-kr/400/9f1963fe8d4d6878d717d872a8f3fdb5.woff2 +0 -0
  959. package/src/styles/fonts/noto-serif-kr/400/a0d00fe4816c95a8c7dffd445ef00b03.woff2 +0 -0
  960. package/src/styles/fonts/noto-serif-kr/400/a0f199077fa1a33bc2a1e01c64de7fe6.woff2 +0 -0
  961. package/src/styles/fonts/noto-serif-kr/400/a7c35e42a659347e490c3cb7983b2e7f.woff2 +0 -0
  962. package/src/styles/fonts/noto-serif-kr/400/aa662092cf249707123ca4d575e2764b.woff2 +0 -0
  963. package/src/styles/fonts/noto-serif-kr/400/aae1eda193285ab817a2d1b408440326.woff2 +0 -0
  964. package/src/styles/fonts/noto-serif-kr/400/b0cb664cb2e1371efda8943c0b7dcd1c.woff2 +0 -0
  965. package/src/styles/fonts/noto-serif-kr/400/b0d1cdced482352cf0d3ae58638aacb9.woff2 +0 -0
  966. package/src/styles/fonts/noto-serif-kr/400/b320f5d185b2cff933ac549c184031c5.woff2 +0 -0
  967. package/src/styles/fonts/noto-serif-kr/400/ba9c59d7dfa4494db1bb764ada81467d.woff2 +0 -0
  968. package/src/styles/fonts/noto-serif-kr/400/bab547459d514f46206e340c4bb2dc88.woff2 +0 -0
  969. package/src/styles/fonts/noto-serif-kr/400/bcc39eda837bb7a7a3d37c8c60fffb81.woff2 +0 -0
  970. package/src/styles/fonts/noto-serif-kr/400/bd73264d7f98776708d5d6f3c9b78fcc.woff2 +0 -0
  971. package/src/styles/fonts/noto-serif-kr/400/bed610b217d500f5975cfc9fe6157570.woff2 +0 -0
  972. package/src/styles/fonts/noto-serif-kr/400/bf8901f8f11d4f433ea17dabc9370ea6.woff2 +0 -0
  973. package/src/styles/fonts/noto-serif-kr/400/c4143bb9f2fe77b6ccf20088a8904650.woff2 +0 -0
  974. package/src/styles/fonts/noto-serif-kr/400/c7ff3f6bbdcd5f604b7343602ab904df.woff2 +0 -0
  975. package/src/styles/fonts/noto-serif-kr/400/c945c62368357d05a53206620460fb30.woff2 +0 -0
  976. package/src/styles/fonts/noto-serif-kr/400/ce022e18a1377ac509443c3c3790b431.woff2 +0 -0
  977. package/src/styles/fonts/noto-serif-kr/400/cf9cffe56636322f62b40d61130fbc5e.woff2 +0 -0
  978. package/src/styles/fonts/noto-serif-kr/400/d20d8944bc0b85f5b2aae4b24f343516.woff2 +0 -0
  979. package/src/styles/fonts/noto-serif-kr/400/d36644d6502527e1fff205d0c7eca434.woff2 +0 -0
  980. package/src/styles/fonts/noto-serif-kr/400/d40ab99c7a38026f411c8f112f742b48.woff2 +0 -0
  981. package/src/styles/fonts/noto-serif-kr/400/dc6e234ded795e91f76d6647f628fbf0.woff2 +0 -0
  982. package/src/styles/fonts/noto-serif-kr/400/dd0cfdfdac0866e66d587e2b5a9e9961.woff2 +0 -0
  983. package/src/styles/fonts/noto-serif-kr/400/de304c8a02e45ded4f8dcc479d167198.woff2 +0 -0
  984. package/src/styles/fonts/noto-serif-kr/400/e8c364c16daa04835bf32d293d2598db.woff2 +0 -0
  985. package/src/styles/fonts/noto-serif-kr/400/ec86e23683052da5cfdc3b77641bd15a.woff2 +0 -0
  986. package/src/styles/fonts/noto-serif-kr/400/eda1b0cb6d1719dd9bedcf3216a9e8de.woff2 +0 -0
  987. package/src/styles/fonts/noto-serif-kr/400/ee6fce20b420a480714607c66d7f97e5.woff2 +0 -0
  988. package/src/styles/fonts/noto-serif-kr/400/eee3836d6ac17ebb2c450bbcbc9db121.woff2 +0 -0
  989. package/src/styles/fonts/noto-serif-kr/400/f5738255e92d8dd34a46d1bcdf4c4074.woff2 +0 -0
  990. package/src/styles/fonts/noto-serif-kr/400/fa6e58ce4b52695e7ae19bbea6336ec8.woff2 +0 -0
  991. package/src/styles/fonts/noto-serif-kr/400/fd2069e6e8588c70b4f11364093b81f2.woff2 +0 -0
  992. package/src/styles/fonts/noto-serif-kr/700/04b8bde59cff68eeee74fb1914eb09c7.woff2 +0 -0
  993. package/src/styles/fonts/noto-serif-kr/700/05ac821472e235943ed1435e4bb8ecad.woff2 +0 -0
  994. package/src/styles/fonts/noto-serif-kr/700/074c1f8483d5a4d8c45c8c5f3e4cbb32.woff2 +0 -0
  995. package/src/styles/fonts/noto-serif-kr/700/1380210a722ac9aeef54005ad7b015c9.woff2 +0 -0
  996. package/src/styles/fonts/noto-serif-kr/700/17c7f5d0a45e92ede0e5dec3a2c77efa.woff2 +0 -0
  997. package/src/styles/fonts/noto-serif-kr/700/1be602ad456b0d75902d352116cb35fd.woff2 +0 -0
  998. package/src/styles/fonts/noto-serif-kr/700/2612c60f9dc5459ac42763591240a1d6.woff2 +0 -0
  999. package/src/styles/fonts/noto-serif-kr/700/2ab8cf1f23a5ac7939a7876054e711a7.woff2 +0 -0
  1000. package/src/styles/fonts/noto-serif-kr/700/34e6750e00c3a911ef87dc66b336594d.woff2 +0 -0
  1001. package/src/styles/fonts/noto-serif-kr/700/36f3df4730cfca4fc77fe52b52e6a126.woff2 +0 -0
  1002. package/src/styles/fonts/noto-serif-kr/700/37887deb7a9c466cf2af6ee7ff372ea6.woff2 +0 -0
  1003. package/src/styles/fonts/noto-serif-kr/700/3aba89c4d4281553078de4622b498cbb.woff2 +0 -0
  1004. package/src/styles/fonts/noto-serif-kr/700/3d88558097559d775231194d43745ba7.woff2 +0 -0
  1005. package/src/styles/fonts/noto-serif-kr/700/3dc3e8c35524cad2d1236f3393104ccf.woff2 +0 -0
  1006. package/src/styles/fonts/noto-serif-kr/700/45140742dfa8686b58db2899ce89f89a.woff2 +0 -0
  1007. package/src/styles/fonts/noto-serif-kr/700/469d2754e315e77270c12fa0ab28026c.woff2 +0 -0
  1008. package/src/styles/fonts/noto-serif-kr/700/4c0760284569b87d8f2290c324953973.woff2 +0 -0
  1009. package/src/styles/fonts/noto-serif-kr/700/4cba92c942694bdc479bc54101bb1aa4.woff2 +0 -0
  1010. package/src/styles/fonts/noto-serif-kr/700/4e7d263c30bb48604069a3b4404d4eeb.woff2 +0 -0
  1011. package/src/styles/fonts/noto-serif-kr/700/5279c7e465d9871edf51fd02e691eee7.woff2 +0 -0
  1012. package/src/styles/fonts/noto-serif-kr/700/54bfdb6f21f9d6191e5100580239e14f.woff2 +0 -0
  1013. package/src/styles/fonts/noto-serif-kr/700/56be5e32fd01668fe5ba814d94905839.woff2 +0 -0
  1014. package/src/styles/fonts/noto-serif-kr/700/615b38e0b5bb3ea68426f44f47706e6f.woff2 +0 -0
  1015. package/src/styles/fonts/noto-serif-kr/700/65e9c4585d71bf48a5c62f367010da16.woff2 +0 -0
  1016. package/src/styles/fonts/noto-serif-kr/700/6b75213eb0be40ce84241eb2bb438a2e.woff2 +0 -0
  1017. package/src/styles/fonts/noto-serif-kr/700/7520ca6ca8c60eb1e62d50e71c8d30f1.woff2 +0 -0
  1018. package/src/styles/fonts/noto-serif-kr/700/759a647b77791b1e98f99bc0ab5317a7.woff2 +0 -0
  1019. package/src/styles/fonts/noto-serif-kr/700/76e51630143b95b6322ef93ad330da7a.woff2 +0 -0
  1020. package/src/styles/fonts/noto-serif-kr/700/79aa7c8c842c4a27cf57c0a3a1ffca5a.woff2 +0 -0
  1021. package/src/styles/fonts/noto-serif-kr/700/7cc72fd2c9105560422b6a67c6162945.woff2 +0 -0
  1022. package/src/styles/fonts/noto-serif-kr/700/7d0ea0690e432462f4d05a23d720ec19.woff2 +0 -0
  1023. package/src/styles/fonts/noto-serif-kr/700/7dffa5c1bec57e0903fd62357401ff1a.woff2 +0 -0
  1024. package/src/styles/fonts/noto-serif-kr/700/82f6ccc063960eed1cdfd1d61ada8862.woff2 +0 -0
  1025. package/src/styles/fonts/noto-serif-kr/700/845b4b67564d62cf5cad242f7ac08ea5.woff2 +0 -0
  1026. package/src/styles/fonts/noto-serif-kr/700/84c8e7bc0931008ed91f44ed12dfea94.woff2 +0 -0
  1027. package/src/styles/fonts/noto-serif-kr/700/887a11290ba78b1e66c6d2f67043005e.woff2 +0 -0
  1028. package/src/styles/fonts/noto-serif-kr/700/88db1d042074fb6e66821ffc10941930.woff2 +0 -0
  1029. package/src/styles/fonts/noto-serif-kr/700/8dc18c0cebe6aa4bf4c45dbb831048ab.woff2 +0 -0
  1030. package/src/styles/fonts/noto-serif-kr/700/919a879bd2d580d8491a31a449390689.woff2 +0 -0
  1031. package/src/styles/fonts/noto-serif-kr/700/91da6cb174bebfb96976e2f1316be2fe.woff2 +0 -0
  1032. package/src/styles/fonts/noto-serif-kr/700/934dfdbed2bb2c4b6129199d699a34fa.woff2 +0 -0
  1033. package/src/styles/fonts/noto-serif-kr/700/97d7a17234f2b5030ae9697ae00aded2.woff2 +0 -0
  1034. package/src/styles/fonts/noto-serif-kr/700/995c4fda62d25c3b79dd5987737df6ae.woff2 +0 -0
  1035. package/src/styles/fonts/noto-serif-kr/700/9d0afa53dc2f92ba42024f55f11f6779.woff2 +0 -0
  1036. package/src/styles/fonts/noto-serif-kr/700/9e13a47242926f1cd7d68c08d9ef8889.woff2 +0 -0
  1037. package/src/styles/fonts/noto-serif-kr/700/a379ada89abd750c587ded29f77e731c.woff2 +0 -0
  1038. package/src/styles/fonts/noto-serif-kr/700/a48be56ce5d3a99dc8f8331398004412.woff2 +0 -0
  1039. package/src/styles/fonts/noto-serif-kr/700/a806759e72987951d6d08b7cbdf36a1b.woff2 +0 -0
  1040. package/src/styles/fonts/noto-serif-kr/700/a88481ec8bc7be11cb66e46781a79bb9.woff2 +0 -0
  1041. package/src/styles/fonts/noto-serif-kr/700/a9a016d4a93409f65278327e8a8bb38d.woff2 +0 -0
  1042. package/src/styles/fonts/noto-serif-kr/700/ab92d41062a7644faa45f50bf384454a.woff2 +0 -0
  1043. package/src/styles/fonts/noto-serif-kr/700/b326a8a9c33b554db570da94f60bc380.woff2 +0 -0
  1044. package/src/styles/fonts/noto-serif-kr/700/b6117ff2993b11bb1fdc7ea3588a010c.woff2 +0 -0
  1045. package/src/styles/fonts/noto-serif-kr/700/b61982951bd51b724143c30dfaaa9fe9.woff2 +0 -0
  1046. package/src/styles/fonts/noto-serif-kr/700/c0098958e20db68cab90097b5e62516f.woff2 +0 -0
  1047. package/src/styles/fonts/noto-serif-kr/700/c4bfaa5e50798246e3770718b7a7c84a.woff2 +0 -0
  1048. package/src/styles/fonts/noto-serif-kr/700/c84598999133455503042e06f4ab79cb.woff2 +0 -0
  1049. package/src/styles/fonts/noto-serif-kr/700/ca9a533988d7019597a60d4e17127e0c.woff2 +0 -0
  1050. package/src/styles/fonts/noto-serif-kr/700/cf2b28f90f47276f7e2688a65e88a101.woff2 +0 -0
  1051. package/src/styles/fonts/noto-serif-kr/700/d689b1861d7e4377dd72ad3013482612.woff2 +0 -0
  1052. package/src/styles/fonts/noto-serif-kr/700/d9047070d72a816b3dba9d40c2d85e69.woff2 +0 -0
  1053. package/src/styles/fonts/noto-serif-kr/700/da04549f3f4ed28076b01b8cd710d313.woff2 +0 -0
  1054. package/src/styles/fonts/noto-serif-kr/700/da7af303f8c645f9a9dbae0e6e32dd35.woff2 +0 -0
  1055. package/src/styles/fonts/noto-serif-kr/700/df9568257eb29b156449fdd4bec5ec76.woff2 +0 -0
  1056. package/src/styles/fonts/noto-serif-kr/700/e067cd0ed76c90cd0a93c9339253f20b.woff2 +0 -0
  1057. package/src/styles/fonts/noto-serif-kr/700/e08b07772e7bed3cec2832d43f7fd339.woff2 +0 -0
  1058. package/src/styles/fonts/noto-serif-kr/700/e12150d5a39b30be8f567968c7a527b0.woff2 +0 -0
  1059. package/src/styles/fonts/noto-serif-kr/700/e53fcb2381eee345db4f6f973dd95a3e.woff2 +0 -0
  1060. package/src/styles/fonts/noto-serif-kr/700/e5bd313ef81f687d398aacb11cec3069.woff2 +0 -0
  1061. package/src/styles/fonts/noto-serif-kr/700/eb5afb3d952b8593782caec6026514b6.woff2 +0 -0
  1062. package/src/styles/fonts/noto-serif-kr/700/edd6a4f608d04fc0351d7688cfc321e4.woff2 +0 -0
  1063. package/src/styles/fonts/noto-serif-kr/700/f9695c6c4df2bf6bc03045ff79d4f01f.woff2 +0 -0
  1064. package/src/styles/fonts/noto-serif-kr/700/fada6eaa68ff8816afe43d2a36c5423e.woff2 +0 -0
  1065. package/src/styles/fonts/noto-serif-kr/700/fbcc4bf5367218951172bdee6f77d7a6.woff2 +0 -0
  1066. package/src/styles/fonts/noto-serif-kr/noto-serif-kr.css +2601 -0
  1067. package/src/styles/site-media.css +823 -0
  1068. package/src/styles/tokens.css +292 -21
  1069. package/src/styles/ui.css +4188 -1857
  1070. package/src/types/bindings.ts +3 -1
  1071. package/src/types/config.ts +139 -9
  1072. package/src/types/constants.ts +46 -11
  1073. package/src/types/entities.ts +24 -8
  1074. package/src/types/operations.ts +80 -1
  1075. package/src/types/props.ts +10 -2
  1076. package/src/types/raw-assets.d.ts +39 -0
  1077. package/src/types/views.ts +28 -7
  1078. package/src/ui/__tests__/color-themes.test.ts +7 -40
  1079. package/src/ui/__tests__/font-themes.test.ts +30 -11
  1080. package/src/ui/color-themes.ts +311 -270
  1081. package/src/ui/compose/ComposeDialog.tsx +712 -394
  1082. package/src/ui/compose/ComposePrompt.tsx +15 -10
  1083. package/src/ui/dash/ActionButtons.tsx +27 -18
  1084. package/src/ui/dash/DangerZone.tsx +15 -10
  1085. package/src/ui/dash/FormatBadge.tsx +21 -8
  1086. package/src/ui/dash/StatusBadge.tsx +33 -22
  1087. package/src/ui/dash/appearance/AdvancedContent.tsx +44 -30
  1088. package/src/ui/dash/appearance/CodeInjectionContent.tsx +164 -0
  1089. package/src/ui/dash/appearance/ColorThemeContent.tsx +127 -160
  1090. package/src/ui/dash/appearance/FontThemeContent.tsx +20 -15
  1091. package/src/ui/dash/appearance/NavigationContent.tsx +368 -225
  1092. package/src/ui/dash/appearance/__tests__/NavigationContent.test.tsx +54 -0
  1093. package/src/ui/dash/settings/AccountContent.tsx +34 -23
  1094. package/src/ui/dash/settings/AccountMenuContent.tsx +226 -137
  1095. package/src/ui/dash/settings/ApiTokensContent.tsx +122 -79
  1096. package/src/ui/dash/settings/AvatarContent.tsx +73 -45
  1097. package/src/ui/dash/settings/DeleteAccountContent.tsx +106 -66
  1098. package/src/ui/dash/settings/GeneralContent.tsx +274 -157
  1099. package/src/ui/dash/settings/GitHubSyncContent.tsx +558 -0
  1100. package/src/ui/dash/settings/SessionsContent.tsx +79 -38
  1101. package/src/ui/dash/settings/SettingsRootContent.tsx +209 -109
  1102. package/src/ui/dash/settings/__tests__/AccountMenuContent.test.tsx +67 -0
  1103. package/src/ui/dash/settings/__tests__/GeneralContent.test.tsx +75 -0
  1104. package/src/ui/feed/CuratedThreadPreview.tsx +24 -14
  1105. package/src/ui/feed/LinkCard.tsx +116 -84
  1106. package/src/ui/feed/LinkPreview.tsx +75 -0
  1107. package/src/ui/feed/NoteCard.tsx +60 -16
  1108. package/src/ui/feed/PostStatusBadges.tsx +15 -0
  1109. package/src/ui/feed/QuoteCard.tsx +10 -3
  1110. package/src/ui/feed/ThreadPreview.tsx +62 -36
  1111. package/src/ui/feed/TimelineFeed.tsx +2 -1
  1112. package/src/ui/feed/__tests__/thread-preview.test.ts +220 -11
  1113. package/src/ui/feed/__tests__/timeline-cards.test.ts +186 -5
  1114. package/src/ui/feed/thread-preview-state.ts +23 -10
  1115. package/src/ui/font-themes.ts +57 -97
  1116. package/src/ui/layouts/BaseLayout.tsx +221 -136
  1117. package/src/ui/layouts/SiteLayout.tsx +431 -143
  1118. package/src/ui/layouts/__tests__/BaseLayout.test.tsx +76 -6
  1119. package/src/ui/layouts/__tests__/SiteLayout.test.tsx +76 -0
  1120. package/src/ui/pages/ArchivePage.tsx +320 -207
  1121. package/src/ui/pages/BrandPage.tsx +499 -341
  1122. package/src/ui/pages/CollectionEditorPage.tsx +50 -30
  1123. package/src/ui/pages/CollectionPage.tsx +151 -78
  1124. package/src/ui/pages/CollectionsPage.tsx +27 -18
  1125. package/src/ui/pages/ComposePage.tsx +15 -10
  1126. package/src/ui/pages/FeaturedPage.tsx +16 -11
  1127. package/src/ui/pages/HomePage.tsx +15 -10
  1128. package/src/ui/pages/PostPage.tsx +5 -1
  1129. package/src/ui/pages/SearchPage.tsx +93 -43
  1130. package/src/ui/pages/ThemeSamplePage.tsx +758 -520
  1131. package/src/ui/pages/__tests__/ArchivePage.test.tsx +72 -0
  1132. package/src/ui/shared/CollectionDirectory.tsx +215 -34
  1133. package/src/ui/shared/CollectionsManager.tsx +155 -75
  1134. package/src/ui/shared/DecorativeQuoteMark.tsx +12 -9
  1135. package/src/ui/shared/MediaGallery.tsx +350 -256
  1136. package/src/ui/shared/Pagination.tsx +36 -25
  1137. package/src/ui/shared/PostFooter.tsx +139 -96
  1138. package/src/ui/shared/__tests__/media-gallery.test.ts +27 -0
  1139. package/src/ui/shared/__tests__/navigation-labels.test.ts +79 -28
  1140. package/src/ui/shared/__tests__/post-footer.test.ts +42 -17
  1141. package/src/ui/shared/collection-management-labels.ts +109 -33
  1142. package/src/ui/shared/navigation-labels.ts +78 -8
  1143. package/bin/lib/site-localize-media.js +0 -427
  1144. package/dist/client/_assets/client-auth.js +0 -3260
  1145. package/dist/client/_assets/client.css +0 -2
  1146. package/dist/client/_assets/client.js +0 -380
  1147. package/src/__tests__/site-localize-media.test.ts +0 -150
  1148. package/src/client/components/jant-collection-sidebar.ts +0 -816
  1149. package/src/i18n/locales/en.ts +0 -1
  1150. package/src/i18n/locales/zh-Hans.po +0 -3197
  1151. package/src/i18n/locales/zh-Hans.ts +0 -1
  1152. package/src/i18n/locales/zh-Hant.po +0 -3197
  1153. package/src/i18n/locales/zh-Hant.ts +0 -1
  1154. package/src/routes/feed/rss.ts +0 -216
  1155. package/src/types/lingui-react-macro.d.ts +0 -34
  1156. /package/dist/client/_assets/chunks/{heic-to-XcUDQvtx.js → heic-to-DUUaO23q.js} +0 -0
  1157. /package/dist/client/_assets/chunks/{module-ChVQstFd.js → module-DcsAZQZ_.js} +0 -0
  1158. /package/dist/client/_assets/chunks/{native-CR5HLOyf.js → native-DpcrFAPh.js} +0 -0
  1159. /package/dist/client/_assets/{client-cjk.css → client-cjk-B7Z0snDu.css} +0 -0
  1160. /package/dist/client/_assets/{client-cjk-tc.css → client-cjk-tc-BesJYrb2.css} +0 -0
@@ -7,7 +7,7 @@ import {
7
7
  stat,
8
8
  writeFile,
9
9
  } from "node:fs/promises";
10
- import { resolve, join, relative, extname, dirname, basename } from "node:path";
10
+ import { resolve, join, extname, dirname, basename } from "node:path";
11
11
  import { tmpdir } from "node:os";
12
12
  import { parseArgs } from "node:util";
13
13
  import { typeidUnboxed } from "typeid-js";
@@ -20,37 +20,38 @@ import {
20
20
  } from "../lib/site-media-parser.js";
21
21
  import { openNodeDatabase } from "../lib/node-database.js";
22
22
  import { loadNodeRuntime } from "../lib/load-node-runtime.js";
23
+ import { parseFrontMatter as parseFrontMatterShared } from "../../src/lib/hugo-markdown.ts";
23
24
 
24
25
  /**
25
26
  * Parse front matter from a Markdown file.
26
- * Supports both YAML (---...---) and TOML (+++...+++) delimiters.
27
- * Returns { frontMatter, body }.
27
+ * Delegates to the shared hugo-markdown module.
28
28
  */
29
- async function parseFrontMatter(content) {
30
- // Try YAML front matter (---...---)
31
- const yamlMatch = content.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
32
- if (yamlMatch) {
33
- const { parse } = await import("yaml");
34
- const frontMatter = parse(yamlMatch[1]) || {};
35
- return { frontMatter, body: yamlMatch[2] };
36
- }
37
-
38
- // Try TOML front matter (+++...+++)
39
- const tomlMatch = content.match(/^\+\+\+\n([\s\S]*?)\n\+\+\+\n?([\s\S]*)$/);
40
- if (tomlMatch) {
41
- const { parse } = await import("smol-toml");
42
- const frontMatter = parse(tomlMatch[1]);
43
- return { frontMatter, body: tomlMatch[2] };
44
- }
45
-
46
- return { frontMatter: {}, body: content };
47
- }
29
+ const parseFrontMatter = parseFrontMatterShared;
48
30
 
49
31
  async function parseToml(content) {
50
32
  const { parse } = await import("smol-toml");
51
33
  return parse(content);
52
34
  }
53
35
 
36
+ /**
37
+ * Read custom.css from a Jant Hugo export. Prefers the root
38
+ * `static/custom.css` (user override) and falls back to
39
+ * `themes/jant/static/custom.css` (the default location Jant writes).
40
+ */
41
+ async function readImportCustomCss(rootDir) {
42
+ const rootCss = await readFile(
43
+ join(rootDir, "static", "custom.css"),
44
+ "utf-8",
45
+ ).catch(() => null);
46
+ if (rootCss !== null) {
47
+ return rootCss;
48
+ }
49
+ return readFile(
50
+ join(rootDir, "themes", "jant", "static", "custom.css"),
51
+ "utf-8",
52
+ ).catch(() => "");
53
+ }
54
+
54
55
  function resolveImportUrl(url, siteConfig) {
55
56
  if (typeof url !== "string" || url.trim() === "" || url.startsWith("data:")) {
56
57
  return url;
@@ -216,53 +217,6 @@ async function readMediaSpecAsset(media, field = "src") {
216
217
  });
217
218
  }
218
219
 
219
- /**
220
- * Parse reply markers from post body.
221
- * Returns array of { attrs, body } segments where the first is the root.
222
- */
223
- function splitReplies(body) {
224
- const markerRegex = /<!-- jant:reply (.*?) -->/g;
225
-
226
- // Split body by markers, keeping the marker data
227
- const markers = [];
228
- let match;
229
- while ((match = markerRegex.exec(body)) !== null) {
230
- // Parse key="value" pairs from the marker
231
- const attrs = {};
232
- const attrRegex = /(\w+)="([^"]*)"/g;
233
- let attrMatch;
234
- while ((attrMatch = attrRegex.exec(match[1])) !== null) {
235
- attrs[attrMatch[1]] = attrMatch[2];
236
- }
237
- markers.push({
238
- index: match.index,
239
- endIndex: match.index + match[0].length,
240
- attrs,
241
- });
242
- }
243
-
244
- if (markers.length === 0) {
245
- return [{ attrs: null, body: body.trim() }];
246
- }
247
-
248
- const segments = [];
249
-
250
- // Root segment: everything before the first marker
251
- segments.push({ attrs: null, body: body.slice(0, markers[0].index).trim() });
252
-
253
- // Reply segments: between consecutive markers
254
- for (let i = 0; i < markers.length; i++) {
255
- const start = markers[i].endIndex;
256
- const end = i + 1 < markers.length ? markers[i + 1].index : body.length;
257
- segments.push({
258
- attrs: markers[i].attrs,
259
- body: body.slice(start, end).trim(),
260
- });
261
- }
262
-
263
- return segments;
264
- }
265
-
266
220
  function normalizeImportPathKey(value) {
267
221
  if (typeof value !== "string") {
268
222
  return null;
@@ -284,30 +238,35 @@ function normalizeImportAliasPath(value) {
284
238
  return pathKey ? `/${pathKey}` : null;
285
239
  }
286
240
 
287
- function collectReplySlugPaths(replySegments) {
288
- const replySlugPaths = new Set();
289
-
290
- for (const replySegment of replySegments) {
291
- const replySlugPath = normalizeImportAliasPath(replySegment?.attrs?.slug);
292
- if (replySlugPath) {
293
- replySlugPaths.add(replySlugPath);
294
- }
295
- }
296
-
297
- return replySlugPaths;
298
- }
299
-
300
- function getExportedRootAliases(frontMatter) {
301
- const aliases = frontMatter?.extra?.jant?.root_aliases;
302
- return Array.isArray(aliases) ? aliases : [];
303
- }
304
-
305
- function getRootAliasPathsForImport(aliases, postSlug, replySlugPaths) {
241
+ /**
242
+ * Resolve the historical root slugs that should become custom-URL aliases
243
+ * pointing at the current root post. Hugo's `aliases:` on a root also
244
+ * contains every reply slug; we strip those (reply slugs are claimed by
245
+ * their own bundles) and keep only entries that aren't the root itself.
246
+ *
247
+ * `root_aliases:` is the authoritative record of the root's own historical
248
+ * slugs (the exporter writes it for round-trip), so when present we prefer
249
+ * it. Otherwise we fall back to `aliases:` minus reply slugs.
250
+ */
251
+ function getRootAliasPathsForImport(
252
+ aliases,
253
+ rootAliases,
254
+ postSlug,
255
+ replySlugPaths,
256
+ ) {
306
257
  const rootSlugPath = normalizeImportAliasPath(postSlug);
307
258
  const aliasPaths = [];
308
259
  const seen = new Set();
309
260
 
310
- for (const alias of Array.isArray(aliases) ? aliases : []) {
261
+ // Prefer the explicit `root_aliases:` list when the exporter wrote it.
262
+ const source =
263
+ Array.isArray(rootAliases) && rootAliases.length > 0
264
+ ? rootAliases
265
+ : Array.isArray(aliases)
266
+ ? aliases
267
+ : [];
268
+
269
+ for (const alias of source) {
311
270
  const aliasPath = normalizeImportAliasPath(alias);
312
271
  if (!aliasPath) {
313
272
  continue;
@@ -316,9 +275,9 @@ function getRootAliasPathsForImport(aliases, postSlug, replySlugPaths) {
316
275
  continue;
317
276
  }
318
277
  if (replySlugPaths.has(aliasPath)) {
319
- throw new Error(
320
- `Exported root alias "${aliasPath}" conflicts with a reply slug`,
321
- );
278
+ // Reply slugs live in their own bundles — never route their URL back
279
+ // to the root as a custom alias.
280
+ continue;
322
281
  }
323
282
  if (seen.has(aliasPath)) {
324
283
  continue;
@@ -474,6 +433,85 @@ function normalizeImportedBodySegment(markdown) {
474
433
  };
475
434
  }
476
435
 
436
+ function isAbsoluteUrl(value) {
437
+ return typeof value === "string" && /^https?:\/\//i.test(value);
438
+ }
439
+
440
+ /**
441
+ * Resolve a `media:` entry's `src` or `poster` reference to a local disk
442
+ * path when the export bundled the bytes under `static/`. Absolute URLs
443
+ * (remote-linked media) skip the disk lookup — the uploader fetches them
444
+ * directly.
445
+ */
446
+ async function resolveJantMediaDiskPath(ref, sourceRootDir) {
447
+ if (typeof ref !== "string" || !ref.trim()) return null;
448
+ if (isAbsoluteUrl(ref)) return null;
449
+ const normalized = ref.startsWith("/") ? ref.slice(1) : ref;
450
+ const fullPath = join(sourceRootDir, "static", normalized);
451
+ const fileStat = await stat(fullPath).catch(() => null);
452
+ return fileStat?.isFile() ? fullPath : null;
453
+ }
454
+
455
+ /**
456
+ * Build a media spec for upload from a flat `media:` front-matter entry.
457
+ *
458
+ * The entry's `src` is either a site-relative path
459
+ * (`/media/{id}.webp` — bytes live under `static/` in the exported site)
460
+ * or an absolute public URL (the exporter linked to an existing
461
+ * R2/S3/local-proxy host rather than re-bundling the bytes). Poster
462
+ * frames follow the same rule. The import uploader already knows how
463
+ * to handle both forms via `readImportAsset`.
464
+ */
465
+ async function mediaSpecFromJantMedia(entry, sourceRootDir) {
466
+ if (!entry || typeof entry.src !== "string" || !entry.src.trim()) {
467
+ return null;
468
+ }
469
+
470
+ const srcFilePath = await resolveJantMediaDiskPath(entry.src, sourceRootDir);
471
+ if (!srcFilePath && !isAbsoluteUrl(entry.src)) {
472
+ // Relative src but file not on disk — can't upload.
473
+ return null;
474
+ }
475
+
476
+ const poster =
477
+ typeof entry.poster === "string" && entry.poster.trim()
478
+ ? entry.poster
479
+ : null;
480
+ const posterFilePath = poster
481
+ ? await resolveJantMediaDiskPath(poster, sourceRootDir)
482
+ : null;
483
+
484
+ const originalName =
485
+ typeof entry.original_name === "string" && entry.original_name.trim()
486
+ ? entry.original_name
487
+ : srcFilePath
488
+ ? basename(srcFilePath)
489
+ : undefined;
490
+
491
+ return {
492
+ kind: typeof entry.kind === "string" ? entry.kind : undefined,
493
+ src: entry.src,
494
+ srcFilePath,
495
+ poster,
496
+ posterFilePath,
497
+ mimeType:
498
+ typeof entry.mime_type === "string" ? entry.mime_type : undefined,
499
+ originalName,
500
+ size: typeof entry.size === "number" ? entry.size : undefined,
501
+ width: typeof entry.width === "number" ? entry.width : undefined,
502
+ height: typeof entry.height === "number" ? entry.height : undefined,
503
+ alt: typeof entry.alt === "string" ? entry.alt : undefined,
504
+ position:
505
+ typeof entry.position === "string" ? entry.position : undefined,
506
+ blurhash:
507
+ typeof entry.blurhash === "string" ? entry.blurhash : undefined,
508
+ waveform:
509
+ typeof entry.waveform === "string" ? entry.waveform : undefined,
510
+ summary: typeof entry.summary === "string" ? entry.summary : undefined,
511
+ chars: typeof entry.chars === "number" ? entry.chars : undefined,
512
+ };
513
+ }
514
+
477
515
  async function normalizeMediaSpec(spec, siteConfig, sourceRootDir) {
478
516
  if (!spec || typeof spec.src !== "string" || spec.src.trim() === "") {
479
517
  return null;
@@ -488,20 +526,19 @@ async function normalizeMediaSpec(spec, siteConfig, sourceRootDir) {
488
526
  return {
489
527
  kind: spec.kind,
490
528
  src,
491
- srcFilePath: await resolveImportLocalAssetPath(
492
- spec.src,
493
- siteConfig,
494
- sourceRootDir,
495
- ),
529
+ srcFilePath:
530
+ spec.srcFilePath ??
531
+ (await resolveImportLocalAssetPath(spec.src, siteConfig, sourceRootDir)),
496
532
  poster,
497
533
  posterFilePath:
498
- typeof spec.poster === "string"
534
+ spec.posterFilePath ??
535
+ (typeof spec.poster === "string"
499
536
  ? await resolveImportLocalAssetPath(
500
537
  spec.poster,
501
538
  siteConfig,
502
539
  sourceRootDir,
503
540
  )
504
- : null,
541
+ : null),
505
542
  mimeType: spec.mimeType || undefined,
506
543
  originalName: spec.originalName || undefined,
507
544
  size: typeof spec.size === "number" ? spec.size : undefined,
@@ -516,22 +553,86 @@ async function normalizeMediaSpec(spec, siteConfig, sourceRootDir) {
516
553
  };
517
554
  }
518
555
 
519
- function normalizeTextAttachmentSpec(spec) {
556
+ /**
557
+ * Coerce a `<figure data-jant-node="attachment" data-jant-kind="text">` meta
558
+ * block into the shape `createTextAttachment` wants on import.
559
+ *
560
+ * Two source shapes are supported:
561
+ *
562
+ * 1. Inline (legacy exports): the meta JSON carries the markdown itself under
563
+ * `contentFormat: "markdown"` + `content: "..."`. Used before text
564
+ * attachments got their own public URL.
565
+ * 2. Reference (current exports): the meta JSON only carries `kind: "text"`
566
+ * + `src: <url>` pointing at the `.md` artifact. We fetch that URL
567
+ * (falling back to `--pull-media` local disk via
568
+ * `resolveImportLocalAssetPath`, same as images) and use the bytes as
569
+ * the markdown body.
570
+ *
571
+ * Returns `null` when the spec isn't a text attachment or we can't get
572
+ * usable content (unreachable URL, empty body, etc.) — caller drops the
573
+ * attachment rather than creating an empty one.
574
+ */
575
+ async function normalizeTextAttachmentSpec(spec, siteConfig, sourceRootDir) {
576
+ if (!spec || spec.kind !== "text") {
577
+ return null;
578
+ }
579
+
580
+ const summary =
581
+ typeof spec.summary === "string" ? spec.summary : undefined;
582
+
583
+ // Legacy inline content path — no network fetch needed.
520
584
  if (
521
- !spec ||
522
- spec.kind !== "text" ||
523
- spec.contentFormat !== "markdown" ||
524
- typeof spec.content !== "string" ||
525
- spec.content.trim() === ""
585
+ spec.contentFormat === "markdown" &&
586
+ typeof spec.content === "string" &&
587
+ spec.content.trim() !== ""
526
588
  ) {
589
+ return {
590
+ type: "text",
591
+ contentFormat: "markdown",
592
+ content: spec.content,
593
+ summary,
594
+ };
595
+ }
596
+
597
+ // Reference path — fetch the markdown from the stored `src` URL.
598
+ if (typeof spec.src !== "string" || spec.src.trim() === "") {
599
+ return null;
600
+ }
601
+
602
+ const sourceUrl = resolveImportUrl(spec.src, siteConfig);
603
+ if (typeof sourceUrl !== "string" || sourceUrl.trim() === "") {
604
+ return null;
605
+ }
606
+
607
+ const asset = await readImportAsset({
608
+ sourceUrl,
609
+ sourceFilePath: await resolveImportLocalAssetPath(
610
+ spec.src,
611
+ siteConfig,
612
+ sourceRootDir,
613
+ ),
614
+ });
615
+
616
+ if (!asset) {
617
+ return null;
618
+ }
619
+
620
+ let markdown;
621
+ try {
622
+ markdown = new TextDecoder("utf-8", { fatal: false }).decode(asset.bytes);
623
+ } catch {
624
+ return null;
625
+ }
626
+
627
+ if (markdown.trim() === "") {
527
628
  return null;
528
629
  }
529
630
 
530
631
  return {
531
632
  type: "text",
532
633
  contentFormat: "markdown",
533
- content: spec.content,
534
- summary: typeof spec.summary === "string" ? spec.summary : undefined,
634
+ content: markdown,
635
+ summary,
535
636
  };
536
637
  }
537
638
 
@@ -557,7 +658,11 @@ async function buildImportedAttachments(
557
658
  let uploaded = 0;
558
659
 
559
660
  for (const spec of attachmentSpecs) {
560
- const textAttachment = normalizeTextAttachmentSpec(spec);
661
+ const textAttachment = await normalizeTextAttachmentSpec(
662
+ spec,
663
+ siteConfig,
664
+ sourceRootDir,
665
+ );
561
666
  if (textAttachment) {
562
667
  attachments.push(textAttachment);
563
668
  continue;
@@ -600,7 +705,16 @@ async function uploadMediaList(mediaSpecs, target, siteConfig, sourceRootDir) {
600
705
  if (!normalized || normalized.src.startsWith("data:")) continue;
601
706
  const result = await target.uploadMedia(normalized);
602
707
  if (!result) continue;
603
- urlMap.set(normalized.src, result.url);
708
+ // Key the rewrite map by the *original* URL as it appears in the body
709
+ // (e.g. `/media/...`). `normalized.src` has been resolved against
710
+ // `siteConfig.base_url` and becomes absolute, which would never match
711
+ // `node.url` from the markdown AST during `rewriteMediaReferences`.
712
+ if (typeof spec.src === "string" && spec.src.trim() !== "") {
713
+ urlMap.set(spec.src, result.url);
714
+ }
715
+ if (normalized.src !== spec.src) {
716
+ urlMap.set(normalized.src, result.url);
717
+ }
604
718
  mediaIds.push(result.id);
605
719
  uploaded += 1;
606
720
  }
@@ -608,14 +722,190 @@ async function uploadMediaList(mediaSpecs, target, siteConfig, sourceRootDir) {
608
722
  return { urlMap, mediaIds, uploaded };
609
723
  }
610
724
 
725
+ /**
726
+ * Upload each media spec referenced by a bundle's front matter. Accepts
727
+ * both site-relative entries (bytes on disk under `static/`, via
728
+ * `srcFilePath`) and absolute-URL entries (remote-linked media — the
729
+ * uploader fetches the URL). Returns a `urlMap` (for rewriting in-body
730
+ * references) and an ordered `mediaIds` list for post attachment.
731
+ */
732
+ async function uploadBundleResources(resourceSpecs, target) {
733
+ const urlMap = new Map();
734
+ const mediaIds = [];
735
+ let uploaded = 0;
736
+
737
+ for (const spec of resourceSpecs) {
738
+ if (!spec) continue;
739
+ if (!spec.srcFilePath && !isAbsoluteUrl(spec.src)) continue;
740
+ const result = await target.uploadMedia(spec);
741
+ if (!result) continue;
742
+ urlMap.set(spec.src, result.url);
743
+ mediaIds.push(result.id);
744
+ uploaded += 1;
745
+ }
746
+
747
+ return { urlMap, mediaIds, uploaded };
748
+ }
749
+
750
+ function coerceBoolean(value) {
751
+ if (typeof value === "boolean") return value;
752
+ if (typeof value === "string") {
753
+ const normalized = value.trim().toLowerCase();
754
+ return normalized === "true" || normalized === "1" || normalized === "yes";
755
+ }
756
+ return false;
757
+ }
758
+
759
+ /**
760
+ * Build a unified "site config" object from Hugo's split config files.
761
+ *
762
+ * Hugo writes two sources of truth:
763
+ * - `hugo.toml`: baseURL, title, languageCode, theme params
764
+ * - `data/jant.toml`: everything Jant owns — nav items, branding modes,
765
+ * home_default_view, site_footer, avatar urls, display preferences,
766
+ * and the ordered collections directory under `[[directory]]`.
767
+ *
768
+ * This merger normalizes them into a single shape that downstream helpers
769
+ * (`buildSettingsUpdatesFromConfig`, `normalizeImportedNavItems`,
770
+ * `normalizeImportedCollectionDirectory`, `buildSiteAvatarImport`) already
771
+ * know how to read. The shape is intentionally a superset — nothing
772
+ * depends on `hugo.toml`-only vs `data/jant.toml`-only fields, so extra
773
+ * keys are harmless if they leak through.
774
+ */
775
+ async function loadSiteConfig(rootDir) {
776
+ const hugoTomlText = await readFile(
777
+ join(rootDir, "hugo.toml"),
778
+ "utf-8",
779
+ ).catch(() => null);
780
+ const jantDataText = await readFile(
781
+ join(rootDir, "data", "jant.toml"),
782
+ "utf-8",
783
+ ).catch(() => null);
784
+
785
+ if (!hugoTomlText && !jantDataText) {
786
+ return null;
787
+ }
788
+
789
+ const hugoToml = hugoTomlText ? await parseToml(hugoTomlText) : {};
790
+ const jantData = jantDataText ? await parseToml(jantDataText) : {};
791
+
792
+ const params = hugoToml.params ?? {};
793
+
794
+ const title =
795
+ (typeof hugoToml.title === "string" && hugoToml.title) ||
796
+ (typeof jantData.site_name === "string" ? jantData.site_name : "");
797
+ const description =
798
+ (typeof params.description === "string" && params.description) ||
799
+ (typeof jantData.site_description === "string"
800
+ ? jantData.site_description
801
+ : "");
802
+ const baseUrl =
803
+ typeof hugoToml.baseURL === "string"
804
+ ? hugoToml.baseURL
805
+ : typeof hugoToml.baseurl === "string"
806
+ ? hugoToml.baseurl
807
+ : "";
808
+ const language =
809
+ (typeof hugoToml.languageCode === "string" && hugoToml.languageCode) ||
810
+ (typeof hugoToml.defaultContentLanguage === "string" &&
811
+ hugoToml.defaultContentLanguage) ||
812
+ (typeof jantData.site_language === "string"
813
+ ? jantData.site_language
814
+ : "en");
815
+
816
+ const directoryItems = Array.isArray(jantData.directory)
817
+ ? jantData.directory
818
+ : [];
819
+ const directoryExported = Array.isArray(jantData.directory);
820
+
821
+ return {
822
+ // Public base URL (used for resolving relative media URLs).
823
+ base_url: baseUrl,
824
+ title,
825
+ description,
826
+ default_language: language,
827
+ extra: {
828
+ jant: {
829
+ theme_id:
830
+ (typeof params.theme_id === "string" && params.theme_id) ||
831
+ (typeof jantData.theme_id === "string" ? jantData.theme_id : ""),
832
+ default_theme_id:
833
+ (typeof params.default_theme_id === "string" &&
834
+ params.default_theme_id) ||
835
+ (typeof jantData.default_theme_id === "string"
836
+ ? jantData.default_theme_id
837
+ : ""),
838
+ font_theme_id:
839
+ (typeof params.font_theme_id === "string" && params.font_theme_id) ||
840
+ (typeof jantData.font_theme_id === "string"
841
+ ? jantData.font_theme_id
842
+ : ""),
843
+ theme_mode:
844
+ (typeof params.theme_mode === "string" && params.theme_mode) ||
845
+ (typeof jantData.theme_mode === "string" ? jantData.theme_mode : ""),
846
+ home_default_view:
847
+ (typeof params.home_default_view === "string" &&
848
+ params.home_default_view) ||
849
+ (typeof jantData.home_default_view === "string"
850
+ ? jantData.home_default_view
851
+ : ""),
852
+ show_jant_branding_on_home: coerceBoolean(
853
+ params.show_jant_branding_on_home ??
854
+ jantData.show_jant_branding_on_home,
855
+ ),
856
+ show_header_avatar: coerceBoolean(
857
+ params.show_header_avatar ?? jantData.show_header_avatar,
858
+ ),
859
+ noindex: coerceBoolean(params.noindex ?? jantData.noindex),
860
+ site_footer_markdown:
861
+ typeof jantData.site_footer_markdown === "string"
862
+ ? jantData.site_footer_markdown
863
+ : "",
864
+ site_avatar_mode:
865
+ typeof jantData.site_avatar_mode === "string"
866
+ ? jantData.site_avatar_mode
867
+ : "none",
868
+ site_avatar_url:
869
+ typeof jantData.site_avatar_url === "string"
870
+ ? jantData.site_avatar_url
871
+ : typeof params.site_avatar_url === "string"
872
+ ? params.site_avatar_url
873
+ : "",
874
+ favicon_mode:
875
+ typeof jantData.favicon_mode === "string"
876
+ ? jantData.favicon_mode
877
+ : "default",
878
+ favicon_url:
879
+ typeof jantData.favicon_path === "string"
880
+ ? jantData.favicon_path
881
+ : "/favicon.ico",
882
+ apple_touch_mode:
883
+ typeof jantData.apple_touch_mode === "string"
884
+ ? jantData.apple_touch_mode
885
+ : "default",
886
+ apple_touch_icon_url:
887
+ typeof jantData.apple_touch_icon_path === "string"
888
+ ? jantData.apple_touch_icon_path
889
+ : "/apple-touch-icon.png",
890
+ nav: Array.isArray(jantData.nav) ? jantData.nav : [],
891
+ nav_exported: Array.isArray(jantData.nav),
892
+ collections_directory: directoryItems,
893
+ collections_directory_exported: directoryExported,
894
+ },
895
+ jant_export: {
896
+ format:
897
+ typeof jantData.format === "string" ? jantData.format : "jant-site",
898
+ },
899
+ },
900
+ };
901
+ }
902
+
611
903
  function buildSettingsUpdatesFromConfig(siteConfig, customCss = "") {
612
904
  const jant = siteConfig?.extra?.jant || {};
613
905
  const themeId = String(jant.theme_id || "");
614
906
  const defaultThemeId = String(jant.default_theme_id || "");
615
907
  const fontThemeId = String(jant.font_theme_id || "");
616
908
  const themeMode = String(jant.theme_mode || "");
617
- const headerNavMaxVisible = Number(jant.header_nav_max_visible);
618
-
619
909
  return {
620
910
  SITE_NAME: String(siteConfig?.title || ""),
621
911
  SITE_DESCRIPTION: String(siteConfig?.description || ""),
@@ -623,10 +913,6 @@ function buildSettingsUpdatesFromConfig(siteConfig, customCss = "") {
623
913
  SITE_FOOTER: String(jant.site_footer_markdown || ""),
624
914
  HOME_DEFAULT_VIEW:
625
915
  String(jant.home_default_view || "") === "featured" ? "featured" : "",
626
- HEADER_NAV_MAX_VISIBLE:
627
- Number.isFinite(headerNavMaxVisible) && headerNavMaxVisible !== 2
628
- ? String(headerNavMaxVisible)
629
- : "",
630
916
  SHOW_JANT_BRANDING_ON_HOME: jant.show_jant_branding_on_home ? "true" : "",
631
917
  NOINDEX: jant.noindex ? "true" : "",
632
918
  SHOW_HEADER_AVATAR: jant.show_header_avatar ? "true" : "",
@@ -637,6 +923,32 @@ function buildSettingsUpdatesFromConfig(siteConfig, customCss = "") {
637
923
  };
638
924
  }
639
925
 
926
+ /**
927
+ * Internal config keys that the site importer restores via the dedicated
928
+ * `/api/settings/import` route. Must stay in sync with
929
+ * `importableInternalSettingKeys` in `src/lib/api-settings.ts`.
930
+ */
931
+ const IMPORTABLE_INTERNAL_SETTING_KEYS = new Set([
932
+ "THEME",
933
+ "FONT_THEME",
934
+ "THEME_MODE",
935
+ "CUSTOM_CSS",
936
+ "SHOW_HEADER_AVATAR",
937
+ ]);
938
+
939
+ function splitSettingsUpdatesForImport(updates) {
940
+ const editable = {};
941
+ const internal = {};
942
+ for (const [key, value] of Object.entries(updates)) {
943
+ if (IMPORTABLE_INTERNAL_SETTING_KEYS.has(key)) {
944
+ internal[key] = value;
945
+ } else {
946
+ editable[key] = value;
947
+ }
948
+ }
949
+ return { editable, internal };
950
+ }
951
+
640
952
  function normalizeImportedNavItems(siteConfig) {
641
953
  const jant = siteConfig?.extra?.jant || {};
642
954
  const navItems = jant.nav;
@@ -669,6 +981,58 @@ function normalizeImportedNavItems(siteConfig) {
669
981
  };
670
982
  }
671
983
 
984
+ function normalizeImportedCollectionDirectory(siteConfig) {
985
+ const jant = siteConfig?.extra?.jant || {};
986
+ const directoryItems = jant.collections_directory;
987
+ if (!Array.isArray(directoryItems)) {
988
+ return {
989
+ exported: Boolean(jant.collections_directory_exported),
990
+ items: [],
991
+ };
992
+ }
993
+
994
+ return {
995
+ exported: true,
996
+ items: directoryItems
997
+ .map((item) => {
998
+ if (!item || typeof item !== "object") return null;
999
+
1000
+ if (
1001
+ item.type === "collection" &&
1002
+ typeof item.slug === "string" &&
1003
+ item.slug.trim()
1004
+ ) {
1005
+ return {
1006
+ type: "collection",
1007
+ slug: item.slug.trim(),
1008
+ };
1009
+ }
1010
+
1011
+ if (item.type === "divider") {
1012
+ return {
1013
+ type: "divider",
1014
+ label: typeof item.label === "string" ? item.label : null,
1015
+ };
1016
+ }
1017
+
1018
+ if (
1019
+ item.type === "link" &&
1020
+ typeof item.label === "string" &&
1021
+ typeof item.url === "string"
1022
+ ) {
1023
+ return {
1024
+ type: "link",
1025
+ label: item.label,
1026
+ url: item.url,
1027
+ };
1028
+ }
1029
+
1030
+ return null;
1031
+ })
1032
+ .filter(Boolean),
1033
+ };
1034
+ }
1035
+
672
1036
  async function buildSiteAvatarImport(siteConfig, sourceRootDir) {
673
1037
  const exportInfo = siteConfig?.extra?.jant_export || {};
674
1038
  if (exportInfo.format !== "jant-site") {
@@ -744,6 +1108,117 @@ async function buildSiteAvatarImport(siteConfig, sourceRootDir) {
744
1108
  };
745
1109
  }
746
1110
 
1111
+ async function reorderCollectionDirectoryItems(target, orderedIds) {
1112
+ const dedupedIds = [];
1113
+ const seenIds = new Set();
1114
+ for (const id of orderedIds) {
1115
+ if (typeof id !== "string" || !id || seenIds.has(id)) continue;
1116
+ seenIds.add(id);
1117
+ dedupedIds.push(id);
1118
+ }
1119
+
1120
+ let moves = 0;
1121
+
1122
+ for (let index = 0; index < dedupedIds.length; index += 1) {
1123
+ const itemId = dedupedIds[index];
1124
+ const currentItems = await target.listCollectionDirectoryItems();
1125
+ const currentIds = currentItems.map((item) => item.id);
1126
+ const currentIndex = currentIds.indexOf(itemId);
1127
+ if (currentIndex === -1) continue;
1128
+
1129
+ if (index === 0) {
1130
+ if (currentIndex === 0) continue;
1131
+ const beforeId = currentIds.find((id) => id !== itemId) ?? null;
1132
+ await target.moveCollectionDirectoryItem(itemId, null, beforeId);
1133
+ moves += 1;
1134
+ continue;
1135
+ }
1136
+
1137
+ const afterId = dedupedIds[index - 1];
1138
+ const afterIndex = currentIds.indexOf(afterId);
1139
+ if (afterIndex === -1) continue;
1140
+ if (currentIds[afterIndex + 1] === itemId) continue;
1141
+
1142
+ const beforeId =
1143
+ currentIds.slice(afterIndex + 1).find((id) => id !== itemId) ?? null;
1144
+ await target.moveCollectionDirectoryItem(itemId, afterId, beforeId);
1145
+ moves += 1;
1146
+ }
1147
+
1148
+ return moves;
1149
+ }
1150
+
1151
+ async function syncImportedCollectionDirectory(
1152
+ target,
1153
+ importedDirectory,
1154
+ collectionSlugToId,
1155
+ ) {
1156
+ if (!importedDirectory.exported) {
1157
+ return { created: 0, deleted: 0, moved: 0 };
1158
+ }
1159
+
1160
+ let deleted = 0;
1161
+ const existingItems = await target.listCollectionDirectoryItems();
1162
+ for (const item of existingItems) {
1163
+ if (item.type === "collection") continue;
1164
+ const removed = await target.deleteCollectionDirectoryItem(item.id);
1165
+ if (removed !== false) {
1166
+ deleted += 1;
1167
+ }
1168
+ }
1169
+
1170
+ let currentItems = await target.listCollectionDirectoryItems();
1171
+ const collectionItemIds = new Map(
1172
+ currentItems
1173
+ .filter((item) => item.type === "collection" && item.collectionId)
1174
+ .map((item) => [item.collectionId, item.id]),
1175
+ );
1176
+
1177
+ const desiredIds = [];
1178
+ const seenCollectionIds = new Set();
1179
+ let created = 0;
1180
+
1181
+ for (const item of importedDirectory.items) {
1182
+ if (item.type === "collection") {
1183
+ const collectionId = collectionSlugToId.get(item.slug);
1184
+ if (!collectionId || seenCollectionIds.has(collectionId)) {
1185
+ continue;
1186
+ }
1187
+ seenCollectionIds.add(collectionId);
1188
+ const directoryItemId = collectionItemIds.get(collectionId);
1189
+ if (directoryItemId) {
1190
+ desiredIds.push(directoryItemId);
1191
+ }
1192
+ continue;
1193
+ }
1194
+
1195
+ const createdItem = await target.createCollectionDirectoryItem(
1196
+ item.type === "divider"
1197
+ ? {
1198
+ type: "divider",
1199
+ ...(item.label !== null ? { label: item.label } : {}),
1200
+ }
1201
+ : {
1202
+ type: "link",
1203
+ label: item.label,
1204
+ url: item.url,
1205
+ },
1206
+ );
1207
+ desiredIds.push(createdItem.id);
1208
+ created += 1;
1209
+ }
1210
+
1211
+ currentItems = await target.listCollectionDirectoryItems();
1212
+ const currentIds = currentItems.map((item) => item.id);
1213
+ const desiredIdSet = new Set(desiredIds);
1214
+ const moved = await reorderCollectionDirectoryItems(target, [
1215
+ ...desiredIds,
1216
+ ...currentIds.filter((id) => !desiredIdSet.has(id)),
1217
+ ]);
1218
+
1219
+ return { created, deleted, moved };
1220
+ }
1221
+
747
1222
  function buildIncompleteSetupError(targetLabel) {
748
1223
  return [
749
1224
  `${targetLabel} has not completed setup.`,
@@ -859,6 +1334,9 @@ function createRemoteTarget(apiUrl, token) {
859
1334
  async updateSettings(updates) {
860
1335
  return apiCall("PUT", "/api/settings", apiUrl, token, updates);
861
1336
  },
1337
+ async updateImportSettings(updates) {
1338
+ return apiCall("PUT", "/api/settings/import", apiUrl, token, updates);
1339
+ },
862
1340
  async listNavItems() {
863
1341
  const result = await apiCall("GET", "/api/nav-items", apiUrl, token);
864
1342
  return result.navItems || [];
@@ -945,9 +1423,39 @@ function createRemoteTarget(apiUrl, token) {
945
1423
  const existing = await apiCall("GET", "/api/collections", apiUrl, token);
946
1424
  return existing.collections || [];
947
1425
  },
1426
+ async listCollectionDirectoryItems() {
1427
+ const existing = await apiCall("GET", "/api/collections", apiUrl, token);
1428
+ return existing.directoryItems || [];
1429
+ },
948
1430
  async createCollection(data) {
949
1431
  return apiCall("POST", "/api/collections", apiUrl, token, data);
950
1432
  },
1433
+ async createCollectionDirectoryItem(data) {
1434
+ return apiCall(
1435
+ "POST",
1436
+ "/api/collections/directory-items",
1437
+ apiUrl,
1438
+ token,
1439
+ data,
1440
+ );
1441
+ },
1442
+ async moveCollectionDirectoryItem(id, after, before) {
1443
+ return apiCall(
1444
+ "PUT",
1445
+ `/api/collections/directory-items/${id}/move`,
1446
+ apiUrl,
1447
+ token,
1448
+ { after, before },
1449
+ );
1450
+ },
1451
+ async deleteCollectionDirectoryItem(id) {
1452
+ return apiCall(
1453
+ "DELETE",
1454
+ `/api/collections/directory-items/${id}`,
1455
+ apiUrl,
1456
+ token,
1457
+ );
1458
+ },
951
1459
  async createPost(data) {
952
1460
  return apiCall(
953
1461
  "POST",
@@ -1002,6 +1510,10 @@ async function createLocalTarget(env = process.env) {
1002
1510
  await runtime.services.settings.setMany(updates);
1003
1511
  return { settings: updates };
1004
1512
  },
1513
+ async updateImportSettings(updates) {
1514
+ await runtime.services.settings.setMany(updates);
1515
+ return { success: true };
1516
+ },
1005
1517
  async listNavItems() {
1006
1518
  return runtime.services.navItems.list();
1007
1519
  },
@@ -1081,9 +1593,21 @@ async function createLocalTarget(env = process.env) {
1081
1593
  async listCollections() {
1082
1594
  return runtime.services.collections.list();
1083
1595
  },
1596
+ async listCollectionDirectoryItems() {
1597
+ return runtime.services.collections.listDirectoryItems();
1598
+ },
1084
1599
  async createCollection(data) {
1085
1600
  return runtime.services.collections.create(data);
1086
1601
  },
1602
+ async createCollectionDirectoryItem(data) {
1603
+ return runtime.services.collections.createDirectoryItem(data);
1604
+ },
1605
+ async moveCollectionDirectoryItem(id, after, before) {
1606
+ return runtime.services.collections.moveDirectoryItem(id, after, before);
1607
+ },
1608
+ async deleteCollectionDirectoryItem(id) {
1609
+ return runtime.services.collections.deleteDirectoryItem(id);
1610
+ },
1087
1611
  async createPost(data) {
1088
1612
  const { attachments, ...postData } = data;
1089
1613
  return runtime.services.posts.createWithAttachments(
@@ -1181,9 +1705,24 @@ async function createLocalTarget(env = process.env) {
1181
1705
  }
1182
1706
 
1183
1707
  /**
1184
- * Recursively walk a directory's content/ folder and collect post/collection files.
1708
+ * Walk `content/` and classify each `_index.md` / `index.md` bundle by its
1709
+ * front-matter `type`. Returns ordered root-post bundles (with child reply
1710
+ * bundles attached) and stand-alone collection landing pages.
1711
+ *
1712
+ * Algorithm:
1713
+ * 1. Recurse into `content/` collecting every directory that has either
1714
+ * `_index.md` (branch bundle / section) or `index.md` (leaf bundle).
1715
+ * 2. For each `_index.md`, read front matter. `type: "post"` (or a
1716
+ * missing `type` with post-shaped keys) → root bundle. `type:
1717
+ * "collection"` → collection landing page. Other known section types
1718
+ * (`home`, `featured`, `archive`, `collections`) are recorded and
1719
+ * skipped for post import.
1720
+ * 3. For each root bundle, enumerate immediate child directories; any
1721
+ * child dir containing `index.md` becomes a reply leaf bundle.
1722
+ * 4. Reply bundles are sorted by `frontMatter.date` ascending (fallback:
1723
+ * directory name) so thread order is deterministic.
1185
1724
  */
1186
- async function walkContent(rootDir, postFiles, collectionFiles) {
1725
+ async function walkHugoContent(rootDir) {
1187
1726
  const contentDir = join(rootDir, "content");
1188
1727
  const contentStat = await stat(contentDir).catch(() => null);
1189
1728
  if (!contentStat?.isDirectory()) {
@@ -1191,33 +1730,249 @@ async function walkContent(rootDir, postFiles, collectionFiles) {
1191
1730
  process.exit(1);
1192
1731
  }
1193
1732
 
1733
+ // Map of absolute dir → { frontMatter, body, kind, slug, dir, children[] }
1734
+ const dirs = new Map();
1735
+
1736
+ async function readBundle(dir, fileName) {
1737
+ const filePath = join(dir, fileName);
1738
+ const content = await readFile(filePath, "utf-8").catch(() => null);
1739
+ if (content === null) return null;
1740
+ const { frontMatter, body } = await parseFrontMatter(content);
1741
+ return { frontMatter, body };
1742
+ }
1743
+
1194
1744
  async function walk(dir) {
1195
- const entries = await readdir(dir, { withFileTypes: true });
1745
+ const entries = await readdir(dir, { withFileTypes: true }).catch(
1746
+ () => null,
1747
+ );
1748
+ if (!entries) return;
1749
+
1750
+ const hasIndex = entries.some(
1751
+ (entry) => entry.isFile() && entry.name === "_index.md",
1752
+ );
1753
+ const hasLeaf = entries.some(
1754
+ (entry) => entry.isFile() && entry.name === "index.md",
1755
+ );
1756
+
1757
+ if (hasIndex) {
1758
+ const parsed = await readBundle(dir, "_index.md");
1759
+ if (parsed) {
1760
+ const type =
1761
+ typeof parsed.frontMatter.type === "string"
1762
+ ? parsed.frontMatter.type
1763
+ : null;
1764
+ const slug =
1765
+ typeof parsed.frontMatter.slug === "string"
1766
+ ? parsed.frontMatter.slug
1767
+ : basename(dir);
1768
+ dirs.set(dir, {
1769
+ kind: type || "post",
1770
+ slug,
1771
+ dir,
1772
+ frontMatter: parsed.frontMatter,
1773
+ body: parsed.body,
1774
+ file: "_index.md",
1775
+ children: [],
1776
+ });
1777
+ }
1778
+ } else if (hasLeaf) {
1779
+ const parsed = await readBundle(dir, "index.md");
1780
+ if (parsed) {
1781
+ const slug =
1782
+ typeof parsed.frontMatter.slug === "string"
1783
+ ? parsed.frontMatter.slug
1784
+ : basename(dir);
1785
+ dirs.set(dir, {
1786
+ kind: "leaf",
1787
+ slug,
1788
+ dir,
1789
+ frontMatter: parsed.frontMatter,
1790
+ body: parsed.body,
1791
+ file: "index.md",
1792
+ children: [],
1793
+ });
1794
+ }
1795
+ }
1796
+
1196
1797
  for (const entry of entries) {
1197
- const fullPath = join(dir, entry.name);
1198
1798
  if (entry.isDirectory()) {
1199
- await walk(fullPath);
1200
- } else if (entry.name === "index.md" || entry.name === "_index.md") {
1201
- const relPath = relative(rootDir, fullPath).replace(/\\/g, "/");
1202
- const content = await readFile(fullPath, "utf-8");
1203
- if (
1204
- (relPath.startsWith("content/jant-collections/") ||
1205
- relPath.startsWith("content/c/")) &&
1206
- relPath.endsWith("/_index.md")
1207
- ) {
1208
- collectionFiles.push({ path: relPath, content });
1209
- } else if (
1210
- relPath.startsWith("content/") &&
1211
- relPath.endsWith("/index.md") &&
1212
- relPath !== "content/_index.md"
1213
- ) {
1214
- postFiles.push({ path: relPath, content });
1215
- }
1799
+ await walk(join(dir, entry.name));
1216
1800
  }
1217
1801
  }
1218
1802
  }
1219
1803
 
1220
1804
  await walk(contentDir);
1805
+
1806
+ // Attach leaf children to their parent root bundles.
1807
+ const rootBundles = [];
1808
+ const collectionBundles = [];
1809
+
1810
+ for (const record of dirs.values()) {
1811
+ if (record.kind === "leaf") {
1812
+ const parentDir = dirname(record.dir);
1813
+ const parent = dirs.get(parentDir);
1814
+ if (parent && (parent.kind === "post" || parent.kind === null)) {
1815
+ parent.children.push(record);
1816
+ }
1817
+ continue;
1818
+ }
1819
+ if (record.kind === "collection") {
1820
+ collectionBundles.push(record);
1821
+ continue;
1822
+ }
1823
+ if (record.kind === "post") {
1824
+ rootBundles.push(record);
1825
+ continue;
1826
+ }
1827
+ // home / featured / archive / collections / anything else — skip.
1828
+ }
1829
+
1830
+ // Sort replies within each root by `date` asc (fallback: directory name).
1831
+ for (const root of rootBundles) {
1832
+ root.children.sort((a, b) => {
1833
+ const aDate =
1834
+ typeof a.frontMatter.date === "string"
1835
+ ? Date.parse(a.frontMatter.date)
1836
+ : NaN;
1837
+ const bDate =
1838
+ typeof b.frontMatter.date === "string"
1839
+ ? Date.parse(b.frontMatter.date)
1840
+ : NaN;
1841
+ if (Number.isFinite(aDate) && Number.isFinite(bDate) && aDate !== bDate) {
1842
+ return aDate - bDate;
1843
+ }
1844
+ if (Number.isFinite(aDate) && !Number.isFinite(bDate)) return -1;
1845
+ if (!Number.isFinite(aDate) && Number.isFinite(bDate)) return 1;
1846
+ return basename(a.dir).localeCompare(basename(b.dir));
1847
+ });
1848
+ }
1849
+
1850
+ return { rootBundles, collectionBundles };
1851
+ }
1852
+
1853
+ /**
1854
+ * Resolve the collection memberships listed in a bundle's top-level
1855
+ * `collections:` front-matter array into `{collectionId, createdAt,
1856
+ * position, pinnedAt}` records the post-create APIs expect.
1857
+ *
1858
+ * Drops entries whose slug doesn't map to a known collection — the
1859
+ * importer prints no warning (collections without slugs are already
1860
+ * filtered by the exporter).
1861
+ */
1862
+ function resolveCollectionMemberships(frontMatter, collectionSlugToId) {
1863
+ const collections = Array.isArray(frontMatter.collections)
1864
+ ? frontMatter.collections
1865
+ : [];
1866
+ const entries = [];
1867
+ const ids = [];
1868
+ for (const raw of collections) {
1869
+ if (!raw || typeof raw.slug !== "string") continue;
1870
+ const id = collectionSlugToId.get(raw.slug);
1871
+ if (!id) continue;
1872
+ const entry = { collectionId: id };
1873
+ if (typeof raw.collected_at === "string" && raw.collected_at) {
1874
+ entry.createdAt = Math.floor(
1875
+ new Date(raw.collected_at).getTime() / 1000,
1876
+ );
1877
+ }
1878
+ if (typeof raw.position === "number") {
1879
+ entry.position = raw.position;
1880
+ }
1881
+ if (typeof raw.pinned_at === "string" && raw.pinned_at) {
1882
+ entry.pinnedAt = Math.floor(new Date(raw.pinned_at).getTime() / 1000);
1883
+ }
1884
+ entries.push(entry);
1885
+ ids.push(id);
1886
+ }
1887
+ return { entries, ids };
1888
+ }
1889
+
1890
+ /**
1891
+ * Build the payload for `target.createPost()` from a parsed bundle. Works
1892
+ * for both root bundles (`forReply: false`) and reply leaf bundles — the
1893
+ * front-matter shape is identical aside from `build:` and the parent link.
1894
+ */
1895
+ function buildPostPayloadFromBundle(bundle, options) {
1896
+ const { frontMatter } = bundle;
1897
+ const format =
1898
+ typeof frontMatter.format === "string" ? frontMatter.format : "note";
1899
+ const slug =
1900
+ typeof frontMatter.slug === "string" ? frontMatter.slug : undefined;
1901
+ const status =
1902
+ frontMatter.status === "draft" || frontMatter.status === "published"
1903
+ ? frontMatter.status
1904
+ : frontMatter.draft
1905
+ ? "draft"
1906
+ : "published";
1907
+ // Accept every canonical visibility value (see `VISIBILITIES` in
1908
+ // `src/types/constants.ts`). Anything else — or missing — falls back to
1909
+ // the service default (`public`).
1910
+ const visibility =
1911
+ frontMatter.visibility === "public" ||
1912
+ frontMatter.visibility === "latest_hidden" ||
1913
+ frontMatter.visibility === "private"
1914
+ ? frontMatter.visibility
1915
+ : undefined;
1916
+ const { entries: collectionEntries, ids: collectionIds } = options.memberships;
1917
+
1918
+ const data = {
1919
+ format,
1920
+ title:
1921
+ format === "quote"
1922
+ ? typeof frontMatter.source_name === "string"
1923
+ ? frontMatter.source_name
1924
+ : undefined
1925
+ : typeof frontMatter.title === "string"
1926
+ ? frontMatter.title
1927
+ : undefined,
1928
+ bodyMarkdown: options.bodyMarkdown || undefined,
1929
+ slug,
1930
+ status,
1931
+ visibility,
1932
+ collectionIds:
1933
+ collectionEntries.length === 0 && collectionIds.length > 0
1934
+ ? collectionIds
1935
+ : undefined,
1936
+ collectionEntries:
1937
+ collectionEntries.length > 0 ? collectionEntries : undefined,
1938
+ attachments:
1939
+ options.attachments.length > 0 ? options.attachments : undefined,
1940
+ publishedAt:
1941
+ status === "published" && typeof frontMatter.date === "string"
1942
+ ? Math.floor(new Date(frontMatter.date).getTime() / 1000)
1943
+ : undefined,
1944
+ featuredAt:
1945
+ typeof frontMatter.featured_at === "string" && frontMatter.featured_at
1946
+ ? Math.floor(new Date(frontMatter.featured_at).getTime() / 1000)
1947
+ : undefined,
1948
+ pinnedAt:
1949
+ typeof frontMatter.pinned_at === "string" && frontMatter.pinned_at
1950
+ ? Math.floor(new Date(frontMatter.pinned_at).getTime() / 1000)
1951
+ : undefined,
1952
+ rating:
1953
+ typeof frontMatter.rating === "number" ? frontMatter.rating : undefined,
1954
+ };
1955
+
1956
+ if (options.replyToId) {
1957
+ data.replyToId = options.replyToId;
1958
+ }
1959
+
1960
+ if (format === "link" && typeof frontMatter.link_url === "string") {
1961
+ data.url = frontMatter.link_url;
1962
+ }
1963
+ if (format === "quote") {
1964
+ if (typeof frontMatter.quote_text === "string") {
1965
+ data.quoteText = frontMatter.quote_text;
1966
+ }
1967
+ if (
1968
+ typeof frontMatter.source_url === "string" &&
1969
+ frontMatter.source_url.trim()
1970
+ ) {
1971
+ data.url = frontMatter.source_url;
1972
+ }
1973
+ }
1974
+
1975
+ return data;
1221
1976
  }
1222
1977
 
1223
1978
  export const __test__ = {
@@ -1227,16 +1982,23 @@ export const __test__ = {
1227
1982
  normalizeTextAttachmentSpec,
1228
1983
  extractAttachmentBlocks,
1229
1984
  buildImportedAttachments,
1985
+ uploadMediaList,
1230
1986
  buildSettingsUpdatesFromConfig,
1231
1987
  normalizeImportedNavItems,
1988
+ normalizeImportedCollectionDirectory,
1232
1989
  buildSiteAvatarImport,
1233
- collectReplySlugPaths,
1234
- getExportedRootAliases,
1990
+ reorderCollectionDirectoryItems,
1991
+ syncImportedCollectionDirectory,
1235
1992
  getRootAliasPathsForImport,
1236
1993
  toRemotePostPayload,
1237
1994
  buildIncompleteSetupError,
1238
1995
  detectRemoteSetupStatus,
1239
1996
  getIncompleteSetupError,
1997
+ loadSiteConfig,
1998
+ walkHugoContent,
1999
+ mediaSpecFromJantMedia,
2000
+ resolveCollectionMemberships,
2001
+ buildPostPayloadFromBundle,
1240
2002
  };
1241
2003
 
1242
2004
  export async function run(argv) {
@@ -1255,7 +2017,7 @@ export async function run(argv) {
1255
2017
  if (values.help) {
1256
2018
  console.log("Usage: jant site import [--url <url>] [options]");
1257
2019
  console.log("");
1258
- console.log("Import a Zola export directory or ZIP into a Jant instance.");
2020
+ console.log("Import a Hugo export directory or ZIP into a Jant instance.");
1259
2021
  console.log("");
1260
2022
  console.log("Modes:");
1261
2023
  console.log(
@@ -1320,26 +2082,10 @@ export async function run(argv) {
1320
2082
  process.exit(1);
1321
2083
  }
1322
2084
 
1323
- const postFiles = [];
1324
- const collectionFiles = [];
1325
- let siteConfig = null;
1326
- let customCss = "";
1327
2085
  let sourceRootDir = inputPath;
1328
2086
  let tempSourceRootDir = null;
1329
2087
 
1330
- if (inputStat.isDirectory()) {
1331
- console.log(`Reading directory ${inputPath}...`);
1332
- await walkContent(sourceRootDir, postFiles, collectionFiles);
1333
- const configPath = join(sourceRootDir, "config.toml");
1334
- const configContent = await readFile(configPath, "utf-8").catch(() => null);
1335
- if (configContent) {
1336
- siteConfig = await parseToml(configContent);
1337
- }
1338
- customCss = await readFile(
1339
- join(sourceRootDir, "static", "custom.css"),
1340
- "utf-8",
1341
- ).catch(() => "");
1342
- } else {
2088
+ if (inputStat.isFile()) {
1343
2089
  console.log(`Reading ZIP ${inputPath}...`);
1344
2090
  const zipData = await readFile(inputPath);
1345
2091
  const { unzipSync } = await import("fflate");
@@ -1351,23 +2097,26 @@ export async function run(argv) {
1351
2097
  await mkdir(dirname(fullPath), { recursive: true });
1352
2098
  await writeFile(fullPath, data);
1353
2099
  }
1354
-
1355
- await walkContent(sourceRootDir, postFiles, collectionFiles);
1356
- const configPath = join(sourceRootDir, "config.toml");
1357
- const configContent = await readFile(configPath, "utf-8").catch(() => null);
1358
- if (configContent) {
1359
- siteConfig = await parseToml(configContent);
1360
- }
1361
- customCss = await readFile(
1362
- join(sourceRootDir, "static", "custom.css"),
1363
- "utf-8",
1364
- ).catch(() => "");
2100
+ } else {
2101
+ console.log(`Reading directory ${inputPath}...`);
1365
2102
  }
1366
2103
 
2104
+ const { rootBundles, collectionBundles } =
2105
+ await walkHugoContent(sourceRootDir);
2106
+ const siteConfig = await loadSiteConfig(sourceRootDir);
2107
+ const customCss = await readImportCustomCss(sourceRootDir);
2108
+
1367
2109
  try {
2110
+ const replyCount = rootBundles.reduce(
2111
+ (sum, root) => sum + root.children.length,
2112
+ 0,
2113
+ );
1368
2114
  console.log(
1369
- `Found ${postFiles.length} posts and ${collectionFiles.length} collections`,
2115
+ `Found ${rootBundles.length} posts (+${replyCount} replies) and ${collectionBundles.length} collections`,
1370
2116
  );
2117
+ const importedCollectionDirectory = siteConfig
2118
+ ? normalizeImportedCollectionDirectory(siteConfig)
2119
+ : { exported: false, items: [] };
1371
2120
 
1372
2121
  if (target) {
1373
2122
  const setupError = await getIncompleteSetupError(
@@ -1408,8 +2157,10 @@ export async function run(argv) {
1408
2157
  }
1409
2158
  }
1410
2159
  } else {
2160
+ const { editable, internal } =
2161
+ splitSettingsUpdatesForImport(settingsUpdates);
1411
2162
  try {
1412
- const result = await target.updateSettings(settingsUpdates);
2163
+ const result = await target.updateSettings(editable);
1413
2164
  if (result?.rejectedKeys?.length) {
1414
2165
  console.warn(
1415
2166
  `Warning: Some site settings were rejected: ${result.rejectedKeys.join(", ")}`,
@@ -1422,6 +2173,22 @@ export async function run(argv) {
1422
2173
  process.exit(1);
1423
2174
  }
1424
2175
 
2176
+ if (Object.keys(internal).length > 0) {
2177
+ try {
2178
+ const result = await target.updateImportSettings(internal);
2179
+ if (result?.rejectedKeys?.length) {
2180
+ console.warn(
2181
+ `Warning: Some internal site settings were rejected: ${result.rejectedKeys.join(", ")}`,
2182
+ );
2183
+ }
2184
+ } catch (err) {
2185
+ console.error(
2186
+ `Error applying exported internal site settings: ${err.message}`,
2187
+ );
2188
+ process.exit(1);
2189
+ }
2190
+ }
2191
+
1425
2192
  if (importedNav.exported) {
1426
2193
  try {
1427
2194
  const existingNavItems = await target.listNavItems();
@@ -1443,8 +2210,13 @@ export async function run(argv) {
1443
2210
  avatarImport.mode === "set" ? avatarImport : null,
1444
2211
  );
1445
2212
  } catch (err) {
1446
- console.error(`Error importing site avatar: ${err.message}`);
1447
- process.exit(1);
2213
+ // Non-fatal: continue the rest of the import even if the avatar
2214
+ // source asset is unreachable (e.g. unreadable file, stale URL).
2215
+ // The rest of the site data is far more valuable than losing the
2216
+ // entire import over a single image.
2217
+ console.warn(
2218
+ `Warning: Could not import site avatar — continuing without it. (${err.message})`,
2219
+ );
1448
2220
  }
1449
2221
  }
1450
2222
  }
@@ -1465,12 +2237,8 @@ export async function run(argv) {
1465
2237
  }
1466
2238
  }
1467
2239
 
1468
- for (const { path, content } of collectionFiles) {
1469
- const { frontMatter } = await parseFrontMatter(content);
1470
- const slug = path
1471
- .replace("content/jant-collections/", "")
1472
- .replace("content/c/", "")
1473
- .replace("/_index.md", "");
2240
+ for (const bundle of collectionBundles) {
2241
+ const slug = bundle.slug;
1474
2242
 
1475
2243
  if (collectionSlugToId.has(slug)) {
1476
2244
  console.error(
@@ -1481,83 +2249,117 @@ export async function run(argv) {
1481
2249
 
1482
2250
  if (dryRun) {
1483
2251
  console.log(
1484
- `[dry-run] Would create collection: ${frontMatter.title || slug}`,
2252
+ `[dry-run] Would create collection: ${bundle.frontMatter.title || slug}`,
1485
2253
  );
1486
2254
  collectionSlugToId.set(slug, `dry-run-${slug}`);
1487
2255
  continue;
1488
2256
  }
1489
2257
 
1490
2258
  try {
1491
- const collectionExtra = frontMatter.extra || {};
1492
2259
  const result = await target.createCollection({
1493
- title: frontMatter.title || slug,
2260
+ title: bundle.frontMatter.title || slug,
1494
2261
  slug,
1495
- description: frontMatter.description || null,
2262
+ description:
2263
+ typeof bundle.frontMatter.summary_text === "string"
2264
+ ? bundle.frontMatter.summary_text
2265
+ : undefined,
1496
2266
  sortOrder:
1497
- collectionExtra.sort_order ||
1498
- collectionExtra.sortOrder ||
1499
- undefined,
2267
+ typeof bundle.frontMatter.sort_order === "string"
2268
+ ? bundle.frontMatter.sort_order
2269
+ : undefined,
1500
2270
  });
1501
2271
  collectionSlugToId.set(slug, result.id);
1502
- console.log(`Created collection: ${frontMatter.title || slug}`);
2272
+ console.log(`Created collection: ${bundle.frontMatter.title || slug}`);
1503
2273
  } catch (err) {
1504
2274
  console.error(`Error creating collection "${slug}": ${err.message}`);
1505
2275
  process.exit(1);
1506
2276
  }
1507
2277
  }
1508
2278
 
1509
- // 4. Process posts
2279
+ if (importedCollectionDirectory.exported) {
2280
+ if (dryRun) {
2281
+ console.log(
2282
+ `[dry-run] Would restore collection directory with ${importedCollectionDirectory.items.length} items`,
2283
+ );
2284
+ } else {
2285
+ try {
2286
+ await syncImportedCollectionDirectory(
2287
+ target,
2288
+ importedCollectionDirectory,
2289
+ collectionSlugToId,
2290
+ );
2291
+ } catch (err) {
2292
+ console.error(
2293
+ `Error restoring collections directory: ${err.message}`,
2294
+ );
2295
+ process.exit(1);
2296
+ }
2297
+ }
2298
+ }
2299
+
2300
+ // 4. Process posts — root bundle first, then each reply leaf, then aliases.
1510
2301
  let postsCreated = 0;
1511
2302
  let repliesCreated = 0;
1512
2303
  let mediaUploaded = 0;
1513
2304
  let aliasesCreated = 0;
1514
2305
 
1515
- for (const { path, content } of postFiles) {
1516
- const { frontMatter, body } = await parseFrontMatter(content);
1517
-
1518
- const segments = splitReplies(body);
1519
- const rootSegment = segments[0];
1520
- const replySegments = segments.slice(1);
1521
- const replySlugPaths = collectReplySlugPaths(replySegments);
1522
-
1523
- // Resolve collection IDs from taxonomy slugs
1524
- const collectionIds = [];
1525
- const taxonomyCollections =
1526
- frontMatter.taxonomies?.c || frontMatter.taxonomies?.collections || [];
1527
- for (const colSlug of taxonomyCollections) {
1528
- const id = collectionSlugToId.get(colSlug);
1529
- if (id) collectionIds.push(id);
1530
- }
1531
-
1532
- const extra = frontMatter.extra || {};
1533
- const format = extra.format || "note";
1534
- const postSlug =
1535
- frontMatter.slug != null ? String(frontMatter.slug) : undefined;
1536
- const postStatus =
1537
- extra.status === "draft" || extra.status === "published"
1538
- ? extra.status
1539
- : frontMatter.draft
1540
- ? "draft"
1541
- : "published";
1542
- const postVisibility =
1543
- extra.visibility === "unlisted" || extra.visibility === "private"
1544
- ? extra.visibility
1545
- : undefined;
2306
+ for (const rootBundle of rootBundles) {
2307
+ const { frontMatter: rootFm } = rootBundle;
2308
+ const postSlug = rootBundle.slug;
2309
+ const format =
2310
+ typeof rootFm.format === "string" ? rootFm.format : "note";
1546
2311
  const postLabel =
1547
- (format === "quote" ? extra.source_name : frontMatter.title) ||
2312
+ (format === "quote"
2313
+ ? typeof rootFm.source_name === "string"
2314
+ ? rootFm.source_name
2315
+ : null
2316
+ : typeof rootFm.title === "string"
2317
+ ? rootFm.title
2318
+ : null) ||
1548
2319
  postSlug ||
1549
2320
  "(untitled)";
1550
2321
 
1551
- if (!dryRun && postSlug) {
2322
+ if (!dryRun) {
1552
2323
  await assertImportSlugAvailable(target, postSlug, postLabel, "post");
1553
2324
  }
1554
2325
 
1555
- // Process images in root body
1556
- let rootBody = rootSegment?.body || "";
1557
- const normalizedRootBody = normalizeImportedBodySegment(rootBody);
1558
- rootBody = normalizedRootBody.markdown;
2326
+ // Normalize body + extract embedded attachment blocks.
2327
+ const normalizedRoot = normalizeImportedBodySegment(rootBundle.body);
2328
+ let rootBody = normalizedRoot.markdown;
1559
2329
  let importedAttachments = [];
1560
2330
 
2331
+ // Upload media declared in flat `media:` front matter. Each entry's
2332
+ // `src` is either a site-relative path (bytes under `static/`) or an
2333
+ // absolute URL (media still served by the original provider).
2334
+ // Text attachments (`kind: "text"`) live under the same `media:` key
2335
+ // but must be imported via `normalizeTextAttachmentSpec` so the body
2336
+ // is decoded and stored as a text attachment rather than uploaded as
2337
+ // a generic media file.
2338
+ const rootResourceSpecs = [];
2339
+ const rootTextAttachmentEntries = [];
2340
+ if (Array.isArray(rootFm.media)) {
2341
+ for (const entry of rootFm.media) {
2342
+ if (entry && typeof entry === "object" && entry.kind === "text") {
2343
+ rootTextAttachmentEntries.push(entry);
2344
+ continue;
2345
+ }
2346
+ const spec = await mediaSpecFromJantMedia(entry, sourceRootDir);
2347
+ if (spec) rootResourceSpecs.push(spec);
2348
+ }
2349
+ }
2350
+
2351
+ const rootResourceIds = [];
2352
+ if (!skipMedia && !dryRun && rootResourceSpecs.length > 0) {
2353
+ const result = await uploadBundleResources(rootResourceSpecs, target);
2354
+ mediaUploaded += result.uploaded;
2355
+ if (result.urlMap.size > 0) {
2356
+ rootBody = rewriteMediaReferences(rootBody, result.urlMap);
2357
+ }
2358
+ rootResourceIds.push(...result.mediaIds);
2359
+ }
2360
+
2361
+ // Fallback: rewrite any leftover in-body image URLs (covers hand-
2362
+ // authored Hugo content where the exporter didn't declare resources).
1561
2363
  if (!skipMedia && !dryRun) {
1562
2364
  const imageMedia = findImageUrls(rootBody).map((src) => ({ src }));
1563
2365
  const uploadResult = await uploadMediaList(
@@ -1567,14 +2369,14 @@ export async function run(argv) {
1567
2369
  sourceRootDir,
1568
2370
  );
1569
2371
  mediaUploaded += uploadResult.uploaded;
1570
-
1571
2372
  if (uploadResult.urlMap.size > 0) {
1572
2373
  rootBody = rewriteMediaReferences(rootBody, uploadResult.urlMap);
1573
2374
  }
1574
2375
  }
2376
+
1575
2377
  if (!dryRun) {
1576
2378
  const attachmentResult = await buildImportedAttachments(
1577
- normalizedRootBody.attachments,
2379
+ normalizedRoot.attachments,
1578
2380
  target,
1579
2381
  siteConfig,
1580
2382
  sourceRootDir,
@@ -1584,91 +2386,129 @@ export async function run(argv) {
1584
2386
  mediaUploaded += attachmentResult.uploaded;
1585
2387
  }
1586
2388
 
1587
- const postData = {
1588
- format,
1589
- title:
1590
- format === "quote"
1591
- ? typeof extra.source_name === "string"
1592
- ? extra.source_name
1593
- : undefined
1594
- : frontMatter.title != null
1595
- ? String(frontMatter.title)
1596
- : undefined,
1597
- bodyMarkdown: rootBody || undefined,
1598
- slug: postSlug,
1599
- path: frontMatter.path != null ? String(frontMatter.path) : undefined,
1600
- status: postStatus,
1601
- visibility: postVisibility,
1602
- collectionIds: collectionIds.length > 0 ? collectionIds : undefined,
1603
- attachments:
1604
- importedAttachments.length > 0 ? importedAttachments : undefined,
1605
- publishedAt:
1606
- postStatus === "published" && frontMatter.date
1607
- ? Math.floor(new Date(frontMatter.date).getTime() / 1000)
1608
- : undefined,
1609
- pinned: extra.pinned || undefined,
1610
- featured: extra.featured || undefined,
1611
- rating: extra.rating || undefined,
1612
- };
1613
-
1614
- if (format === "link" && extra.link_url) {
1615
- postData.url = extra.link_url;
2389
+ // Also attach every uploaded root page-resource as a media attachment
2390
+ // so the post record carries them even when none appear in body text.
2391
+ for (const mediaId of rootResourceIds) {
2392
+ importedAttachments.push({ type: "media", mediaId });
1616
2393
  }
1617
- if (format === "quote" && extra.quote_text) {
1618
- postData.quoteText = extra.quote_text;
1619
- if (typeof extra.source_url === "string" && extra.source_url.trim()) {
1620
- postData.url = extra.source_url;
2394
+
2395
+ // Build text attachments declared in the flat `media:` front matter.
2396
+ // These reference a `.md` artifact that holds the full body; the
2397
+ // normalizer fetches the bytes (local disk first, then remote URL)
2398
+ // and decodes them.
2399
+ if (!skipMedia && !dryRun) {
2400
+ for (const textEntry of rootTextAttachmentEntries) {
2401
+ const textAttachment = await normalizeTextAttachmentSpec(
2402
+ textEntry,
2403
+ siteConfig,
2404
+ sourceRootDir,
2405
+ );
2406
+ if (textAttachment) importedAttachments.push(textAttachment);
1621
2407
  }
1622
2408
  }
1623
2409
 
2410
+ const memberships = resolveCollectionMemberships(
2411
+ rootFm,
2412
+ collectionSlugToId,
2413
+ );
2414
+ const postData = buildPostPayloadFromBundle(rootBundle, {
2415
+ bodyMarkdown: rootBody,
2416
+ attachments: importedAttachments,
2417
+ memberships,
2418
+ replyToId: null,
2419
+ });
2420
+
1624
2421
  if (dryRun) {
1625
2422
  console.log(`[dry-run] Would create post: ${postLabel} (${format})`);
1626
- if (replySegments.length > 0) {
1627
- console.log(` [dry-run] With ${replySegments.length} replies`);
2423
+ if (rootBundle.children.length > 0) {
2424
+ console.log(
2425
+ ` [dry-run] With ${rootBundle.children.length} replies`,
2426
+ );
1628
2427
  }
1629
2428
  postsCreated++;
1630
- repliesCreated += replySegments.length;
2429
+ repliesCreated += rootBundle.children.length;
1631
2430
  continue;
1632
2431
  }
1633
2432
 
1634
- const progress = `[${postsCreated + 1}/${postFiles.length}]`;
1635
-
2433
+ const progress = `[${postsCreated + 1}/${rootBundles.length}]`;
1636
2434
  let post;
1637
2435
  try {
1638
2436
  post = await target.createPost(postData);
1639
2437
  postsCreated++;
1640
2438
  const replyInfo =
1641
- replySegments.length > 0 ? ` (+${replySegments.length} replies)` : "";
2439
+ rootBundle.children.length > 0
2440
+ ? ` (+${rootBundle.children.length} replies)`
2441
+ : "";
1642
2442
  console.log(`${progress} Created: ${postLabel}${replyInfo}`);
1643
2443
  } catch (err) {
1644
2444
  console.error(`Error creating post "${postLabel}": ${err.message}`);
1645
2445
  process.exit(1);
1646
2446
  }
1647
2447
 
1648
- // Create replies before aliases so reply slugs can claim their own paths.
2448
+ // Create replies before aliases so reply slugs can claim their paths.
1649
2449
  if (!post) continue;
1650
- for (const replySegment of replySegments) {
1651
- const replyAttrs = replySegment.attrs || {};
1652
- const replySlug = replyAttrs.slug || undefined;
2450
+ const replySlugPaths = new Set();
2451
+ // Jant threads are linear: each reply must point at the current end of
2452
+ // the thread, not at the root. Track the tail as we go so the Nth
2453
+ // reply chains after the (N−1)th.
2454
+ let threadTailId = post.id;
2455
+ for (const replyBundle of rootBundle.children) {
2456
+ const replyFm = replyBundle.frontMatter;
2457
+ const replySlug = replyBundle.slug;
2458
+ const replyFormat =
2459
+ typeof replyFm.format === "string" ? replyFm.format : "note";
1653
2460
  const replyLabel =
1654
- replyAttrs.source_name ||
1655
- replyAttrs.title ||
2461
+ (replyFormat === "quote"
2462
+ ? typeof replyFm.source_name === "string"
2463
+ ? replyFm.source_name
2464
+ : null
2465
+ : typeof replyFm.title === "string"
2466
+ ? replyFm.title
2467
+ : null) ||
1656
2468
  replySlug ||
1657
2469
  "(untitled reply)";
1658
- if (!dryRun && replySlug) {
1659
- await assertImportSlugAvailable(
2470
+
2471
+ const replySlugPath = normalizeImportAliasPath(replySlug);
2472
+ if (replySlugPath) replySlugPaths.add(replySlugPath);
2473
+
2474
+ await assertImportSlugAvailable(
2475
+ target,
2476
+ replySlug,
2477
+ `${replyLabel} in ${postLabel}`,
2478
+ "reply",
2479
+ );
2480
+
2481
+ const normalizedReply = normalizeImportedBodySegment(replyBundle.body);
2482
+ let replyBody = normalizedReply.markdown;
2483
+ let replyAttachments = [];
2484
+
2485
+ const replyResourceSpecs = [];
2486
+ const replyTextAttachmentEntries = [];
2487
+ if (Array.isArray(replyFm.media)) {
2488
+ for (const entry of replyFm.media) {
2489
+ if (entry && typeof entry === "object" && entry.kind === "text") {
2490
+ replyTextAttachmentEntries.push(entry);
2491
+ continue;
2492
+ }
2493
+ const spec = await mediaSpecFromJantMedia(entry, sourceRootDir);
2494
+ if (spec) replyResourceSpecs.push(spec);
2495
+ }
2496
+ }
2497
+
2498
+ const replyResourceIds = [];
2499
+ if (!skipMedia && replyResourceSpecs.length > 0) {
2500
+ const result = await uploadBundleResources(
2501
+ replyResourceSpecs,
1660
2502
  target,
1661
- replySlug,
1662
- `${replyLabel} in ${postLabel}`,
1663
- "reply",
1664
2503
  );
2504
+ mediaUploaded += result.uploaded;
2505
+ if (result.urlMap.size > 0) {
2506
+ replyBody = rewriteMediaReferences(replyBody, result.urlMap);
2507
+ }
2508
+ replyResourceIds.push(...result.mediaIds);
1665
2509
  }
1666
- let replyBody = replySegment.body || "";
1667
- const normalizedReplyBody = normalizeImportedBodySegment(replyBody);
1668
- replyBody = normalizedReplyBody.markdown;
1669
- let replyAttachments = [];
1670
2510
 
1671
- if (!skipMedia && !dryRun) {
2511
+ if (!skipMedia) {
1672
2512
  const imageMedia = findImageUrls(replyBody).map((src) => ({ src }));
1673
2513
  const uploadResult = await uploadMediaList(
1674
2514
  imageMedia,
@@ -1677,87 +2517,72 @@ export async function run(argv) {
1677
2517
  sourceRootDir,
1678
2518
  );
1679
2519
  mediaUploaded += uploadResult.uploaded;
1680
-
1681
2520
  if (uploadResult.urlMap.size > 0) {
1682
2521
  replyBody = rewriteMediaReferences(replyBody, uploadResult.urlMap);
1683
2522
  }
1684
2523
  }
1685
- if (!dryRun) {
1686
- const attachmentResult = await buildImportedAttachments(
1687
- normalizedReplyBody.attachments,
1688
- target,
1689
- siteConfig,
1690
- sourceRootDir,
1691
- { skipUploads: skipMedia },
1692
- );
1693
- replyAttachments = attachmentResult.attachments;
1694
- mediaUploaded += attachmentResult.uploaded;
1695
- }
1696
2524
 
1697
- const replyFormat = replyAttrs.format || "note";
1698
- const replyStatus =
1699
- replyAttrs.status === "draft" || replyAttrs.status === "published"
1700
- ? replyAttrs.status
1701
- : "published";
1702
- const replyVisibility =
1703
- replyAttrs.visibility === "unlisted" ||
1704
- replyAttrs.visibility === "private"
1705
- ? replyAttrs.visibility
1706
- : undefined;
1707
- const replyData = {
1708
- format: replyFormat,
1709
- status: replyStatus,
1710
- title:
1711
- replyFormat === "quote"
1712
- ? replyAttrs.source_name || undefined
1713
- : replyAttrs.title || undefined,
1714
- bodyMarkdown: replyBody || undefined,
1715
- replyToId: post.id,
1716
- slug: replySlug,
1717
- visibility: replyVisibility,
1718
- attachments:
1719
- replyAttachments.length > 0 ? replyAttachments : undefined,
1720
- publishedAt: replyAttrs.date
1721
- ? Math.floor(new Date(replyAttrs.date).getTime() / 1000)
1722
- : undefined,
1723
- rating: replyAttrs.rating ? Number(replyAttrs.rating) : undefined,
1724
- };
1725
-
1726
- if (replyFormat === "link" && replyAttrs.url) {
1727
- replyData.url = replyAttrs.url;
2525
+ const attachmentResult = await buildImportedAttachments(
2526
+ normalizedReply.attachments,
2527
+ target,
2528
+ siteConfig,
2529
+ sourceRootDir,
2530
+ { skipUploads: skipMedia },
2531
+ );
2532
+ replyAttachments = attachmentResult.attachments;
2533
+ mediaUploaded += attachmentResult.uploaded;
2534
+ for (const mediaId of replyResourceIds) {
2535
+ replyAttachments.push({ type: "media", mediaId });
1728
2536
  }
1729
- if (replyFormat === "quote" && replyAttrs.quote_text) {
1730
- replyData.quoteText = decodeURIComponent(replyAttrs.quote_text);
1731
- if (replyAttrs.source_url) {
1732
- replyData.url = replyAttrs.source_url;
2537
+
2538
+ if (!skipMedia) {
2539
+ for (const textEntry of replyTextAttachmentEntries) {
2540
+ const textAttachment = await normalizeTextAttachmentSpec(
2541
+ textEntry,
2542
+ siteConfig,
2543
+ sourceRootDir,
2544
+ );
2545
+ if (textAttachment) replyAttachments.push(textAttachment);
1733
2546
  }
1734
2547
  }
1735
2548
 
2549
+ const replyMemberships = resolveCollectionMemberships(
2550
+ replyFm,
2551
+ collectionSlugToId,
2552
+ );
2553
+ const replyData = buildPostPayloadFromBundle(replyBundle, {
2554
+ bodyMarkdown: replyBody,
2555
+ attachments: replyAttachments,
2556
+ memberships: replyMemberships,
2557
+ replyToId: threadTailId,
2558
+ });
2559
+
1736
2560
  try {
1737
- await target.createPost(replyData);
2561
+ const createdReply = await target.createPost(replyData);
1738
2562
  repliesCreated++;
2563
+ if (createdReply?.id) {
2564
+ threadTailId = createdReply.id;
2565
+ }
1739
2566
  } catch (err) {
1740
2567
  console.error(` Error creating reply: ${err.message}`);
1741
2568
  process.exit(1);
1742
2569
  }
1743
2570
  }
1744
2571
 
1745
- // Create exported root aliases after replies. Reply slugs are handled by
1746
- // the thread markers; only true root aliases round-trip back into Jant.
2572
+ // Create exported root aliases after replies. Historical root slugs
2573
+ // round-trip via `root_aliases:`; reply slugs are handled by their
2574
+ // own bundles so we strip them from the alias list.
1747
2575
  const rootTargetSlug = postSlug || post.slug;
1748
- let aliasPaths;
1749
- try {
1750
- aliasPaths = getRootAliasPathsForImport(
1751
- getExportedRootAliases(frontMatter),
1752
- rootTargetSlug,
1753
- replySlugPaths,
1754
- );
1755
- } catch (err) {
1756
- console.error(
1757
- `Error importing aliases for "${postLabel}": ${err.message}`,
1758
- );
1759
- process.exit(1);
1760
- }
2576
+ const aliases = Array.isArray(rootFm.aliases) ? rootFm.aliases : [];
2577
+ const rootAliases = Array.isArray(rootFm.root_aliases)
2578
+ ? rootFm.root_aliases
2579
+ : [];
2580
+ const aliasPaths = getRootAliasPathsForImport(
2581
+ aliases,
2582
+ rootAliases,
2583
+ rootTargetSlug,
2584
+ replySlugPaths,
2585
+ );
1761
2586
  for (const aliasPath of aliasPaths) {
1762
2587
  try {
1763
2588
  await target.createAlias(aliasPath, rootTargetSlug);
@@ -1791,3 +2616,4 @@ export async function run(argv) {
1791
2616
  }
1792
2617
  }
1793
2618
  }
2619
+