@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
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { describe, it, expect, beforeEach, vi } from "vitest";
4
4
  import type { Editor } from "@tiptap/core";
5
+ import { MAX_THREAD_POSTS } from "../../../types.js";
5
6
 
6
7
  import type {
7
8
  ComposeLabels,
@@ -31,6 +32,31 @@ function requireEditor(el: JantComposeEditor): Editor {
31
32
  return editor;
32
33
  }
33
34
 
35
+ function keydown(
36
+ element: globalThis.Element,
37
+ key: string,
38
+ init: globalThis.KeyboardEventInit = {},
39
+ ) {
40
+ element.dispatchEvent(
41
+ new globalThis.KeyboardEvent("keydown", {
42
+ bubbles: true,
43
+ key,
44
+ ...init,
45
+ }),
46
+ );
47
+ }
48
+
49
+ function collectionOptionTitles(root: globalThis.Element): string[] {
50
+ return Array.from(
51
+ root.querySelectorAll<HTMLElement>("[data-popover] [role='option']"),
52
+ ).map(
53
+ (option) =>
54
+ option
55
+ .querySelector(".compose-collection-option-label")
56
+ ?.textContent?.trim() ?? "",
57
+ );
58
+ }
59
+
34
60
  async function flushUpdates(el?: JantComposeDialog) {
35
61
  await Promise.resolve();
36
62
  await Promise.resolve();
@@ -39,6 +65,31 @@ async function flushUpdates(el?: JantComposeDialog) {
39
65
  }
40
66
  }
41
67
 
68
+ function deferred<T>() {
69
+ let resolve!: (value: T) => void;
70
+ const promise = new Promise<T>((res) => {
71
+ resolve = res;
72
+ });
73
+ return { promise, resolve };
74
+ }
75
+
76
+ function mockMatchMedia(matches: boolean) {
77
+ Object.defineProperty(globalThis, "matchMedia", {
78
+ configurable: true,
79
+ writable: true,
80
+ value: vi.fn().mockImplementation((query: string) => ({
81
+ matches,
82
+ media: query,
83
+ onchange: null,
84
+ addEventListener: vi.fn(),
85
+ removeEventListener: vi.fn(),
86
+ addListener: vi.fn(),
87
+ removeListener: vi.fn(),
88
+ dispatchEvent: vi.fn(),
89
+ })),
90
+ });
91
+ }
92
+
42
93
  function mockSlugApi(
43
94
  handler: (url: URL) => { status?: number; body: unknown },
44
95
  ): ReturnType<typeof vi.spyOn> {
@@ -91,6 +142,7 @@ const labels: ComposeLabels = {
91
142
  emoji: "Emoji",
92
143
  title: "Title",
93
144
  fullscreen: "Fullscreen",
145
+ exitFullscreen: "Exit fullscreen",
94
146
  collection: "Collection",
95
147
  searchCollections: "Search...",
96
148
  noCollections: "No collections match that search. Try a different name.",
@@ -103,6 +155,8 @@ const labels: ComposeLabels = {
103
155
  addMore: "Add",
104
156
  removeAttachment: "Remove attachment",
105
157
  uploading: "Uploading...",
158
+ loadingPost: "Loading post...",
159
+ loadPostFailed: "Couldn't load this post. Try again.",
106
160
  published: "Published!",
107
161
  view: "View",
108
162
  retryAll: "Tap to retry",
@@ -144,10 +198,21 @@ const labels: ComposeLabels = {
144
198
  "Doesn't appear in Latest. Still appears in collections you add it to.",
145
199
  publishVisibilityPrivate: "Private",
146
200
  publishVisibilityPrivateHint: "Only visible when signed in.",
201
+ publishDateLabel: "Published on",
202
+ publishDateHint:
203
+ "Leave blank to publish now. Use an earlier date when importing older posts.",
204
+ publishDateReset: "Use current date",
205
+ publishDateInvalid: "Enter a valid date.",
206
+ publishDateFutureError:
207
+ "Choose today or an earlier date, or leave it blank to publish now.",
208
+ publishDateSummaryNow: "Publish now",
209
+ publishDateSummaryAction: "Edit publish date",
147
210
  publishSlugLabel: "Custom link",
148
211
  publishSlugPlaceholder: "your-post-link",
149
212
  publishSlugHint: "Leave blank to generate one automatically.",
150
213
  publishSlugAuto: "Generate automatically",
214
+ publishSlugSummaryAuto: "Auto link",
215
+ publishSlugSummaryAction: "Edit custom link",
151
216
  publishSlugReset: "Reset link",
152
217
  publishSlugSuggested: "Suggested link",
153
218
  publishSlugGenerating: "Generating a link...",
@@ -157,8 +222,12 @@ const labels: ComposeLabels = {
157
222
  publishSlugReserved: "This link is reserved. Choose something else.",
158
223
  postHiddenFromLatest: "Post hidden",
159
224
  postPrivately: "Post privately",
225
+ quietReplyLabel: "Reply quietly",
226
+ quietReplyHint: "Won't move the thread to the top of latest.",
227
+ threadLimitReached: "Threads can include up to 20 posts.",
160
228
  showMore: "Show more",
161
229
  showLess: "Show less",
230
+ newThread: "New Thread",
162
231
  collectionFormLabels: {
163
232
  titleLabel: "Title",
164
233
  titlePlaceholder: "My Collection",
@@ -183,8 +252,8 @@ const labels: ComposeLabels = {
183
252
  };
184
253
 
185
254
  const collections: ComposeCollection[] = [
186
- { id: "col-1", title: "Books" },
187
- { id: "col-2", title: "Movies" },
255
+ { id: "col-1", title: "Books", slug: "books" },
256
+ { id: "col-2", title: "Movies", slug: "movies" },
188
257
  ];
189
258
 
190
259
  async function createElement(
@@ -206,11 +275,33 @@ describe("JantComposeDialog", () => {
206
275
  vi.restoreAllMocks();
207
276
  document.body.innerHTML = "";
208
277
  globalThis.localStorage.clear();
278
+ Object.defineProperty(globalThis, "matchMedia", {
279
+ configurable: true,
280
+ writable: true,
281
+ value: undefined,
282
+ });
283
+ Object.defineProperty(globalThis, "visualViewport", {
284
+ configurable: true,
285
+ writable: true,
286
+ value: undefined,
287
+ });
209
288
  (
210
289
  customElements.get("jant-compose-dialog") as typeof HTMLElement & {
211
290
  _lastNewPostVisibility: string;
212
291
  }
213
292
  )._lastNewPostVisibility = "public";
293
+
294
+ if (!HTMLDialogElement.prototype.showModal) {
295
+ HTMLDialogElement.prototype.showModal = function () {
296
+ this.setAttribute("open", "");
297
+ };
298
+ }
299
+
300
+ if (!HTMLDialogElement.prototype.close) {
301
+ HTMLDialogElement.prototype.close = function () {
302
+ this.removeAttribute("open");
303
+ };
304
+ }
214
305
  });
215
306
 
216
307
  it("renders with collections and labels", async () => {
@@ -246,20 +337,29 @@ describe("JantComposeDialog", () => {
246
337
  ).not.toBeNull();
247
338
  });
248
339
 
249
- it("focuses the main editor body after fullscreen closes", async () => {
340
+ it("restores the editor selection after fullscreen closes", async () => {
250
341
  const el = await createElement();
251
342
  const editor = requireElement(
252
343
  el.querySelector<JantComposeEditor>("jant-compose-editor"),
253
344
  "expected compose editor",
254
345
  );
255
- const focusSpy = vi.spyOn(editor, "focusInput");
346
+ const focusSpy = vi.spyOn(editor, "focusSelection");
256
347
 
257
348
  document.dispatchEvent(
258
349
  new CustomEvent("jant:fullscreen-close", {
259
350
  detail: {
260
- json: null,
351
+ json: {
352
+ type: "doc",
353
+ content: [
354
+ {
355
+ type: "paragraph",
356
+ content: [{ type: "text", text: "Hello" }],
357
+ },
358
+ ],
359
+ },
261
360
  title: "",
262
361
  showTitle: false,
362
+ selection: { from: 3, to: 3 },
263
363
  replyExpanded: false,
264
364
  },
265
365
  }),
@@ -268,7 +368,98 @@ describe("JantComposeDialog", () => {
268
368
  await flushUpdates(el);
269
369
  await flushUpdates(el);
270
370
 
271
- expect(focusSpy).toHaveBeenCalledWith("end");
371
+ expect(focusSpy).toHaveBeenCalledWith({ from: 3, to: 3 });
372
+ });
373
+
374
+ it("opens edit mode without auto-focusing the editor body", async () => {
375
+ vi.spyOn(globalThis, "requestAnimationFrame").mockImplementation((cb) => {
376
+ cb(0);
377
+ return 1;
378
+ });
379
+ vi.spyOn(globalThis, "fetch").mockResolvedValue(
380
+ new Response(
381
+ JSON.stringify({
382
+ id: "pst_123",
383
+ format: "note",
384
+ title: "Long draft",
385
+ body: JSON.stringify({
386
+ type: "doc",
387
+ content: [
388
+ {
389
+ type: "paragraph",
390
+ content: [{ type: "text", text: "Long body text" }],
391
+ },
392
+ ],
393
+ }),
394
+ mediaAttachments: [],
395
+ }),
396
+ {
397
+ headers: { "Content-Type": "application/json" },
398
+ },
399
+ ),
400
+ );
401
+
402
+ const compose = await createElement();
403
+ const editor = requireElement(
404
+ compose.querySelector<JantComposeEditor>("jant-compose-editor"),
405
+ "expected compose editor",
406
+ );
407
+ const shell = requireElement(
408
+ compose.querySelector<HTMLElement>(".compose-dialog-inner"),
409
+ "expected compose dialog shell",
410
+ );
411
+ const shellFocusSpy = vi.spyOn(shell, "focus");
412
+ const focusSpy = vi.spyOn(editor, "focusInput");
413
+
414
+ await compose.openEdit("pst_123");
415
+ await flushUpdates(compose);
416
+ await flushUpdates(compose);
417
+
418
+ expect(focusSpy).not.toHaveBeenCalled();
419
+ expect(shellFocusSpy).toHaveBeenCalled();
420
+ });
421
+
422
+ it("shows a loading state while edit data is still loading", async () => {
423
+ const postResponse = deferred<Response>();
424
+ vi.spyOn(globalThis, "fetch").mockImplementation(
425
+ () => postResponse.promise,
426
+ );
427
+
428
+ const compose = await createElement();
429
+ const opening = compose.openEdit("pst_123");
430
+ await flushUpdates(compose);
431
+
432
+ expect(compose.textContent).toContain("Loading post...");
433
+ expect(compose.querySelector("jant-compose-editor")).toBeNull();
434
+
435
+ postResponse.resolve(
436
+ new Response(
437
+ JSON.stringify({
438
+ id: "pst_123",
439
+ format: "note",
440
+ title: "Loaded later",
441
+ body: JSON.stringify({
442
+ type: "doc",
443
+ content: [
444
+ {
445
+ type: "paragraph",
446
+ content: [{ type: "text", text: "Loaded body text" }],
447
+ },
448
+ ],
449
+ }),
450
+ mediaAttachments: [],
451
+ }),
452
+ {
453
+ headers: { "Content-Type": "application/json" },
454
+ },
455
+ ),
456
+ );
457
+
458
+ await opening;
459
+ await flushUpdates(compose);
460
+
461
+ expect(compose.textContent).not.toContain("Loading post...");
462
+ expect(compose.querySelector("jant-compose-editor")).not.toBeNull();
272
463
  });
273
464
 
274
465
  it("opens publish settings even when publish is disabled", async () => {
@@ -310,7 +501,7 @@ describe("JantComposeDialog", () => {
310
501
  expect(draftButton.getAttribute("aria-label")).toBe("Save as draft");
311
502
  });
312
503
 
313
- it("shows visibility hints in the publish settings panel", async () => {
504
+ it("shows the selected visibility hint in the publish settings panel", async () => {
314
505
  const el = await createElement();
315
506
 
316
507
  requireElement(
@@ -319,10 +510,19 @@ describe("JantComposeDialog", () => {
319
510
  ).click();
320
511
  await el.updateComplete;
321
512
 
322
- expect(el.textContent).toContain(
323
- "Doesn't appear in Latest. Still appears in collections you add it to.",
513
+ // Default is public — only public hint shown
514
+ expect(el.textContent).toContain("Appears in Latest.");
515
+ expect(el.textContent).not.toContain("Only visible when signed in.");
516
+
517
+ // Switch to private — hint updates
518
+ const chips = el.querySelectorAll<HTMLButtonElement>(
519
+ ".compose-publish-chip[role='radio']",
324
520
  );
521
+ chips[2]?.click();
522
+ await el.updateComplete;
523
+
325
524
  expect(el.textContent).toContain("Only visible when signed in.");
525
+ expect(el.textContent).not.toContain("Appears in Latest.");
326
526
  });
327
527
 
328
528
  it("format switching updates active state", async () => {
@@ -355,6 +555,49 @@ describe("JantComposeDialog", () => {
355
555
  ).toBeNull();
356
556
  });
357
557
 
558
+ it("autofocuses the primary field on desktop format switches", async () => {
559
+ vi.spyOn(globalThis, "requestAnimationFrame").mockImplementation((cb) => {
560
+ cb(0);
561
+ return 1;
562
+ });
563
+ const el = await createElement();
564
+ const editor = requireElement(
565
+ el.querySelector<JantComposeEditor>("jant-compose-editor"),
566
+ "expected compose editor",
567
+ );
568
+ const focusSpy = vi.spyOn(editor, "focusInput");
569
+
570
+ const linkBtn = el.querySelectorAll<HTMLButtonElement>(
571
+ ".compose-segmented-item",
572
+ )[1];
573
+ linkBtn.click();
574
+ await flushUpdates(el);
575
+
576
+ expect(focusSpy).toHaveBeenCalledTimes(1);
577
+ });
578
+
579
+ it("does not autofocus the primary field on touch format switches", async () => {
580
+ mockMatchMedia(true);
581
+ vi.spyOn(globalThis, "requestAnimationFrame").mockImplementation((cb) => {
582
+ cb(0);
583
+ return 1;
584
+ });
585
+ const el = await createElement();
586
+ const editor = requireElement(
587
+ el.querySelector<JantComposeEditor>("jant-compose-editor"),
588
+ "expected compose editor",
589
+ );
590
+ const focusSpy = vi.spyOn(editor, "focusInput");
591
+
592
+ const linkBtn = el.querySelectorAll<HTMLButtonElement>(
593
+ ".compose-segmented-item",
594
+ )[1];
595
+ linkBtn.click();
596
+ await flushUpdates(el);
597
+
598
+ expect(focusSpy).not.toHaveBeenCalled();
599
+ });
600
+
358
601
  it("submit dispatches jant:compose-submit-deferred with correct payload", async () => {
359
602
  const el = await createElement();
360
603
  const editor = requireElement(
@@ -457,7 +700,7 @@ describe("JantComposeDialog", () => {
457
700
  await el.updateComplete;
458
701
 
459
702
  const options = el.querySelectorAll<HTMLButtonElement>(
460
- ".compose-publish-option[role='radio']",
703
+ ".compose-publish-chip[role='radio']",
461
704
  );
462
705
  expect(options).toHaveLength(3);
463
706
  options[1]?.click();
@@ -489,6 +732,344 @@ describe("JantComposeDialog", () => {
489
732
  ).toBeUndefined();
490
733
  });
491
734
 
735
+ it("includes a past publish date in the submit payload", async () => {
736
+ vi.useFakeTimers();
737
+ vi.setSystemTime(new Date("2026-03-28T12:34:00Z"));
738
+
739
+ try {
740
+ const el = await createElement();
741
+ const editor = requireElement(
742
+ el.querySelector<JantComposeEditor>("jant-compose-editor"),
743
+ "expected compose editor",
744
+ );
745
+ editor._bodyJson = {
746
+ type: "doc",
747
+ content: [
748
+ {
749
+ type: "paragraph",
750
+ content: [{ type: "text", text: "Backdated post" }],
751
+ },
752
+ ],
753
+ };
754
+ await editor.updateComplete;
755
+
756
+ requireElement(
757
+ el.querySelector<HTMLButtonElement>(".compose-publish-toggle"),
758
+ "expected publish settings toggle",
759
+ ).click();
760
+ await el.updateComplete;
761
+
762
+ const publishedAtInput = requireElement(
763
+ el.querySelector<HTMLInputElement>(".compose-publish-date-input"),
764
+ "expected publish date input",
765
+ );
766
+ publishedAtInput.value = "2024-01-15";
767
+ publishedAtInput.dispatchEvent(new Event("input", { bubbles: true }));
768
+ await el.updateComplete;
769
+
770
+ let receivedDetail: ComposeSubmitDetail | null = null;
771
+ el.addEventListener("jant:compose-submit-deferred", (event) => {
772
+ receivedDetail = (event as CustomEvent<ComposeSubmitDetail>).detail;
773
+ });
774
+
775
+ requireElement(
776
+ el.querySelector<HTMLButtonElement>(".compose-publish-main"),
777
+ "expected publish button",
778
+ ).click();
779
+
780
+ expect(receivedDetail).not.toBeNull();
781
+ const now = new Date();
782
+ expect(
783
+ (receivedDetail as unknown as ComposeSubmitDetail).publishedAt,
784
+ ).toBe(
785
+ Math.floor(
786
+ new Date(
787
+ 2024,
788
+ 0,
789
+ 15,
790
+ now.getHours(),
791
+ now.getMinutes(),
792
+ 0,
793
+ 0,
794
+ ).getTime() / 1000,
795
+ ),
796
+ );
797
+ } finally {
798
+ vi.useRealTimers();
799
+ }
800
+ });
801
+
802
+ it("shows a publish date summary near the submit controls after the date changes", async () => {
803
+ const el = await createElement();
804
+
805
+ const publishToggle = requireElement(
806
+ el.querySelector<HTMLButtonElement>(".compose-publish-toggle"),
807
+ "expected publish settings toggle",
808
+ );
809
+ publishToggle.click();
810
+ await el.updateComplete;
811
+
812
+ const publishedAtInput = requireElement(
813
+ el.querySelector<HTMLInputElement>(".compose-publish-date-input"),
814
+ "expected publish date input",
815
+ );
816
+ publishedAtInput.value = "2024-01-15";
817
+ publishedAtInput.dispatchEvent(new Event("input", { bubbles: true }));
818
+ await el.updateComplete;
819
+
820
+ publishToggle.click();
821
+ await el.updateComplete;
822
+
823
+ const summary = requireElement(
824
+ el.querySelector<HTMLButtonElement>(
825
+ "[data-compose-publish-date-summary]",
826
+ ),
827
+ "expected publish date summary",
828
+ );
829
+ expect(summary.dataset.publishDate).toBe("2024-01-15");
830
+ expect(summary.textContent).toContain("2024");
831
+
832
+ summary.click();
833
+ await el.updateComplete;
834
+ await flushUpdates(el);
835
+
836
+ expect(
837
+ requireElement(
838
+ el.querySelector<HTMLInputElement>(".compose-publish-date-input"),
839
+ "expected publish date input after reopening publish settings",
840
+ ),
841
+ ).toBe(document.activeElement);
842
+ });
843
+
844
+ it("does not show a publish date summary for an unchanged date while editing a post", async () => {
845
+ const el = await createElement();
846
+ (
847
+ el as unknown as {
848
+ _editPostId: string | null;
849
+ _initialPublishedAtInput: string;
850
+ _publishedAtInput: string;
851
+ }
852
+ )._editPostId = "pst_existing";
853
+ (
854
+ el as unknown as {
855
+ _editPostId: string | null;
856
+ _initialPublishedAtInput: string;
857
+ _publishedAtInput: string;
858
+ }
859
+ )._initialPublishedAtInput = "2024-01-15";
860
+ (
861
+ el as unknown as {
862
+ _editPostId: string | null;
863
+ _initialPublishedAtInput: string;
864
+ _publishedAtInput: string;
865
+ }
866
+ )._publishedAtInput = "2024-01-15";
867
+ el.requestUpdate();
868
+ await el.updateComplete;
869
+
870
+ expect(el.querySelector("[data-compose-publish-date-summary]")).toBeNull();
871
+ });
872
+
873
+ it("shows a publish now summary after clearing the publish date while editing a post", async () => {
874
+ const el = await createElement();
875
+ (
876
+ el as unknown as {
877
+ _editPostId: string | null;
878
+ _initialPublishedAtInput: string;
879
+ _publishedAtInput: string;
880
+ }
881
+ )._editPostId = "pst_existing";
882
+ (
883
+ el as unknown as {
884
+ _editPostId: string | null;
885
+ _initialPublishedAtInput: string;
886
+ _publishedAtInput: string;
887
+ }
888
+ )._initialPublishedAtInput = "2024-01-15";
889
+ (
890
+ el as unknown as {
891
+ _editPostId: string | null;
892
+ _initialPublishedAtInput: string;
893
+ _publishedAtInput: string;
894
+ }
895
+ )._publishedAtInput = "";
896
+ el.requestUpdate();
897
+ await el.updateComplete;
898
+
899
+ expect(
900
+ requireElement(
901
+ el.querySelector<HTMLElement>("[data-compose-publish-date-summary]"),
902
+ "expected publish now summary",
903
+ ).textContent,
904
+ ).toContain("Publish now");
905
+ });
906
+
907
+ it("shows a custom link summary near the submit controls after the link changes", async () => {
908
+ mockSlugApi((url) => {
909
+ if (url.searchParams.get("mode") === "suggest") {
910
+ return { body: { slug: "configured-post" } };
911
+ }
912
+ if (url.searchParams.get("mode") === "check") {
913
+ return { body: { slug: "reading-notes", available: true } };
914
+ }
915
+ throw new Error(`Unexpected slug mode: ${url.search}`);
916
+ });
917
+
918
+ const el = await createElement();
919
+ const publishToggle = requireElement(
920
+ el.querySelector<HTMLButtonElement>(".compose-publish-toggle"),
921
+ "expected publish settings toggle",
922
+ );
923
+
924
+ publishToggle.click();
925
+ await el.updateComplete;
926
+ await flushUpdates(el);
927
+
928
+ const slugInput = requireElement(
929
+ el.querySelector<HTMLInputElement>(".compose-publish-slug-input"),
930
+ "expected custom link input",
931
+ );
932
+ slugInput.value = "reading-notes";
933
+ slugInput.dispatchEvent(new Event("input", { bubbles: true }));
934
+ await new Promise((resolve) => setTimeout(resolve, 300));
935
+ await el.updateComplete;
936
+
937
+ publishToggle.click();
938
+ await el.updateComplete;
939
+
940
+ const summary = requireElement(
941
+ el.querySelector<HTMLButtonElement>(
942
+ "[data-compose-publish-slug-summary]",
943
+ ),
944
+ "expected custom link summary",
945
+ );
946
+ expect(summary.dataset.publishSlug).toBe("reading-notes");
947
+ expect(summary.textContent).toContain("/reading-notes");
948
+
949
+ summary.click();
950
+ await el.updateComplete;
951
+ await flushUpdates(el);
952
+
953
+ expect(
954
+ requireElement(
955
+ el.querySelector<HTMLInputElement>(".compose-publish-slug-input"),
956
+ "expected custom link input after reopening publish settings",
957
+ ),
958
+ ).toBe(document.activeElement);
959
+ });
960
+
961
+ it("does not show a custom link summary for an unchanged link while editing a post", async () => {
962
+ const el = await createElement();
963
+ (
964
+ el as unknown as {
965
+ _editPostId: string | null;
966
+ _initialSlug: string;
967
+ _slug: string;
968
+ }
969
+ )._editPostId = "pst_existing";
970
+ (
971
+ el as unknown as {
972
+ _editPostId: string | null;
973
+ _initialSlug: string;
974
+ _slug: string;
975
+ }
976
+ )._initialSlug = "reading-notes";
977
+ (
978
+ el as unknown as {
979
+ _editPostId: string | null;
980
+ _initialSlug: string;
981
+ _slug: string;
982
+ }
983
+ )._slug = "reading-notes";
984
+ el.requestUpdate();
985
+ await el.updateComplete;
986
+
987
+ expect(el.querySelector("[data-compose-publish-slug-summary]")).toBeNull();
988
+ });
989
+
990
+ it("shows an auto link summary after clearing the custom link while editing a post", async () => {
991
+ const el = await createElement();
992
+ (
993
+ el as unknown as {
994
+ _editPostId: string | null;
995
+ _initialSlug: string;
996
+ _slug: string;
997
+ }
998
+ )._editPostId = "pst_existing";
999
+ (
1000
+ el as unknown as {
1001
+ _editPostId: string | null;
1002
+ _initialSlug: string;
1003
+ _slug: string;
1004
+ }
1005
+ )._initialSlug = "reading-notes";
1006
+ (
1007
+ el as unknown as {
1008
+ _editPostId: string | null;
1009
+ _initialSlug: string;
1010
+ _slug: string;
1011
+ }
1012
+ )._slug = "";
1013
+ el.requestUpdate();
1014
+ await el.updateComplete;
1015
+
1016
+ expect(
1017
+ requireElement(
1018
+ el.querySelector<HTMLElement>("[data-compose-publish-slug-summary]"),
1019
+ "expected auto link summary",
1020
+ ).textContent,
1021
+ ).toContain("Auto link");
1022
+ });
1023
+
1024
+ it("blocks publishing with a future publish date", async () => {
1025
+ vi.useFakeTimers();
1026
+ vi.setSystemTime(new Date("2026-03-28T12:34:00Z"));
1027
+
1028
+ try {
1029
+ const el = await createElement();
1030
+ const editor = requireElement(
1031
+ el.querySelector<JantComposeEditor>("jant-compose-editor"),
1032
+ "expected compose editor",
1033
+ );
1034
+ editor._bodyJson = {
1035
+ type: "doc",
1036
+ content: [
1037
+ {
1038
+ type: "paragraph",
1039
+ content: [{ type: "text", text: "Future post" }],
1040
+ },
1041
+ ],
1042
+ };
1043
+ await editor.updateComplete;
1044
+
1045
+ requireElement(
1046
+ el.querySelector<HTMLButtonElement>(".compose-publish-toggle"),
1047
+ "expected publish settings toggle",
1048
+ ).click();
1049
+ await el.updateComplete;
1050
+
1051
+ const publishedAtInput = requireElement(
1052
+ el.querySelector<HTMLInputElement>(".compose-publish-date-input"),
1053
+ "expected publish date input",
1054
+ );
1055
+ publishedAtInput.value = "2099-01-01";
1056
+ publishedAtInput.dispatchEvent(new Event("input", { bubbles: true }));
1057
+ await el.updateComplete;
1058
+
1059
+ expect(
1060
+ requireElement(
1061
+ el.querySelector<HTMLButtonElement>(".compose-publish-main"),
1062
+ "expected publish button",
1063
+ ).disabled,
1064
+ ).toBe(true);
1065
+ expect(el.textContent).toContain(
1066
+ "Choose today or an earlier date, or leave it blank to publish now.",
1067
+ );
1068
+ } finally {
1069
+ vi.useRealTimers();
1070
+ }
1071
+ });
1072
+
492
1073
  it("updates the publish button label for private visibility", async () => {
493
1074
  const el = await createElement();
494
1075
 
@@ -499,7 +1080,7 @@ describe("JantComposeDialog", () => {
499
1080
  await el.updateComplete;
500
1081
 
501
1082
  const options = el.querySelectorAll<HTMLButtonElement>(
502
- ".compose-publish-option[role='radio']",
1083
+ ".compose-publish-chip[role='radio']",
503
1084
  );
504
1085
  expect(options).toHaveLength(3);
505
1086
  options[2]?.click();
@@ -523,7 +1104,7 @@ describe("JantComposeDialog", () => {
523
1104
  await el.updateComplete;
524
1105
 
525
1106
  const options = el.querySelectorAll<HTMLButtonElement>(
526
- ".compose-publish-option[role='radio']",
1107
+ ".compose-publish-chip[role='radio']",
527
1108
  );
528
1109
  expect(options).toHaveLength(3);
529
1110
  options[1]?.click();
@@ -553,7 +1134,7 @@ describe("JantComposeDialog", () => {
553
1134
  await el.updateComplete;
554
1135
 
555
1136
  const options = el.querySelectorAll<HTMLButtonElement>(
556
- ".compose-publish-option[role='radio']",
1137
+ ".compose-publish-chip[role='radio']",
557
1138
  );
558
1139
  options[1]?.click();
559
1140
  await el.updateComplete;
@@ -709,6 +1290,165 @@ describe("JantComposeDialog", () => {
709
1290
  expect(el.querySelector(".compose-publish-slug-input")).not.toBeNull();
710
1291
  });
711
1292
 
1293
+ it("keeps the desktop publish settings panel attached to the publish button when space below is tight", async () => {
1294
+ const el = await createElement();
1295
+ Object.defineProperty(globalThis, "visualViewport", {
1296
+ configurable: true,
1297
+ writable: true,
1298
+ value: {
1299
+ offsetLeft: 0,
1300
+ offsetTop: 0,
1301
+ width: 500,
1302
+ height: 420,
1303
+ addEventListener: () => {},
1304
+ removeEventListener: () => {},
1305
+ },
1306
+ });
1307
+ (
1308
+ el as unknown as {
1309
+ _dialogEl: Pick<
1310
+ HTMLDialogElement,
1311
+ "addEventListener" | "getBoundingClientRect" | "removeEventListener"
1312
+ > | null;
1313
+ }
1314
+ )._dialogEl = {
1315
+ addEventListener: () => {},
1316
+ getBoundingClientRect: () =>
1317
+ ({
1318
+ x: 0,
1319
+ y: 80,
1320
+ width: 480,
1321
+ height: 340,
1322
+ top: 80,
1323
+ right: 480,
1324
+ bottom: 420,
1325
+ left: 0,
1326
+ toJSON: () => ({}),
1327
+ }) as never,
1328
+ removeEventListener: () => {},
1329
+ };
1330
+
1331
+ const publishGroup = requireElement(
1332
+ el.querySelector<HTMLElement>(".compose-publish-group"),
1333
+ "expected publish button group",
1334
+ );
1335
+ vi.spyOn(publishGroup, "getBoundingClientRect").mockReturnValue({
1336
+ x: 320,
1337
+ y: 340,
1338
+ width: 120,
1339
+ height: 40,
1340
+ top: 340,
1341
+ right: 440,
1342
+ bottom: 380,
1343
+ left: 320,
1344
+ toJSON: () => ({}),
1345
+ } as never);
1346
+
1347
+ requireElement(
1348
+ el.querySelector<HTMLButtonElement>(".compose-publish-toggle"),
1349
+ "expected publish settings toggle",
1350
+ ).click();
1351
+ await el.updateComplete;
1352
+
1353
+ const panel = requireElement(
1354
+ el.querySelector<HTMLElement>("[data-compose-publish-panel-desktop]"),
1355
+ "expected publish settings panel",
1356
+ );
1357
+ expect(panel.dataset.position).toBe("up");
1358
+ expect(publishGroup.contains(panel)).toBe(true);
1359
+ expect(
1360
+ panel.style.getPropertyValue("--compose-publish-panel-max-height"),
1361
+ ).toBe("318px");
1362
+ });
1363
+
1364
+ it("opens the desktop publish settings panel below the publish button when the viewport has room", async () => {
1365
+ const el = await createElement();
1366
+ Object.defineProperty(globalThis, "visualViewport", {
1367
+ configurable: true,
1368
+ writable: true,
1369
+ value: {
1370
+ offsetLeft: 0,
1371
+ offsetTop: 0,
1372
+ width: 500,
1373
+ height: 420,
1374
+ addEventListener: () => {},
1375
+ removeEventListener: () => {},
1376
+ },
1377
+ });
1378
+ (
1379
+ el as unknown as {
1380
+ _dialogEl: Pick<
1381
+ HTMLDialogElement,
1382
+ "addEventListener" | "getBoundingClientRect" | "removeEventListener"
1383
+ > | null;
1384
+ }
1385
+ )._dialogEl = {
1386
+ addEventListener: () => {},
1387
+ getBoundingClientRect: () =>
1388
+ ({
1389
+ x: 0,
1390
+ y: 80,
1391
+ width: 480,
1392
+ height: 340,
1393
+ top: 80,
1394
+ right: 480,
1395
+ bottom: 420,
1396
+ left: 0,
1397
+ toJSON: () => ({}),
1398
+ }) as never,
1399
+ removeEventListener: () => {},
1400
+ };
1401
+
1402
+ const publishGroup = requireElement(
1403
+ el.querySelector<HTMLElement>(".compose-publish-group"),
1404
+ "expected publish button group",
1405
+ );
1406
+ vi.spyOn(publishGroup, "getBoundingClientRect").mockReturnValue({
1407
+ x: 320,
1408
+ y: 130,
1409
+ width: 120,
1410
+ height: 40,
1411
+ top: 130,
1412
+ right: 440,
1413
+ bottom: 170,
1414
+ left: 320,
1415
+ toJSON: () => ({}),
1416
+ } as never);
1417
+
1418
+ requireElement(
1419
+ el.querySelector<HTMLButtonElement>(".compose-publish-toggle"),
1420
+ "expected publish settings toggle",
1421
+ ).click();
1422
+ await el.updateComplete;
1423
+
1424
+ const panel = requireElement(
1425
+ el.querySelector<HTMLElement>("[data-compose-publish-panel-desktop]"),
1426
+ "expected publish settings panel",
1427
+ );
1428
+ expect(panel.dataset.position).toBe("down");
1429
+ expect(publishGroup.contains(panel)).toBe(true);
1430
+ expect(
1431
+ panel.style.getPropertyValue("--compose-publish-panel-max-height"),
1432
+ ).toBe("228px");
1433
+ });
1434
+
1435
+ it("renders publish settings as a fullscreen subview on compact viewports", async () => {
1436
+ mockMatchMedia(true);
1437
+
1438
+ const el = await createElement();
1439
+
1440
+ requireElement(
1441
+ el.querySelector<HTMLButtonElement>(".compose-publish-toggle"),
1442
+ "expected publish settings toggle",
1443
+ ).click();
1444
+ await el.updateComplete;
1445
+
1446
+ expect(el.querySelector("[data-compose-publish-panel-desktop]")).toBeNull();
1447
+ expect(
1448
+ el.querySelector("[data-compose-publish-panel-mobile]"),
1449
+ ).not.toBeNull();
1450
+ });
1451
+
712
1452
  it("shows a slug error and blocks publish when the custom link is invalid", async () => {
713
1453
  mockSlugApi(() => ({ body: { slug: "hello-world" } }));
714
1454
 
@@ -831,9 +1571,11 @@ describe("JantComposeDialog", () => {
831
1571
  );
832
1572
  expect(panel.textContent).not.toContain("Publish settings");
833
1573
  expect(panel.textContent).toContain("Visibility");
1574
+ expect(panel.textContent).toContain("Published on");
834
1575
  expect(panel.textContent).toContain("Custom link");
835
1576
  expect(panel.textContent).not.toContain("Save as draft");
836
1577
  expect(panel.textContent).not.toContain("Discard");
1578
+ expect(panel.querySelector(".compose-publish-date-input")).not.toBeNull();
837
1579
  expect(panel.querySelector(".compose-publish-slug-input")).not.toBeNull();
838
1580
  });
839
1581
 
@@ -1113,25 +1855,143 @@ describe("JantComposeDialog", () => {
1113
1855
  slugInput.dispatchEvent(new Event("input", { bubbles: true }));
1114
1856
  await el.updateComplete;
1115
1857
 
1116
- expect(
1117
- el.querySelector("[data-compose-slug-preview]")?.textContent?.trim(),
1118
- ).toBe(`${globalThis.location.origin}/final-link`);
1858
+ expect(
1859
+ el.querySelector("[data-compose-slug-preview]")?.textContent?.trim(),
1860
+ ).toBe(`${globalThis.location.origin}/final-link`);
1861
+ });
1862
+
1863
+ it("includes the thread root id when replying", async () => {
1864
+ const el = await createElement();
1865
+ await el.openReply(
1866
+ "019ce8ce-d6d8-7fda-a5df-c2da2bef5ade",
1867
+ {
1868
+ contentHtml: "<p>Parent</p>",
1869
+ dateText: "Mar 14",
1870
+ },
1871
+ "019ce8cf-19a1-7d16-9a75-017a9ac7299d",
1872
+ {
1873
+ kind: "timeline-item",
1874
+ id: "019ce8cf-19a1-7d16-9a75-017a9ac7299d",
1875
+ },
1876
+ );
1877
+
1878
+ const editor = requireElement(
1879
+ el.querySelector<JantComposeEditor>("jant-compose-editor"),
1880
+ "expected compose editor",
1881
+ );
1882
+ editor._bodyJson = {
1883
+ type: "doc",
1884
+ content: [
1885
+ { type: "paragraph", content: [{ type: "text", text: "Reply body" }] },
1886
+ ],
1887
+ };
1888
+ await editor.updateComplete;
1889
+ await el.updateComplete;
1890
+
1891
+ let receivedDetail:
1892
+ | (ComposeSubmitDetail & { pendingAttachments: unknown[] })
1893
+ | null = null;
1894
+ el.addEventListener("jant:compose-submit-deferred", (event) => {
1895
+ const customEvent = event as CustomEvent<
1896
+ ComposeSubmitDetail & { pendingAttachments: unknown[] }
1897
+ >;
1898
+ receivedDetail = customEvent.detail;
1899
+ });
1900
+
1901
+ requireElement(
1902
+ el.querySelector<HTMLButtonElement>(".compose-publish-main"),
1903
+ "expected reply button",
1904
+ ).click();
1905
+
1906
+ expect(receivedDetail).not.toBeNull();
1907
+ const detail = receivedDetail as unknown as ComposeSubmitDetail & {
1908
+ pendingAttachments: unknown[];
1909
+ };
1910
+ expect(detail.replyToId).toBe("019ce8ce-d6d8-7fda-a5df-c2da2bef5ade");
1911
+ expect(detail.replyThreadRootId).toBe(
1912
+ "019ce8cf-19a1-7d16-9a75-017a9ac7299d",
1913
+ );
1914
+ expect(detail.replyRefreshKind).toBe("timeline-item");
1915
+ expect(detail.replyRefreshId).toBe("019ce8cf-19a1-7d16-9a75-017a9ac7299d");
1916
+ });
1917
+
1918
+ it("uses the replied post format as the initial compose format", async () => {
1919
+ const el = await createElement();
1920
+
1921
+ await el.openReply(
1922
+ "019ce8ce-d6d8-7fda-a5df-c2da2bef5ade",
1923
+ {
1924
+ contentHtml: "<p>Parent</p>",
1925
+ dateText: "Mar 14",
1926
+ },
1927
+ undefined,
1928
+ undefined,
1929
+ { initialFormat: "quote" },
1930
+ );
1931
+ await flushUpdates(el);
1932
+
1933
+ expect(el._format).toBe("quote");
1934
+ });
1935
+
1936
+ it("restores a matching local reply draft when reopening reply compose", async () => {
1937
+ const el = await createElement();
1938
+ const replyToId = "019ce8ce-d6d8-7fda-a5df-c2da2bef5ade";
1939
+ vi.spyOn(globalThis, "fetch").mockResolvedValue(
1940
+ new Response(null, { status: 404 }),
1941
+ );
1942
+
1943
+ globalThis.localStorage.setItem(
1944
+ "jant:compose-draft",
1945
+ JSON.stringify({
1946
+ format: "note",
1947
+ title: "",
1948
+ bodyJson: {
1949
+ type: "doc",
1950
+ content: [
1951
+ {
1952
+ type: "paragraph",
1953
+ content: [{ type: "text", text: "Recovered reply draft" }],
1954
+ },
1955
+ ],
1956
+ },
1957
+ url: "",
1958
+ quoteText: "",
1959
+ quoteAuthor: "",
1960
+ slug: "",
1961
+ visibility: "public",
1962
+ rating: 0,
1963
+ showTitle: false,
1964
+ showRating: false,
1965
+ collectionIds: [],
1966
+ replyToId,
1967
+ attachedTexts: [],
1968
+ attachmentOrder: [],
1969
+ savedAt: Date.now(),
1970
+ }),
1971
+ );
1972
+
1973
+ await el.openReply(replyToId, {
1974
+ contentHtml: "<p>Parent</p>",
1975
+ dateText: "Mar 14",
1976
+ });
1977
+ await flushUpdates(el);
1978
+
1979
+ const editor = requireElement(
1980
+ el.querySelector<JantComposeEditor>("jant-compose-editor"),
1981
+ "expected compose editor",
1982
+ );
1983
+ expect(el._replyToId).toBe(replyToId);
1984
+ expect(editor.getData().body).toContain("Recovered reply draft");
1119
1985
  });
1120
1986
 
1121
- it("includes the thread root id when replying", async () => {
1987
+ it("keeps the local reply draft when submit is dispatched", async () => {
1122
1988
  const el = await createElement();
1123
- await el.openReply(
1124
- "019ce8ce-d6d8-7fda-a5df-c2da2bef5ade",
1125
- {
1126
- contentHtml: "<p>Parent</p>",
1127
- dateText: "Mar 14",
1128
- },
1129
- "019ce8cf-19a1-7d16-9a75-017a9ac7299d",
1130
- {
1131
- kind: "timeline-item",
1132
- id: "019ce8cf-19a1-7d16-9a75-017a9ac7299d",
1133
- },
1134
- );
1989
+ const replyToId = "019ce8ce-d6d8-7fda-a5df-c2da2bef5ade";
1990
+
1991
+ await el.openReply(replyToId, {
1992
+ contentHtml: "<p>Parent</p>",
1993
+ dateText: "Mar 14",
1994
+ });
1135
1995
 
1136
1996
  const editor = requireElement(
1137
1997
  el.querySelector<JantComposeEditor>("jant-compose-editor"),
@@ -1140,37 +2000,49 @@ describe("JantComposeDialog", () => {
1140
2000
  editor._bodyJson = {
1141
2001
  type: "doc",
1142
2002
  content: [
1143
- { type: "paragraph", content: [{ type: "text", text: "Reply body" }] },
2003
+ {
2004
+ type: "paragraph",
2005
+ content: [{ type: "text", text: "Persist me before submit" }],
2006
+ },
1144
2007
  ],
1145
2008
  };
1146
2009
  await editor.updateComplete;
1147
2010
  await el.updateComplete;
1148
2011
 
1149
- let receivedDetail:
1150
- | (ComposeSubmitDetail & { pendingAttachments: unknown[] })
1151
- | null = null;
1152
- el.addEventListener("jant:compose-submit-deferred", (event) => {
1153
- const customEvent = event as CustomEvent<
1154
- ComposeSubmitDetail & { pendingAttachments: unknown[] }
1155
- >;
1156
- receivedDetail = customEvent.detail;
1157
- });
1158
-
1159
2012
  requireElement(
1160
2013
  el.querySelector<HTMLButtonElement>(".compose-publish-main"),
1161
2014
  "expected reply button",
1162
2015
  ).click();
1163
2016
 
1164
- expect(receivedDetail).not.toBeNull();
1165
- const detail = receivedDetail as unknown as ComposeSubmitDetail & {
1166
- pendingAttachments: unknown[];
2017
+ const savedDraftRaw = globalThis.localStorage.getItem("jant:compose-draft");
2018
+ if (!savedDraftRaw) {
2019
+ throw new Error("expected local compose draft");
2020
+ }
2021
+ const savedDraft = JSON.parse(savedDraftRaw) as {
2022
+ replyToId: string;
2023
+ bodyJson: { content: Array<{ content: Array<{ text: string }> }> };
1167
2024
  };
1168
- expect(detail.replyToId).toBe("019ce8ce-d6d8-7fda-a5df-c2da2bef5ade");
1169
- expect(detail.replyThreadRootId).toBe(
1170
- "019ce8cf-19a1-7d16-9a75-017a9ac7299d",
2025
+
2026
+ expect(savedDraft.replyToId).toBe(replyToId);
2027
+ expect(savedDraft.bodyJson.content[0]?.content[0]?.text).toBe(
2028
+ "Persist me before submit",
1171
2029
  );
1172
- expect(detail.replyRefreshKind).toBe("timeline-item");
1173
- expect(detail.replyRefreshId).toBe("019ce8cf-19a1-7d16-9a75-017a9ac7299d");
2030
+ });
2031
+
2032
+ it("does not add more thread items than the shared limit", async () => {
2033
+ const el = await createElement();
2034
+ el._threadItems = Array.from({ length: MAX_THREAD_POSTS }, (_, index) => ({
2035
+ id: `thread-${index}`,
2036
+ format: "note",
2037
+ }));
2038
+
2039
+ (
2040
+ el as unknown as {
2041
+ _addThreadItem: () => void;
2042
+ }
2043
+ )._addThreadItem();
2044
+
2045
+ expect(el._threadItems).toHaveLength(MAX_THREAD_POSTS);
1174
2046
  });
1175
2047
 
1176
2048
  it("omits visibility from locked edit submissions", async () => {
@@ -1252,6 +2124,238 @@ describe("JantComposeDialog", () => {
1252
2124
  expect(el._collectionIds).toEqual(["col-2"]);
1253
2125
  });
1254
2126
 
2127
+ it("moves collection focus with arrow keys and toggles with Space", async () => {
2128
+ vi.spyOn(globalThis, "requestAnimationFrame").mockImplementation((cb) => {
2129
+ cb(0);
2130
+ return 1;
2131
+ });
2132
+ const el = await createElement();
2133
+
2134
+ requireElement(
2135
+ el.querySelector<HTMLButtonElement>(".compose-collection-trigger"),
2136
+ "expected collection trigger",
2137
+ ).click();
2138
+ await flushUpdates(el);
2139
+
2140
+ const searchInput = requireElement(
2141
+ el.querySelector<HTMLInputElement>(".compose-collection-search-input"),
2142
+ "expected collection search input",
2143
+ );
2144
+ expect(document.activeElement).toBe(searchInput);
2145
+
2146
+ keydown(searchInput, "ArrowDown");
2147
+ await flushUpdates(el);
2148
+
2149
+ let options = Array.from(
2150
+ el.querySelectorAll<HTMLButtonElement>("[data-popover] [role='option']"),
2151
+ );
2152
+ expect(document.activeElement).toBe(options[0]);
2153
+
2154
+ keydown(
2155
+ requireElement(options[0] ?? null, "expected first option"),
2156
+ "ArrowDown",
2157
+ );
2158
+ await flushUpdates(el);
2159
+ options = Array.from(
2160
+ el.querySelectorAll<HTMLButtonElement>("[data-popover] [role='option']"),
2161
+ );
2162
+ expect(document.activeElement).toBe(options[1]);
2163
+
2164
+ keydown(
2165
+ requireElement(options[1] ?? null, "expected second option"),
2166
+ "ArrowUp",
2167
+ );
2168
+ await flushUpdates(el);
2169
+ options = Array.from(
2170
+ el.querySelectorAll<HTMLButtonElement>("[data-popover] [role='option']"),
2171
+ );
2172
+ expect(document.activeElement).toBe(options[0]);
2173
+
2174
+ keydown(requireElement(options[0] ?? null, "expected first option"), " ");
2175
+ await flushUpdates(el);
2176
+ expect(el._collectionIds).toEqual(["col-1"]);
2177
+
2178
+ options = Array.from(
2179
+ el.querySelectorAll<HTMLButtonElement>("[data-popover] [role='option']"),
2180
+ );
2181
+ expect(document.activeElement).toBe(options[0]);
2182
+
2183
+ keydown(
2184
+ requireElement(options[0] ?? null, "expected first option"),
2185
+ "ArrowUp",
2186
+ );
2187
+ await flushUpdates(el);
2188
+ expect(document.activeElement).toBe(searchInput);
2189
+ });
2190
+
2191
+ it("closes the collection picker from the search input on Enter", async () => {
2192
+ vi.spyOn(globalThis, "requestAnimationFrame").mockImplementation((cb) => {
2193
+ cb(0);
2194
+ return 1;
2195
+ });
2196
+ const el = await createElement();
2197
+
2198
+ const trigger = requireElement(
2199
+ el.querySelector<HTMLButtonElement>(".compose-collection-trigger"),
2200
+ "expected collection trigger",
2201
+ );
2202
+ trigger.click();
2203
+ await flushUpdates(el);
2204
+
2205
+ const searchInput = requireElement(
2206
+ el.querySelector<HTMLInputElement>(".compose-collection-search-input"),
2207
+ "expected collection search input",
2208
+ );
2209
+ expect(document.activeElement).toBe(searchInput);
2210
+
2211
+ keydown(searchInput, "Enter");
2212
+ await flushUpdates(el);
2213
+
2214
+ expect(el._showCollection).toBe(false);
2215
+ expect(el._collectionSearch).toBe("");
2216
+ expect(document.activeElement).toBe(trigger);
2217
+ });
2218
+
2219
+ it("closes the collection picker from an option on Enter without toggling", async () => {
2220
+ vi.spyOn(globalThis, "requestAnimationFrame").mockImplementation((cb) => {
2221
+ cb(0);
2222
+ return 1;
2223
+ });
2224
+ const el = await createElement();
2225
+
2226
+ const trigger = requireElement(
2227
+ el.querySelector<HTMLButtonElement>(".compose-collection-trigger"),
2228
+ "expected collection trigger",
2229
+ );
2230
+ trigger.click();
2231
+ await flushUpdates(el);
2232
+
2233
+ const firstOption = requireElement(
2234
+ el.querySelector<HTMLButtonElement>("[data-popover] [role='option']"),
2235
+ "expected first collection option",
2236
+ );
2237
+ firstOption.focus();
2238
+
2239
+ keydown(firstOption, "Enter");
2240
+ await flushUpdates(el);
2241
+
2242
+ expect(el._collectionIds).toEqual([]);
2243
+ expect(el._showCollection).toBe(false);
2244
+ expect(document.activeElement).toBe(trigger);
2245
+ });
2246
+
2247
+ it("does not autofocus collection search on coarse pointer devices", async () => {
2248
+ Object.defineProperty(globalThis, "matchMedia", {
2249
+ configurable: true,
2250
+ writable: true,
2251
+ value: vi.fn().mockImplementation((query: string) => ({
2252
+ matches: query === "(hover: none) and (pointer: coarse)",
2253
+ media: query,
2254
+ onchange: null,
2255
+ addEventListener: vi.fn(),
2256
+ removeEventListener: vi.fn(),
2257
+ addListener: vi.fn(),
2258
+ removeListener: vi.fn(),
2259
+ dispatchEvent: vi.fn(),
2260
+ })),
2261
+ });
2262
+ vi.spyOn(globalThis, "requestAnimationFrame").mockImplementation((cb) => {
2263
+ cb(0);
2264
+ return 1;
2265
+ });
2266
+
2267
+ const el = await createElement();
2268
+ const trigger = requireElement(
2269
+ el.querySelector<HTMLButtonElement>(".compose-collection-trigger"),
2270
+ "expected collection trigger",
2271
+ );
2272
+ trigger.focus();
2273
+ trigger.click();
2274
+ await flushUpdates(el);
2275
+
2276
+ const searchInput = requireElement(
2277
+ el.querySelector<HTMLInputElement>(".compose-collection-search-input"),
2278
+ "expected collection search input",
2279
+ );
2280
+ expect(document.activeElement).not.toBe(searchInput);
2281
+ });
2282
+
2283
+ it("moves trigger keyboard input into the collection search when the picker is open", async () => {
2284
+ Object.defineProperty(globalThis, "matchMedia", {
2285
+ configurable: true,
2286
+ writable: true,
2287
+ value: vi.fn().mockImplementation((query: string) => ({
2288
+ matches: query === "(hover: none) and (pointer: coarse)",
2289
+ media: query,
2290
+ onchange: null,
2291
+ addEventListener: vi.fn(),
2292
+ removeEventListener: vi.fn(),
2293
+ addListener: vi.fn(),
2294
+ removeListener: vi.fn(),
2295
+ dispatchEvent: vi.fn(),
2296
+ })),
2297
+ });
2298
+ vi.spyOn(globalThis, "requestAnimationFrame").mockImplementation((cb) => {
2299
+ cb(0);
2300
+ return 1;
2301
+ });
2302
+
2303
+ const el = await createElement();
2304
+ const trigger = requireElement(
2305
+ el.querySelector<HTMLButtonElement>(".compose-collection-trigger"),
2306
+ "expected collection trigger",
2307
+ );
2308
+ trigger.focus();
2309
+ trigger.click();
2310
+ await flushUpdates(el);
2311
+
2312
+ keydown(trigger, "m");
2313
+ await flushUpdates(el);
2314
+
2315
+ const searchInput = requireElement(
2316
+ el.querySelector<HTMLInputElement>(".compose-collection-search-input"),
2317
+ "expected collection search input",
2318
+ );
2319
+ expect(document.activeElement).toBe(searchInput);
2320
+ expect(el._collectionSearch).toBe("m");
2321
+ });
2322
+
2323
+ it("keeps selected collections first when opening and after reopening", async () => {
2324
+ const el = await createElement([
2325
+ { id: "col-1", title: "Books", slug: "books" },
2326
+ { id: "col-2", title: "Movies", slug: "movies" },
2327
+ { id: "col-3", title: "Travel", slug: "travel" },
2328
+ ]);
2329
+ el._collectionIds = ["col-2"];
2330
+ await el.updateComplete;
2331
+
2332
+ const trigger = requireElement(
2333
+ el.querySelector<HTMLButtonElement>(".compose-collection-trigger"),
2334
+ "expected collection trigger",
2335
+ );
2336
+
2337
+ trigger.click();
2338
+ await el.updateComplete;
2339
+
2340
+ expect(collectionOptionTitles(el)).toEqual(["Movies", "Books", "Travel"]);
2341
+
2342
+ const options = el.querySelectorAll<HTMLButtonElement>(
2343
+ "[data-popover] [role='option']",
2344
+ );
2345
+ options[2]?.click();
2346
+ await el.updateComplete;
2347
+
2348
+ expect(el._collectionIds).toEqual(["col-2", "col-3"]);
2349
+ expect(collectionOptionTitles(el)).toEqual(["Movies", "Books", "Travel"]);
2350
+
2351
+ trigger.click();
2352
+ await el.updateComplete;
2353
+ trigger.click();
2354
+ await el.updateComplete;
2355
+
2356
+ expect(collectionOptionTitles(el)).toEqual(["Movies", "Travel", "Books"]);
2357
+ });
2358
+
1255
2359
  it("reset restores initial state", async () => {
1256
2360
  const el = await createElement();
1257
2361
  el._format = "link";
@@ -1289,13 +2393,15 @@ describe("JantComposeDialog", () => {
1289
2393
  });
1290
2394
 
1291
2395
  it("refreshes collections from the compose-sorted endpoint", async () => {
1292
- const el = await createElement([{ id: "col-1", title: "Books" }]);
2396
+ const el = await createElement([
2397
+ { id: "col-1", title: "Books", slug: "books" },
2398
+ ]);
1293
2399
  const fetchSpy = vi.spyOn(globalThis, "fetch").mockResolvedValue(
1294
2400
  new Response(
1295
2401
  JSON.stringify({
1296
2402
  collections: [
1297
- { id: "col-2", title: "Movies" },
1298
- { id: "col-1", title: "Books" },
2403
+ { id: "col-2", title: "Movies", slug: "movies" },
2404
+ { id: "col-1", title: "Books", slug: "books" },
1299
2405
  ],
1300
2406
  }),
1301
2407
  {
@@ -1309,11 +2415,12 @@ describe("JantComposeDialog", () => {
1309
2415
 
1310
2416
  expect(refreshed).toBe(true);
1311
2417
  expect(fetchSpy).toHaveBeenCalledWith("/api/collections?view=compose", {
2418
+ cache: "no-store",
1312
2419
  headers: { Accept: "application/json" },
1313
2420
  });
1314
2421
  expect(el.collections).toEqual([
1315
- { id: "col-2", title: "Movies" },
1316
- { id: "col-1", title: "Books" },
2422
+ { id: "col-2", title: "Movies", slug: "movies" },
2423
+ { id: "col-1", title: "Books", slug: "books" },
1317
2424
  ]);
1318
2425
  });
1319
2426
 
@@ -1420,7 +2527,7 @@ describe("JantComposeDialog", () => {
1420
2527
 
1421
2528
  if (url.pathname === "/api/collections" && method === "POST") {
1422
2529
  return new Response(
1423
- JSON.stringify({ id: "col-3", title: "Travel" }),
2530
+ JSON.stringify({ id: "col-3", title: "Travel", slug: "travel" }),
1424
2531
  {
1425
2532
  status: 200,
1426
2533
  headers: { "Content-Type": "application/json" },
@@ -1435,9 +2542,9 @@ describe("JantComposeDialog", () => {
1435
2542
  return new Response(
1436
2543
  JSON.stringify({
1437
2544
  collections: [
1438
- { id: "col-3", title: "Travel" },
1439
- { id: "col-1", title: "Books" },
1440
- { id: "col-2", title: "Movies" },
2545
+ { id: "col-3", title: "Travel", slug: "travel" },
2546
+ { id: "col-1", title: "Books", slug: "books" },
2547
+ { id: "col-2", title: "Movies", slug: "movies" },
1441
2548
  ],
1442
2549
  }),
1443
2550
  {
@@ -1486,9 +2593,9 @@ describe("JantComposeDialog", () => {
1486
2593
  ).toBe(false);
1487
2594
  expect(el._collectionIds).toContain("col-3");
1488
2595
  expect(el.collections).toEqual([
1489
- { id: "col-3", title: "Travel" },
1490
- { id: "col-1", title: "Books" },
1491
- { id: "col-2", title: "Movies" },
2596
+ { id: "col-3", title: "Travel", slug: "travel" },
2597
+ { id: "col-1", title: "Books", slug: "books" },
2598
+ { id: "col-2", title: "Movies", slug: "movies" },
1492
2599
  ]);
1493
2600
  });
1494
2601
 
@@ -1609,6 +2716,7 @@ describe("JantComposeDialog", () => {
1609
2716
  mediaId: "media-1",
1610
2717
  alt: "",
1611
2718
  error: null,
2719
+ posterUrl: null,
1612
2720
  summary: null,
1613
2721
  chars: null,
1614
2722
  },
@@ -1652,6 +2760,7 @@ describe("JantComposeDialog", () => {
1652
2760
  mediaId: "media-1",
1653
2761
  alt: "",
1654
2762
  error: null,
2763
+ posterUrl: null,
1655
2764
  summary: null,
1656
2765
  chars: null,
1657
2766
  },
@@ -1693,6 +2802,7 @@ describe("JantComposeDialog", () => {
1693
2802
  mediaId: "media-1",
1694
2803
  alt: "",
1695
2804
  error: null,
2805
+ posterUrl: null,
1696
2806
  summary: null,
1697
2807
  chars: null,
1698
2808
  },
@@ -1747,6 +2857,7 @@ describe("JantComposeDialog", () => {
1747
2857
  mediaId: "media-1",
1748
2858
  alt: "A test image",
1749
2859
  error: null,
2860
+ posterUrl: null,
1750
2861
  summary: null,
1751
2862
  chars: null,
1752
2863
  },
@@ -1816,6 +2927,7 @@ describe("JantComposeDialog", () => {
1816
2927
  mediaId: null,
1817
2928
  alt: "Alt for pending",
1818
2929
  error: null,
2930
+ posterUrl: null,
1819
2931
  summary: null,
1820
2932
  chars: null,
1821
2933
  },
@@ -1849,6 +2961,48 @@ describe("JantComposeDialog", () => {
1849
2961
  URL.revokeObjectURL(previewUrl);
1850
2962
  });
1851
2963
 
2964
+ it("dispatches submit immediately even with pending inline image uploads", async () => {
2965
+ const el = await createElement();
2966
+ const editor = requireElement(
2967
+ el.querySelector<JantComposeEditor>("jant-compose-editor"),
2968
+ "expected compose editor",
2969
+ );
2970
+
2971
+ const bodyJson = {
2972
+ type: "doc",
2973
+ content: [
2974
+ {
2975
+ type: "image",
2976
+ attrs: {
2977
+ src: "blob:inline-preview",
2978
+ alt: "",
2979
+ title: "",
2980
+ caption: "",
2981
+ },
2982
+ },
2983
+ ],
2984
+ };
2985
+ editor._bodyJson = bodyJson;
2986
+ await editor.updateComplete;
2987
+
2988
+ let receivedDetail: ComposeSubmitDetail | null = null;
2989
+ el.addEventListener("jant:compose-submit-deferred", (event) => {
2990
+ receivedDetail = (event as CustomEvent<ComposeSubmitDetail>).detail;
2991
+ });
2992
+
2993
+ requireElement(
2994
+ el.querySelector<HTMLButtonElement>(".compose-publish-main"),
2995
+ "expected post button",
2996
+ ).click();
2997
+ await flushUpdates(el);
2998
+
2999
+ // Submit fires immediately — blob URL resolution is handled by compose-bridge
3000
+ expect(receivedDetail).not.toBeNull();
3001
+ expect((receivedDetail as unknown as ComposeSubmitDetail).body).toBe(
3002
+ JSON.stringify(bodyJson),
3003
+ );
3004
+ });
3005
+
1852
3006
  // ── Close confirmation ─────────────────────────────────────────────
1853
3007
 
1854
3008
  it("requestClose on empty form closes immediately without confirmation", async () => {
@@ -1898,9 +3052,12 @@ describe("JantComposeDialog", () => {
1898
3052
  (
1899
3053
  el as unknown as {
1900
3054
  _dialogEl: HTMLDialogElement | null;
3055
+ _mousedownOnBackdrop: boolean;
1901
3056
  _handleDialogClick: (event: Event) => void;
1902
3057
  }
1903
3058
  )._dialogEl = dialog;
3059
+ (el as unknown as { _mousedownOnBackdrop: boolean })._mousedownOnBackdrop =
3060
+ true;
1904
3061
  vi.spyOn(document, "elementFromPoint").mockReturnValue(dialog);
1905
3062
 
1906
3063
  (
@@ -2353,6 +3510,55 @@ describe("JantComposeDialog", () => {
2353
3510
  expect(tiptap.getText()).toBe("/zzz");
2354
3511
  });
2355
3512
 
3513
+ it("does not open the slash menu when the caret sits inside a `/word` in prose", async () => {
3514
+ const el = await createElement();
3515
+ const editor = requireElement(
3516
+ el.querySelector<JantComposeEditor>("jant-compose-editor"),
3517
+ "expected compose editor",
3518
+ );
3519
+ const tiptap = requireEditor(editor);
3520
+
3521
+ tiptap.commands.focus("end");
3522
+ tiptap.commands.insertContent("before /now, after");
3523
+ await flushUpdates(el);
3524
+
3525
+ // Move the caret into the middle of "now" (between "/" and "n").
3526
+ const text = tiptap.getText();
3527
+ const slashIndex = text.indexOf("/now");
3528
+ // +2 accounts for the doc/paragraph boundary before the text starts.
3529
+ tiptap.commands.setTextSelection(slashIndex + 2);
3530
+ await flushUpdates(el);
3531
+
3532
+ expect(document.querySelector(".tiptap-slash-menu")).toBeNull();
3533
+ });
3534
+
3535
+ it("does not reopen the slash menu when the caret moves back to an existing `/word`", async () => {
3536
+ const el = await createElement();
3537
+ const editor = requireElement(
3538
+ el.querySelector<JantComposeEditor>("jant-compose-editor"),
3539
+ "expected compose editor",
3540
+ );
3541
+ const tiptap = requireEditor(editor);
3542
+
3543
+ // Type "/image " followed by more text, then press Escape to close the
3544
+ // menu that (rightly) opened while typing "/image".
3545
+ tiptap.commands.focus("end");
3546
+ tiptap.commands.insertContent("/image hello");
3547
+ await flushUpdates(el);
3548
+
3549
+ // Menu should be gone because the space after "/image" closed it.
3550
+ expect(document.querySelector(".tiptap-slash-menu")).toBeNull();
3551
+
3552
+ // Now move the caret back to just after "/image" (between "e" and " ").
3553
+ const text = tiptap.getText();
3554
+ const endOfImage = text.indexOf("/image") + "/image".length;
3555
+ tiptap.commands.setTextSelection(endOfImage + 1);
3556
+ await flushUpdates(el);
3557
+
3558
+ // Selection-only change should NOT reopen the menu.
3559
+ expect(document.querySelector(".tiptap-slash-menu")).toBeNull();
3560
+ });
3561
+
2356
3562
  it("dialog cancel closes the emoji picker before prompting to save", async () => {
2357
3563
  const el = await createElement();
2358
3564
  const editor = requireElement(
@@ -2369,7 +3575,7 @@ describe("JantComposeDialog", () => {
2369
3575
  await editor.updateComplete;
2370
3576
 
2371
3577
  const closeEmojiPickerSpy = vi.spyOn(editor, "closeEmojiPicker");
2372
- const focusSpy = vi.spyOn(editor, "focusInput");
3578
+ const focusSpy = vi.spyOn(editor, "focusSelection");
2373
3579
 
2374
3580
  (
2375
3581
  el as unknown as {
@@ -2380,7 +3586,7 @@ describe("JantComposeDialog", () => {
2380
3586
 
2381
3587
  expect(closeEmojiPickerSpy).toHaveBeenCalledWith({ restoreFocus: true });
2382
3588
  expect(el._confirmPanelOpen).toBe(false);
2383
- expect(focusSpy).toHaveBeenCalledWith("end");
3589
+ expect(focusSpy).toHaveBeenCalled();
2384
3590
  });
2385
3591
 
2386
3592
  it("dialog cancel closes the collection selector and keeps selected collections", async () => {
@@ -2411,7 +3617,7 @@ describe("JantComposeDialog", () => {
2411
3617
  expect(el._showCollection).toBe(false);
2412
3618
  expect(el._collectionIds).toEqual(["col-1"]);
2413
3619
  expect(el._confirmPanelOpen).toBe(false);
2414
- expect(focusSpy).toHaveBeenCalledWith("end");
3620
+ expect(focusSpy).toHaveBeenCalledWith();
2415
3621
  });
2416
3622
 
2417
3623
  it("dialog cancel closes publish settings and returns focus to the editor", async () => {
@@ -2443,7 +3649,7 @@ describe("JantComposeDialog", () => {
2443
3649
 
2444
3650
  expect(el._showPublishPanel).toBe(false);
2445
3651
  expect(el._confirmPanelOpen).toBe(false);
2446
- expect(focusSpy).toHaveBeenCalledWith("end");
3652
+ expect(focusSpy).toHaveBeenCalledWith();
2447
3653
  });
2448
3654
 
2449
3655
  it("requestClose with content shows confirmation panel", async () => {
@@ -3173,10 +4379,22 @@ describe("JantComposeDialog", () => {
3173
4379
  expect((receivedDetail as unknown as ComposeSubmitDetail).status).toBe(
3174
4380
  "draft",
3175
4381
  );
3176
- // Drafts panel opened instead of dialog closing
3177
- expect(el._draftsPanelOpen).toBe(true);
4382
+ // Drafts panel waits for the bridge to finish writing before opening
4383
+ expect(el._draftsPanelOpen).toBe(false);
3178
4384
  expect(el._confirmPanelOpen).toBe(false);
3179
4385
 
4386
+ // Simulate the bridge signalling that the draft was saved
4387
+ document.dispatchEvent(
4388
+ new CustomEvent("jant:compose-submit-complete", {
4389
+ detail: { status: "draft" },
4390
+ }),
4391
+ );
4392
+ await el.updateComplete;
4393
+ await new Promise((r) => setTimeout(r, 0));
4394
+ await el.updateComplete;
4395
+
4396
+ expect(el._draftsPanelOpen).toBe(true);
4397
+
3180
4398
  fetchSpy.mockRestore();
3181
4399
  });
3182
4400
 
@@ -3256,6 +4474,7 @@ describe("JantComposeDialog", () => {
3256
4474
  mediaId: "media-1",
3257
4475
  alt: "",
3258
4476
  error: null,
4477
+ posterUrl: null,
3259
4478
  summary: null,
3260
4479
  chars: null,
3261
4480
  },