@jant/core 0.3.40 → 0.3.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -34
- package/bin/commands/assets/upload.js +247 -0
- package/bin/commands/collections.js +268 -0
- package/bin/commands/import-site.js +1169 -343
- package/bin/commands/media.js +302 -0
- package/bin/commands/migrate-text-attachments.js +183 -0
- package/bin/commands/migrate.js +263 -2
- package/bin/commands/posts.js +262 -0
- package/bin/commands/search-reindex.js +175 -0
- package/bin/commands/search.js +53 -0
- package/bin/commands/settings.js +93 -0
- package/bin/commands/site/export.js +34 -33
- package/bin/commands/site/{localize-media.js → pull-media.js} +17 -17
- package/bin/lib/d1-query.js +15 -3
- package/bin/lib/http-api.js +223 -0
- package/bin/lib/hugo-markdown.js +102 -0
- package/bin/lib/media-upload.js +206 -0
- package/bin/lib/migration-runner.js +64 -0
- package/bin/lib/runtime-target.js +7 -2
- package/bin/lib/site-media-parser.js +71 -16
- package/bin/lib/site-pull-media.js +695 -0
- package/bin/lib/site-snapshot.js +0 -1
- package/dist/app-Ctl0T0zO.js +5 -0
- package/dist/{app-CAtsuLLh.js → app-GbfwoeDJ.js} +24357 -23139
- package/dist/client/.vite/manifest.json +3485 -0
- package/dist/client/_assets/chunks/02611a045a7fe83a12014e3debc9f731-BqEDtwXC.woff2 +0 -0
- package/dist/client/_assets/chunks/033466ef683afe931f7f520cfb42d928-BluLHEh9.woff2 +0 -0
- package/dist/client/_assets/chunks/04b8bde59cff68eeee74fb1914eb09c7-D5j2CLTt.woff2 +0 -0
- package/dist/client/_assets/chunks/057a6a98bda7fe57105ddaa99ec82015-DY_AoijT.woff2 +0 -0
- package/dist/client/_assets/chunks/05ac821472e235943ed1435e4bb8ecad-CUm8GiH6.woff2 +0 -0
- package/dist/client/_assets/chunks/05da12edb9d52210581dc6ec4541031f-BXAOx2XP.woff2 +0 -0
- package/dist/client/_assets/chunks/074c1f8483d5a4d8c45c8c5f3e4cbb32-CDqdMQm8.woff2 +0 -0
- package/dist/client/_assets/chunks/0968e4861204b51f62a2f8e9f15dd5e0-D5tLgF_8.woff2 +0 -0
- package/dist/client/_assets/chunks/0acd1fe2b2ea1ad1bfee7ae1fa139d27-BKK1m412.woff2 +0 -0
- package/dist/client/_assets/chunks/0c2ab6b295e55f356f8020d4e7747522-CkCQjQw3.woff2 +0 -0
- package/dist/client/_assets/chunks/0c5f9492af03a4fa42c784de94649de1-CQlJqksa.woff2 +0 -0
- package/dist/client/_assets/chunks/0d5dec931dc885f07fe5cd5af8bed675-C74x-U_U.woff2 +0 -0
- package/dist/client/_assets/chunks/0d9a936885a4c39077438effd3779cbd-Bw0fYF4S.woff2 +0 -0
- package/dist/client/_assets/chunks/0e645da524f7cfc0e8c3c03fb2b08428-BVeI_5Th.woff2 +0 -0
- package/dist/client/_assets/chunks/0f5e1a18987dbc84ca05188c129e1936-B6_lZ6fE.woff2 +0 -0
- package/dist/client/_assets/chunks/1076f0f6f66d28d7a2f16427faad4413-ubiXnD1P.woff2 +0 -0
- package/dist/client/_assets/chunks/112743a4ab5fdd1498dfdf2b11336380-B5FzQxTK.woff2 +0 -0
- package/dist/client/_assets/chunks/1211f03d3ff5759a702631445793f60e-DDcSiyCt.woff2 +0 -0
- package/dist/client/_assets/chunks/12ef1ba76bd20b004b865266a1aa70b3-BwmaD87S.woff2 +0 -0
- package/dist/client/_assets/chunks/135e83b403475c5dc9e49b43501a5b84-C5mE_KZS.woff2 +0 -0
- package/dist/client/_assets/chunks/1380210a722ac9aeef54005ad7b015c9-CjA-MP82.woff2 +0 -0
- package/dist/client/_assets/chunks/13b2c53b30e6a3e4de7132dbc18dbdfc-B9WOB7kv.woff2 +0 -0
- package/dist/client/_assets/chunks/147b24c2f08d03bbed30887d4cb3311c-IeeFCJ13.woff2 +0 -0
- package/dist/client/_assets/chunks/152395634a207579552f8cb54db88599-C2y2JyNZ.woff2 +0 -0
- package/dist/client/_assets/chunks/15d95680dc31cc6ce20e0a5464106dc4-CpD8dqRm.woff2 +0 -0
- package/dist/client/_assets/chunks/16663e567f81f4725a1522f37e18f71f-BNoxS9pQ.woff2 +0 -0
- package/dist/client/_assets/chunks/1733f27476507ca68b68a803bc533cc2-DXL-14Ug.woff2 +0 -0
- package/dist/client/_assets/chunks/17c7f5d0a45e92ede0e5dec3a2c77efa-CNj3jQMl.woff2 +0 -0
- package/dist/client/_assets/chunks/1824321da801e6257b902f3d1d09054c-D4dgdyWB.woff2 +0 -0
- package/dist/client/_assets/chunks/188c2db794f3dd7a45889ddbc81da9bb-C9NbUBBO.woff2 +0 -0
- package/dist/client/_assets/chunks/1a08931435f885e707edb85978ea3bb6-BOiP2u7G.woff2 +0 -0
- package/dist/client/_assets/chunks/1a3a92c7c060a71a6c35819b9ebcdbb9-DFfonQfz.woff2 +0 -0
- package/dist/client/_assets/chunks/1be602ad456b0d75902d352116cb35fd-Bc-XQtQh.woff2 +0 -0
- package/dist/client/_assets/chunks/1cf737900dd49c2e88f1b3221a82a602-CfTmnns3.woff2 +0 -0
- package/dist/client/_assets/chunks/1fba7ec0e412e911bf31841de5a8a4e4-18ELMbFF.woff2 +0 -0
- package/dist/client/_assets/chunks/233d3a685ee18276b319363474599d47-BaFdJ0-G.woff2 +0 -0
- package/dist/client/_assets/chunks/2612c60f9dc5459ac42763591240a1d6-CTD69cq1.woff2 +0 -0
- package/dist/client/_assets/chunks/284b53bbefb06924cf236d24c8ed5641-CPDiuOv6.woff2 +0 -0
- package/dist/client/_assets/chunks/28a97af9ab9d38983d20f39ff09840e1-mFqcT3Jw.woff2 +0 -0
- package/dist/client/_assets/chunks/2a56eaf19d1d38a6b57e2a388f733676-CU52qBlE.woff2 +0 -0
- package/dist/client/_assets/chunks/2ab8cf1f23a5ac7939a7876054e711a7-B4T3dK7A.woff2 +0 -0
- package/dist/client/_assets/chunks/2abfbab82b6a7c04426afc054d2464da-D5PIIHde.woff2 +0 -0
- package/dist/client/_assets/chunks/2bfaadaf3479c72286248e6de0be0ec9-DEpU6O74.woff2 +0 -0
- package/dist/client/_assets/chunks/2c8c55e4cec85ce77e95cac9d330f5cf-BCTA96EZ.woff2 +0 -0
- package/dist/client/_assets/chunks/2e6f4bb71ef6b38765d51acc5a79f638-Mbs6PECD.woff2 +0 -0
- package/dist/client/_assets/chunks/2ed9981d2e8983365bd051159b976b6d-D8gg6NWM.woff2 +0 -0
- package/dist/client/_assets/chunks/2f4c633e923ba30c6ba376367379fc91-CIJCFztv.woff2 +0 -0
- package/dist/client/_assets/chunks/31194d303a67561926a544ed0e072aee-w3-eGYAi.woff2 +0 -0
- package/dist/client/_assets/chunks/31a197213ae61d7043c81013f52d10d6-DXZrrkJP.woff2 +0 -0
- package/dist/client/_assets/chunks/349965eee0a9b6c984a319ab96a4ece9-DByAVmXw.woff2 +0 -0
- package/dist/client/_assets/chunks/34e6750e00c3a911ef87dc66b336594d-DNbWesOu.woff2 +0 -0
- package/dist/client/_assets/chunks/3623d96698132c61e861f984ce11cc23-jqV9ErzX.woff2 +0 -0
- package/dist/client/_assets/chunks/36cda3eae13370b934bba4429fab9078-BPDcUlw9.woff2 +0 -0
- package/dist/client/_assets/chunks/36f3df4730cfca4fc77fe52b52e6a126-BwK10NC0.woff2 +0 -0
- package/dist/client/_assets/chunks/37887deb7a9c466cf2af6ee7ff372ea6-BrAerJB1.woff2 +0 -0
- package/dist/client/_assets/chunks/37dbd564820cce2f7b3331e442da0df3-Cql2kzIi.woff2 +0 -0
- package/dist/client/_assets/chunks/37dc8505b20b37a2477a9121b16d44a7-CoXw6XWV.woff2 +0 -0
- package/dist/client/_assets/chunks/39211b02f24c69cbd1b54f6d41b01933-4Xu7vvnq.woff2 +0 -0
- package/dist/client/_assets/chunks/3aa13f1843d7c8fa16109b6395a9a29d-BQFZUc3B.woff2 +0 -0
- package/dist/client/_assets/chunks/3aba89c4d4281553078de4622b498cbb-8QS1zDYk.woff2 +0 -0
- package/dist/client/_assets/chunks/3ac0a07878cacdc98bb889f40f5970d3-C-BaM6-y.woff2 +0 -0
- package/dist/client/_assets/chunks/3d88558097559d775231194d43745ba7-CR2aqGC7.woff2 +0 -0
- package/dist/client/_assets/chunks/3dc3e8c35524cad2d1236f3393104ccf-9sqBsF7B.woff2 +0 -0
- package/dist/client/_assets/chunks/3e24063b19abd2b051af986a6870046d-TlweM3SW.woff2 +0 -0
- package/dist/client/_assets/chunks/3e2c06bdd9dcab29aeaeb0cfb7fa608a-BaqU-dIt.woff2 +0 -0
- package/dist/client/_assets/chunks/424123f9371ba357087363263eeafcda-C8SVfEJi.woff2 +0 -0
- package/dist/client/_assets/chunks/43b3bbf43055c3e65db9cdf2c5b18c1c-CbHx6-Ki.woff2 +0 -0
- package/dist/client/_assets/chunks/4410f876d27c9821839474b6b1059fd7-ClYz91aY.woff2 +0 -0
- package/dist/client/_assets/chunks/44704471b60c18b3ecec5300a88f1102-DDmmy6-m.woff2 +0 -0
- package/dist/client/_assets/chunks/45140742dfa8686b58db2899ce89f89a-D5IMMzDM.woff2 +0 -0
- package/dist/client/_assets/chunks/46604090efbb99c2db1800928af0a239-JX2fpYX_.woff2 +0 -0
- package/dist/client/_assets/chunks/46998df85d31e629c0142f0556f5e7c5-BIkNG9Wh.woff2 +0 -0
- package/dist/client/_assets/chunks/469d2754e315e77270c12fa0ab28026c-D-y-vsfA.woff2 +0 -0
- package/dist/client/_assets/chunks/479329a39a3eb6c0047e6b0981919855-vZKCRx42.woff2 +0 -0
- package/dist/client/_assets/chunks/491bbba374093fff045f55803a22bfef-DSQXiDCd.woff2 +0 -0
- package/dist/client/_assets/chunks/49902f1c41b281f8eb3771abdd4dccb8-DxVxNg2W.woff2 +0 -0
- package/dist/client/_assets/chunks/4c0760284569b87d8f2290c324953973-Bqd-TKHH.woff2 +0 -0
- package/dist/client/_assets/chunks/4cba92c942694bdc479bc54101bb1aa4-BrfsrgrC.woff2 +0 -0
- package/dist/client/_assets/chunks/4d38e56738156625329d93bd5c8cc74a-DONkMP0y.woff2 +0 -0
- package/dist/client/_assets/chunks/4db4f31a16965baa42cb7dad667a8f04-Dt1Cy4gr.woff2 +0 -0
- package/dist/client/_assets/chunks/4e7d263c30bb48604069a3b4404d4eeb-DNPCkvKD.woff2 +0 -0
- package/dist/client/_assets/chunks/5279c7e465d9871edf51fd02e691eee7-CKhyhvVf.woff2 +0 -0
- package/dist/client/_assets/chunks/527ff336279465617aafbafe1a830632-CC3lv4pm.woff2 +0 -0
- package/dist/client/_assets/chunks/54bfdb6f21f9d6191e5100580239e14f-PGlqYvgl.woff2 +0 -0
- package/dist/client/_assets/chunks/56b40518ea0608e62826bd44ee27f5f9-BD9F9Oss.woff2 +0 -0
- package/dist/client/_assets/chunks/56be5e32fd01668fe5ba814d94905839-CSyFA-9l.woff2 +0 -0
- package/dist/client/_assets/chunks/572b9e28f1e6d89b765a16d809db84a1-BIYMjzEa.woff2 +0 -0
- package/dist/client/_assets/chunks/57e9a86651366c1ba299e47aa7e358c2-BHuZXB4W.woff2 +0 -0
- package/dist/client/_assets/chunks/57f89adeae55aa7fe2add3fc1801ce03-Cy3PNrfx.woff2 +0 -0
- package/dist/client/_assets/chunks/5921cef36750ff6092d94b83a802823b-hF-uymTh.woff2 +0 -0
- package/dist/client/_assets/chunks/59e95d8b5332dcdae894a19946ab767d-ClRfsjWZ.woff2 +0 -0
- package/dist/client/_assets/chunks/5acc7f736217259db79a42bf44241c48-FYQUuFOM.woff2 +0 -0
- package/dist/client/_assets/chunks/5c05f8b75c9d7f25b3c04ca711cf43b3-D-YWPkSq.woff2 +0 -0
- package/dist/client/_assets/chunks/5df9ce98c75f8c50fb4fff7f01a23925-DgoCENyW.woff2 +0 -0
- package/dist/client/_assets/chunks/5e0124c7265f1b4cf0fc797b94efbebc-BTBNHsSB.woff2 +0 -0
- package/dist/client/_assets/chunks/5ec349ff62b9ee5fe0c6dae867704dc4-B4dezhrz.woff2 +0 -0
- package/dist/client/_assets/chunks/60020a830d809c26970cf8e48ba1eeda-B6FDTSik.woff2 +0 -0
- package/dist/client/_assets/chunks/601f812e8b1eafecd5ba585a6a6d7962-gIVBlUU9.woff2 +0 -0
- package/dist/client/_assets/chunks/6025ddaf99d86cc8bfd781006b19fbd6-CS-ze337.woff2 +0 -0
- package/dist/client/_assets/chunks/60bcfa3ea7446eb42394aab02d28be40-C-PQfCu3.woff2 +0 -0
- package/dist/client/_assets/chunks/60f6e28b3f0400b0e8fe32c3c7116102-Dag9L2qU.woff2 +0 -0
- package/dist/client/_assets/chunks/615a7e0d63f257109d599b2a1977de68-D6Y93yoG.woff2 +0 -0
- package/dist/client/_assets/chunks/615b38e0b5bb3ea68426f44f47706e6f-B5yzIIer.woff2 +0 -0
- package/dist/client/_assets/chunks/61a2c80d0c924d5a6187c02e8d1d1642-BY3MmHbZ.woff2 +0 -0
- package/dist/client/_assets/chunks/62fc94f8d4c5a750b7f25e7387539910-3ql2_WDK.woff2 +0 -0
- package/dist/client/_assets/chunks/639c7c6b0b0c27c738702741cfa4b8c0-BCD3OGri.woff2 +0 -0
- package/dist/client/_assets/chunks/63fafcb069520613d0ea35ad3e6b1e42-DP0Rrj4o.woff2 +0 -0
- package/dist/client/_assets/chunks/64a404d675f1d726f0891b7a80794595-DU3L0I9s.woff2 +0 -0
- package/dist/client/_assets/chunks/65a60d87c64228d258a123cbe85f5f31-B4kbf-79.woff2 +0 -0
- package/dist/client/_assets/chunks/65e9c4585d71bf48a5c62f367010da16-CvzgzReN.woff2 +0 -0
- package/dist/client/_assets/chunks/68a3a4bf337f8a0722be76676e20b850-Dnaw8EPD.woff2 +0 -0
- package/dist/client/_assets/chunks/6968e889e18891d912809fe484c2e745-Bj1m5e0s.woff2 +0 -0
- package/dist/client/_assets/chunks/6abdd163d2c4b85698db8aa1ce149361-B5FVGJuo.woff2 +0 -0
- package/dist/client/_assets/chunks/6b75213eb0be40ce84241eb2bb438a2e-CqHmF-NR.woff2 +0 -0
- package/dist/client/_assets/chunks/70714891ad3fbfc3d5f10a8669dacc5a-BWk5nLFN.woff2 +0 -0
- package/dist/client/_assets/chunks/7158022889c6c177062ac85036e7af10-B3D4K22n.woff2 +0 -0
- package/dist/client/_assets/chunks/73b794d885b88f7befb7ea8ab1780a15-D0ZehnEI.woff2 +0 -0
- package/dist/client/_assets/chunks/743f290baf027d4626a86e22f3d44600-DDTDQdRC.woff2 +0 -0
- package/dist/client/_assets/chunks/7520ca6ca8c60eb1e62d50e71c8d30f1-CVRaGu2G.woff2 +0 -0
- package/dist/client/_assets/chunks/757019c01c5e2c2b0f54293cea3b5636-BTovUnoH.woff2 +0 -0
- package/dist/client/_assets/chunks/759a647b77791b1e98f99bc0ab5317a7-CrK6FIFz.woff2 +0 -0
- package/dist/client/_assets/chunks/761d84afca8d7f34eebefe538adba827-DRbiVuwX.woff2 +0 -0
- package/dist/client/_assets/chunks/76e07530323418ec723c5d7634c9bcca-DGtVJTTH.woff2 +0 -0
- package/dist/client/_assets/chunks/76e51630143b95b6322ef93ad330da7a-D-kV82C_.woff2 +0 -0
- package/dist/client/_assets/chunks/76f117a3baacda304a71965a17266a13-DFfvsFUm.woff2 +0 -0
- package/dist/client/_assets/chunks/773819b7b9b8fd404a929867c0fd677e-Bu1xCMZb.woff2 +0 -0
- package/dist/client/_assets/chunks/77747f17625a259fb405b7bbdd84ac4b-o_vNfh-6.woff2 +0 -0
- package/dist/client/_assets/chunks/7817dd16805145d8538ad57590f69f5a-DguSUP3O.woff2 +0 -0
- package/dist/client/_assets/chunks/788498548ddfb710d6ef3c7bff79fa97-3ffoQfRd.woff2 +0 -0
- package/dist/client/_assets/chunks/78ea6c40923ce95367e3517dd1e5a849-DvLDRhX9.woff2 +0 -0
- package/dist/client/_assets/chunks/79aa7c8c842c4a27cf57c0a3a1ffca5a-sDoCeoKI.woff2 +0 -0
- package/dist/client/_assets/chunks/7b59f0ec7792b18458dc5a361e37884c-DWvxkKmT.woff2 +0 -0
- package/dist/client/_assets/chunks/7c3945788a689a69356c1a622d69d48b-1JjIpss2.woff2 +0 -0
- package/dist/client/_assets/chunks/7cc72fd2c9105560422b6a67c6162945-Co3AF5_G.woff2 +0 -0
- package/dist/client/_assets/chunks/7d0ea0690e432462f4d05a23d720ec19-Dx7DK4C3.woff2 +0 -0
- package/dist/client/_assets/chunks/7dffa5c1bec57e0903fd62357401ff1a-DuQHmiiN.woff2 +0 -0
- package/dist/client/_assets/chunks/7e38ca789a9e76ac91d9256374e154f0-BtETgGRe.woff2 +0 -0
- package/dist/client/_assets/chunks/7f9a6c9286b68de9c72b0024f7beeb40-JpXdo4FC.woff2 +0 -0
- package/dist/client/_assets/chunks/815a0b797465190f60d8b1a04656c42e-Spzo94nU.woff2 +0 -0
- package/dist/client/_assets/chunks/823d1bb11097331238d103c7f72138dc-CGN6m2VF.woff2 +0 -0
- package/dist/client/_assets/chunks/82f6ccc063960eed1cdfd1d61ada8862-CsmAqx2a.woff2 +0 -0
- package/dist/client/_assets/chunks/835b6505bb9eea9678925a1fa885353d-C6mSBwUc.woff2 +0 -0
- package/dist/client/_assets/chunks/845b4b67564d62cf5cad242f7ac08ea5-DnvzrloJ.woff2 +0 -0
- package/dist/client/_assets/chunks/84c8e7bc0931008ed91f44ed12dfea94-C2Hvhh-b.woff2 +0 -0
- package/dist/client/_assets/chunks/8726558cf297cbda831cc19514897205-gSFV1vW0.woff2 +0 -0
- package/dist/client/_assets/chunks/8855472f3c02f6c7ebb3216617ebe4af-C1JsWVqq.woff2 +0 -0
- package/dist/client/_assets/chunks/887a11290ba78b1e66c6d2f67043005e-L_A1f5Fz.woff2 +0 -0
- package/dist/client/_assets/chunks/888ba53510213d5d1f6a7deb60e569b6-OP8E-ELN.woff2 +0 -0
- package/dist/client/_assets/chunks/88db1d042074fb6e66821ffc10941930-kZ6K3e62.woff2 +0 -0
- package/dist/client/_assets/chunks/892aa49b2529c89bb4076d4aa51fe30f-Bw0_Km2A.woff2 +0 -0
- package/dist/client/_assets/chunks/8b5c9ef81159f31d2ab35f45ca4373e0-Djw95sKC.woff2 +0 -0
- package/dist/client/_assets/chunks/8b91c7c2ed390f1278b9befa3aa87233-B2C3iFbQ.woff2 +0 -0
- package/dist/client/_assets/chunks/8dc18c0cebe6aa4bf4c45dbb831048ab-bUlANLit.woff2 +0 -0
- package/dist/client/_assets/chunks/8e231d707f0c4f8e3cde90a6b52a79aa-CvwbWlul.woff2 +0 -0
- package/dist/client/_assets/chunks/8fb45117a62d92dce44a80f0b729ead5-Djr4-o5-.woff2 +0 -0
- package/dist/client/_assets/chunks/9064c12fd72c7aba235d8c3881755b91-BnguxT-p.woff2 +0 -0
- package/dist/client/_assets/chunks/90b7848e9b1623b77bdcf155e90b839c-CFvPRnoM.woff2 +0 -0
- package/dist/client/_assets/chunks/911b9e53e9814de2998c60bf3115f560-DmPmj635.woff2 +0 -0
- package/dist/client/_assets/chunks/919a879bd2d580d8491a31a449390689-BY2w-8_Y.woff2 +0 -0
- package/dist/client/_assets/chunks/91da6cb174bebfb96976e2f1316be2fe-DztAJb6W.woff2 +0 -0
- package/dist/client/_assets/chunks/92fdc376bce277874e75db666f175b44-DJUnHEIN.woff2 +0 -0
- package/dist/client/_assets/chunks/930a23430f0eb64480d7fe5f82834e21-2VrDPT7E.woff2 +0 -0
- package/dist/client/_assets/chunks/934dfdbed2bb2c4b6129199d699a34fa-CtpHsjRD.woff2 +0 -0
- package/dist/client/_assets/chunks/93bed0e5f2dd5a21cf73304fcfbc149d-DJiaETX7.woff2 +0 -0
- package/dist/client/_assets/chunks/93f3ea6918533d96d9c252378b9a4af0-D-FJaGi-.woff2 +0 -0
- package/dist/client/_assets/chunks/95a0951d2a2722ff773a1a45e8c474d6-DuZg-BpP.woff2 +0 -0
- package/dist/client/_assets/chunks/95ff41191c76ef893ac70a1043e95e44-C08QDbJO.woff2 +0 -0
- package/dist/client/_assets/chunks/96c75862c8cec51a7c05ff025fea86cd-DBnRwCuW.woff2 +0 -0
- package/dist/client/_assets/chunks/97d7a17234f2b5030ae9697ae00aded2-CkUSaB-p.woff2 +0 -0
- package/dist/client/_assets/chunks/982cd1765ca6bbb3e6547e47834d5148-DkIybx1r.woff2 +0 -0
- package/dist/client/_assets/chunks/985d4d41afa0934a4eb2de35473fb9a2-CF7rgJMw.woff2 +0 -0
- package/dist/client/_assets/chunks/995c4fda62d25c3b79dd5987737df6ae-CY5ri8HW.woff2 +0 -0
- package/dist/client/_assets/chunks/9b0c3caac458c7bc9c9133e40405ddea-DhPrYOcz.woff2 +0 -0
- package/dist/client/_assets/chunks/9b21e8244f5930c48ad5073f83777b6c-CuE9-r-r.woff2 +0 -0
- package/dist/client/_assets/chunks/9bb38fd05201de666b88b82d56a386f7-nMl6lB8X.woff2 +0 -0
- package/dist/client/_assets/chunks/9d0afa53dc2f92ba42024f55f11f6779-Bozpx8bL.woff2 +0 -0
- package/dist/client/_assets/chunks/9e13a47242926f1cd7d68c08d9ef8889-DwKQjc8M.woff2 +0 -0
- package/dist/client/_assets/chunks/9eff2ab2a9d2ced47ab761bf6fdb11ec-D86-Zm1H.woff2 +0 -0
- package/dist/client/_assets/chunks/9f1963fe8d4d6878d717d872a8f3fdb5-DxjITUFq.woff2 +0 -0
- package/dist/client/_assets/chunks/9f3bdab4025dc7c5ba1a5871e62e8918-BqxDEFLJ.woff2 +0 -0
- package/dist/client/_assets/chunks/9fce02d1a09f464c36c9fcfb14a354c5-F2tMAB_j.woff2 +0 -0
- package/dist/client/_assets/chunks/a00e94d4f04df6aa659db9c4954c7efe-CRBKCj_3.woff2 +0 -0
- package/dist/client/_assets/chunks/a0a6755c7e3f9e4cf03387027dd9f16c-BAyRqF2w.woff2 +0 -0
- package/dist/client/_assets/chunks/a0d00fe4816c95a8c7dffd445ef00b03-CxcTiRLJ.woff2 +0 -0
- package/dist/client/_assets/chunks/a0f199077fa1a33bc2a1e01c64de7fe6-IdgVJmZD.woff2 +0 -0
- package/dist/client/_assets/chunks/a2b0597837e382aca19919c4b74e58c8-7iVOBjzI.woff2 +0 -0
- package/dist/client/_assets/chunks/a373e85c96aa0dc303092429fb837e51-Dvmw8mbX.woff2 +0 -0
- package/dist/client/_assets/chunks/a379ada89abd750c587ded29f77e731c-CgPBsEsv.woff2 +0 -0
- package/dist/client/_assets/chunks/a4475c442c6b259354c9eda62e64e7df-TZ0c8vK-.woff2 +0 -0
- package/dist/client/_assets/chunks/a48be56ce5d3a99dc8f8331398004412-BR_ixH1W.woff2 +0 -0
- package/dist/client/_assets/chunks/a4a39b5f048671aa37c2b2a9581ab08a-EDfDYsUd.woff2 +0 -0
- package/dist/client/_assets/chunks/a534449d67561ac9b06489f64e57ad98-C355TCrD.woff2 +0 -0
- package/dist/client/_assets/chunks/a6e044424780a57610833cc856c15bf5-x1b8FE4H.woff2 +0 -0
- package/dist/client/_assets/chunks/a7c35e42a659347e490c3cb7983b2e7f-BWpA0-oz.woff2 +0 -0
- package/dist/client/_assets/chunks/a7c58070d68dc1724e9d4e781afb78db-ql27pT-6.woff2 +0 -0
- package/dist/client/_assets/chunks/a806759e72987951d6d08b7cbdf36a1b-TvLDwB_u.woff2 +0 -0
- package/dist/client/_assets/chunks/a88481ec8bc7be11cb66e46781a79bb9-Ck-r_sGx.woff2 +0 -0
- package/dist/client/_assets/chunks/a919a8c6fec6d7e8bb21177965f2e9d7-CC5uWzwd.woff2 +0 -0
- package/dist/client/_assets/chunks/a9a016d4a93409f65278327e8a8bb38d-dylrNwF8.woff2 +0 -0
- package/dist/client/_assets/chunks/aa662092cf249707123ca4d575e2764b-CSxNsTUL.woff2 +0 -0
- package/dist/client/_assets/chunks/aae1eda193285ab817a2d1b408440326-CjyjBoRm.woff2 +0 -0
- package/dist/client/_assets/chunks/ab92d41062a7644faa45f50bf384454a-Cq0X8a9H.woff2 +0 -0
- package/dist/client/_assets/chunks/accc43762ad05edfbff66ba1380d192a-B5jIykaZ.woff2 +0 -0
- package/dist/client/_assets/chunks/ad47bcd6d4663026d648c132d969318d-BYdIbDge.woff2 +0 -0
- package/dist/client/_assets/chunks/ad67113f88b59582991cb0c5d33ea19f-DOH1vcIZ.woff2 +0 -0
- package/dist/client/_assets/chunks/adb6e613fb99c6dd660b727101f554a8-DJDw_ZZK.woff2 +0 -0
- package/dist/client/_assets/chunks/adc4a98a89870ef984ee8f4b57c8a3a5-D30vw26R.woff2 +0 -0
- package/dist/client/_assets/chunks/af57a2e8c72f9ba0efb1af771d32c124-D5Zwq9pS.woff2 +0 -0
- package/dist/client/_assets/chunks/b0ae5f374e43dac992a4a5d334cebc0b-Ccoi4dPt.woff2 +0 -0
- package/dist/client/_assets/chunks/b0cb664cb2e1371efda8943c0b7dcd1c-YzGFnelF.woff2 +0 -0
- package/dist/client/_assets/chunks/b0d1cdced482352cf0d3ae58638aacb9-IunWfc6R.woff2 +0 -0
- package/dist/client/_assets/chunks/b12379ab782468d725519cd07a7d15bc-IjbydKs2.woff2 +0 -0
- package/dist/client/_assets/chunks/b320f5d185b2cff933ac549c184031c5-IYnPrIz4.woff2 +0 -0
- package/dist/client/_assets/chunks/b326a8a9c33b554db570da94f60bc380-B6dzaCQg.woff2 +0 -0
- package/dist/client/_assets/chunks/b55486f8a459c838fe329d4e79a8c211-DW8q7oGV.woff2 +0 -0
- package/dist/client/_assets/chunks/b6117ff2993b11bb1fdc7ea3588a010c-JoMYL5W_.woff2 +0 -0
- package/dist/client/_assets/chunks/b61982951bd51b724143c30dfaaa9fe9-D3KhBKl6.woff2 +0 -0
- package/dist/client/_assets/chunks/b91234c10fd8b8c8abc88e03afe66a1f-BABNh2la.woff2 +0 -0
- package/dist/client/_assets/chunks/b9317198f118a1dfd8ddf2b82ec028f3-CiMecxeH.woff2 +0 -0
- package/dist/client/_assets/chunks/ba825ae79b1a6f7e0cce5215fcb5c96f-B6EWU_D5.woff2 +0 -0
- package/dist/client/_assets/chunks/ba9c59d7dfa4494db1bb764ada81467d-D0yPcC1m.woff2 +0 -0
- package/dist/client/_assets/chunks/bab547459d514f46206e340c4bb2dc88-YnBqlGhw.woff2 +0 -0
- package/dist/client/_assets/chunks/bb0b15492b8cdbbec57c0bcfa0aa9241-BjIFbEsN.woff2 +0 -0
- package/dist/client/_assets/chunks/bc6183ac08f0fac78c46f80c10cf7c92-B81nbRb3.woff2 +0 -0
- package/dist/client/_assets/chunks/bc8ccea5abf6598cf3cfa97eb59804bb-B_7IbluG.woff2 +0 -0
- package/dist/client/_assets/chunks/bc95a792cbca6639214c9b0da13392ff-CHQe0E-D.woff2 +0 -0
- package/dist/client/_assets/chunks/bcc39eda837bb7a7a3d37c8c60fffb81-CGd1Zk6S.woff2 +0 -0
- package/dist/client/_assets/chunks/bcfa62f35731856246c146d3a6932bf3-DMDq6w4v.woff2 +0 -0
- package/dist/client/_assets/chunks/bd73264d7f98776708d5d6f3c9b78fcc-d9QWWMym.woff2 +0 -0
- package/dist/client/_assets/chunks/bed610b217d500f5975cfc9fe6157570-B5t7s1bw.woff2 +0 -0
- package/dist/client/_assets/chunks/bedc74b423b7293b6ad0bdecc61c42cc-4Nnh7peP.woff2 +0 -0
- package/dist/client/_assets/chunks/bf8901f8f11d4f433ea17dabc9370ea6-B8dkfvGn.woff2 +0 -0
- package/dist/client/_assets/chunks/bfb00d4a4c48661bd0be99f300a0faae-OxsWZ7l-.woff2 +0 -0
- package/dist/client/_assets/chunks/c0098958e20db68cab90097b5e62516f-VpjQo3vV.woff2 +0 -0
- package/dist/client/_assets/chunks/c063897793f593eb26d6ff7b7baaba18-BWeJJ1sH.woff2 +0 -0
- package/dist/client/_assets/chunks/c1b0df29ae41d764904df84e9ac83d1e-D1N934vs.woff2 +0 -0
- package/dist/client/_assets/chunks/c4143bb9f2fe77b6ccf20088a8904650-nHfYM24e.woff2 +0 -0
- package/dist/client/_assets/chunks/c42c67070cfe99cf823df92d81c7fa6e-B_a405Ez.woff2 +0 -0
- package/dist/client/_assets/chunks/c4bfaa5e50798246e3770718b7a7c84a-B-r6Y_o0.woff2 +0 -0
- package/dist/client/_assets/chunks/c4d749e45ecd5a5aed5a0bb3ebfd355d-CkqE1fRi.woff2 +0 -0
- package/dist/client/_assets/chunks/c6c2971ad1f3221f6cf84028aa0f477e-D7BZGIcY.woff2 +0 -0
- package/dist/client/_assets/chunks/c7ff3f6bbdcd5f604b7343602ab904df-Bq7UjVrZ.woff2 +0 -0
- package/dist/client/_assets/chunks/c84598999133455503042e06f4ab79cb-DIXndJX3.woff2 +0 -0
- package/dist/client/_assets/chunks/c945c62368357d05a53206620460fb30-82gFEf6n.woff2 +0 -0
- package/dist/client/_assets/chunks/c96d83978add28b356c22c4c84916733-BL4VaPIE.woff2 +0 -0
- package/dist/client/_assets/chunks/c97f41eef722121d86f55d553c056a39-CjejgQO2.woff2 +0 -0
- package/dist/client/_assets/chunks/ca62704509932d3232d62918de97af3f-CKWF-3mX.woff2 +0 -0
- package/dist/client/_assets/chunks/ca9a533988d7019597a60d4e17127e0c-ClZ6ygZM.woff2 +0 -0
- package/dist/client/_assets/chunks/cb7ccd6494256f7a2977b7c2b0225592-Cxp8XObm.woff2 +0 -0
- package/dist/client/_assets/chunks/ce022e18a1377ac509443c3c3790b431-DpH0t6Tn.woff2 +0 -0
- package/dist/client/_assets/chunks/ce41d70ce6a069a498525c9e15c45cf2-DGtgv-_b.woff2 +0 -0
- package/dist/client/_assets/chunks/cf2b28f90f47276f7e2688a65e88a101-LVbNkXHj.woff2 +0 -0
- package/dist/client/_assets/chunks/cf9cffe56636322f62b40d61130fbc5e-DNCBMqAo.woff2 +0 -0
- package/dist/client/_assets/chunks/d1f064825fa5784b5c930652bd831cce-BU6qwjp8.woff2 +0 -0
- package/dist/client/_assets/chunks/d20d8944bc0b85f5b2aae4b24f343516-oY9l-NmC.woff2 +0 -0
- package/dist/client/_assets/chunks/d2faabcedd19f016e7b21bce073c0ec8-Dv6eFCf5.woff2 +0 -0
- package/dist/client/_assets/chunks/d320171e57480510f87dbbc7d5264b0a-CcCfjaFX.woff2 +0 -0
- package/dist/client/_assets/chunks/d36644d6502527e1fff205d0c7eca434-kVBj8zy_.woff2 +0 -0
- package/dist/client/_assets/chunks/d3a72a99d365dddfbca8d017a8011368-Bgdmhl3M.woff2 +0 -0
- package/dist/client/_assets/chunks/d40ab99c7a38026f411c8f112f742b48-D7Nm7fg6.woff2 +0 -0
- package/dist/client/_assets/chunks/d42aafa0f246ad3b4c16fe96d3a3a432-CRxTqZXU.woff2 +0 -0
- package/dist/client/_assets/chunks/d4aaf23c13ae808a4bed617afd13aa07-d_T1SKe9.woff2 +0 -0
- package/dist/client/_assets/chunks/d509ee5c8241bc7c3de2039d75564fa5-BpNFD9JE.woff2 +0 -0
- package/dist/client/_assets/chunks/d675d717cd329bfd4c0524f76ae1579c-Bd5_ctoQ.woff2 +0 -0
- package/dist/client/_assets/chunks/d689b1861d7e4377dd72ad3013482612-jPsVOU3F.woff2 +0 -0
- package/dist/client/_assets/chunks/d6bb686bddfbe8f38a36a68e609a8667-C_QduS-X.woff2 +0 -0
- package/dist/client/_assets/chunks/d9020ff69a83b2a6ee1f42ae480f7db0-DLBUPMCe.woff2 +0 -0
- package/dist/client/_assets/chunks/d9047070d72a816b3dba9d40c2d85e69-LTQ42vEp.woff2 +0 -0
- package/dist/client/_assets/chunks/da04549f3f4ed28076b01b8cd710d313-CDXOeduw.woff2 +0 -0
- package/dist/client/_assets/chunks/da7af303f8c645f9a9dbae0e6e32dd35-DCkS2jqi.woff2 +0 -0
- package/dist/client/_assets/chunks/da90a9012ab2d98f759e3fa0820ef502-D0D9d1zg.woff2 +0 -0
- package/dist/client/_assets/chunks/dc6e234ded795e91f76d6647f628fbf0-IkooXcRh.woff2 +0 -0
- package/dist/client/_assets/chunks/dc8f6256445e68199540be9ade33529f-umb5tWbZ.woff2 +0 -0
- package/dist/client/_assets/chunks/dd0cfdfdac0866e66d587e2b5a9e9961-bl4ozE0c.woff2 +0 -0
- package/dist/client/_assets/chunks/de304c8a02e45ded4f8dcc479d167198-ClRu89-4.woff2 +0 -0
- package/dist/client/_assets/chunks/df10d94bea357a43313e20da5d84bd30-CmAgh2oA.woff2 +0 -0
- package/dist/client/_assets/chunks/df9568257eb29b156449fdd4bec5ec76-BMz6aLRr.woff2 +0 -0
- package/dist/client/_assets/chunks/e067cd0ed76c90cd0a93c9339253f20b-6RxCQ4jy.woff2 +0 -0
- package/dist/client/_assets/chunks/e08b07772e7bed3cec2832d43f7fd339-CQCkeKsr.woff2 +0 -0
- package/dist/client/_assets/chunks/e12150d5a39b30be8f567968c7a527b0-DjdoWzeQ.woff2 +0 -0
- package/dist/client/_assets/chunks/e34b2b141e472dc776c86fdf8eea23b0-Cjk7H-Ev.woff2 +0 -0
- package/dist/client/_assets/chunks/e444fd88f1390636e603d0d681538ac8-Bx7CPOZh.woff2 +0 -0
- package/dist/client/_assets/chunks/e53fcb2381eee345db4f6f973dd95a3e-DmkkCUWz.woff2 +0 -0
- package/dist/client/_assets/chunks/e5bd313ef81f687d398aacb11cec3069-CwBEbcji.woff2 +0 -0
- package/dist/client/_assets/chunks/e79898628283edc27180fc39d0d769c1-By2pplQz.woff2 +0 -0
- package/dist/client/_assets/chunks/e8c15be62faf978d208925e79ea6a10d-Ct5M9lTP.woff2 +0 -0
- package/dist/client/_assets/chunks/e8c364c16daa04835bf32d293d2598db-3v4Tut_d.woff2 +0 -0
- package/dist/client/_assets/chunks/e91c4d941ed9f5c2f3e27f205a3a225e-BwOJ2Lst.woff2 +0 -0
- package/dist/client/_assets/chunks/e9c566be7a5d38a9085225f7372bc82b-BCtnCVML.woff2 +0 -0
- package/dist/client/_assets/chunks/e9ebc567a711eeb29019ddae3e0ce7fe-CnNhV49O.woff2 +0 -0
- package/dist/client/_assets/chunks/eaf332445a40942928e5f5750c1c3116-qZ5LSRrj.woff2 +0 -0
- package/dist/client/_assets/chunks/eb5afb3d952b8593782caec6026514b6-BXugZF2T.woff2 +0 -0
- package/dist/client/_assets/chunks/ec86e23683052da5cfdc3b77641bd15a-BeLcbcRc.woff2 +0 -0
- package/dist/client/_assets/chunks/ecb8875a56c7b038b35432fda41ae128-BVqgNf1o.woff2 +0 -0
- package/dist/client/_assets/chunks/eda1b0cb6d1719dd9bedcf3216a9e8de-BJTfLL5z.woff2 +0 -0
- package/dist/client/_assets/chunks/edd6a4f608d04fc0351d7688cfc321e4-d3V4U40c.woff2 +0 -0
- package/dist/client/_assets/chunks/ee6fce20b420a480714607c66d7f97e5-DEtC-G5G.woff2 +0 -0
- package/dist/client/_assets/chunks/eee3836d6ac17ebb2c450bbcbc9db121-C5a2TZjQ.woff2 +0 -0
- package/dist/client/_assets/chunks/et-book-bold-line-figures-BFJr2_zv.woff +0 -0
- package/dist/client/_assets/chunks/et-book-display-italic-old-style-figures-CoeRJAe8.woff +0 -0
- package/dist/client/_assets/chunks/et-book-roman-line-figures-CaA40oOf.woff +0 -0
- package/dist/client/_assets/chunks/f1661731474b78bdf81114daf10b254f-KnMKX_U3.woff2 +0 -0
- package/dist/client/_assets/chunks/f2c58dee206ba9355046fc23d05491f7-Cm4tCWav.woff2 +0 -0
- package/dist/client/_assets/chunks/f50ac27ea4358d67fdda403c2bb52467-WCfYHde1.woff2 +0 -0
- package/dist/client/_assets/chunks/f55334112f8e8be82d65db29887a663f-vDq1zH8k.woff2 +0 -0
- package/dist/client/_assets/chunks/f5738255e92d8dd34a46d1bcdf4c4074-tpghTzL1.woff2 +0 -0
- package/dist/client/_assets/chunks/f740e93f3c0277ecc616594103bca683-C4BgW_ko.woff2 +0 -0
- package/dist/client/_assets/chunks/f7633b5af033d76ff2fb3c3c266d77c5-BCkrZxwO.woff2 +0 -0
- package/dist/client/_assets/chunks/f7d8468cba2335a83ee414ea68291bab-CVnotkZU.woff2 +0 -0
- package/dist/client/_assets/chunks/f91cd722855f4269256eae1187df64ec-DJlAx1A_.woff2 +0 -0
- package/dist/client/_assets/chunks/f9695c6c4df2bf6bc03045ff79d4f01f-DDaYGYHT.woff2 +0 -0
- package/dist/client/_assets/chunks/fa6e58ce4b52695e7ae19bbea6336ec8-B30jEpze.woff2 +0 -0
- package/dist/client/_assets/chunks/fada6eaa68ff8816afe43d2a36c5423e-DlGAWNPb.woff2 +0 -0
- package/dist/client/_assets/chunks/fbcc4bf5367218951172bdee6f77d7a6-Dax2VSCd.woff2 +0 -0
- package/dist/client/_assets/chunks/fc895f5ce66b656f4a933097bf2a8775-DjjysNdf.woff2 +0 -0
- package/dist/client/_assets/chunks/fc9ae5b600fb711f2d67e93ce768cba4-D1lx7LBz.woff2 +0 -0
- package/dist/client/_assets/chunks/fca6720fd14c467d29a90f18ef3859b9-B3btrwq3.woff2 +0 -0
- package/dist/client/_assets/chunks/fd1bb507bcbf04856eeb7f7fd47ea579-NR1nGJTt.woff2 +0 -0
- package/dist/client/_assets/chunks/fd1bceb55d3e0183ac2454b8532fec7d-D3t-v6nn.woff2 +0 -0
- package/dist/client/_assets/chunks/fd2069e6e8588c70b4f11364093b81f2-BoXhYVgw.woff2 +0 -0
- package/dist/client/_assets/chunks/{url-CG0eolsk.js → url-pLre2DM_.js} +1 -1
- package/dist/client/_assets/client-C_kImWZj.css +2 -0
- package/dist/client/_assets/client-D95FNDg5.js +272 -0
- package/dist/client/_assets/client-auth-CXILhW1b.js +4547 -0
- package/dist/client/_assets/client-cjk-jp-DZwrTzQC.css +1 -0
- package/dist/client/_assets/client-cjk-kr-_3ZNI2ZP.css +1 -0
- package/dist/env-CgaH9Mut.js +252 -0
- package/dist/github-api-BkRWnqMx.js +176 -0
- package/dist/github-app-WeadXMb8.js +275 -0
- package/dist/github-sync-7y_nTXx1.js +4723 -0
- package/dist/index.js +88 -2
- package/dist/node.js +71 -30
- package/dist/url-umUptr5z.js +334 -0
- package/package.json +16 -12
- package/src/__tests__/bin/content-cli.test.ts +179 -0
- package/src/__tests__/bin/media-cli.test.ts +192 -0
- package/src/__tests__/export-hugo-build.test.ts +178 -0
- package/src/__tests__/export-import-roundtrip.test.ts +265 -0
- package/src/__tests__/export-service.test.ts +763 -510
- package/src/__tests__/helpers/app.ts +15 -4
- package/src/__tests__/helpers/export-fixtures.ts +108 -0
- package/src/__tests__/import-site-command.test.ts +358 -350
- package/src/__tests__/site-pull-media.test.ts +256 -0
- package/src/app.tsx +68 -13
- package/src/client/__tests__/collection-form-bridge.test.ts +87 -7
- package/src/client/__tests__/collection-page-actions.test.ts +4 -4
- package/src/client/__tests__/collection-picker-order.test.ts +53 -0
- package/src/client/__tests__/collection-sort-menu.test.ts +2 -2
- package/src/client/__tests__/compose-bridge.test.ts +305 -13
- package/src/client/__tests__/compose-launch.test.ts +120 -0
- package/src/client/__tests__/compose-shortcuts.test.ts +141 -0
- package/src/client/__tests__/feed-video-player.test.ts +44 -0
- package/src/client/__tests__/site-header-nav.test.ts +250 -0
- package/src/client/__tests__/sortable-list.test.ts +6 -2
- package/src/client/__tests__/thread-context.test.ts +117 -0
- package/src/client/collection-form-bridge.ts +109 -4
- package/src/client/collection-page-actions.ts +3 -1
- package/src/client/collection-picker-order.ts +104 -0
- package/src/client/components/__tests__/{jant-collection-sidebar.test.ts → jant-collection-directory.test.ts} +91 -10
- package/src/client/components/__tests__/jant-collection-form.test.ts +40 -18
- package/src/client/components/__tests__/jant-compose-dialog.test.ts +1285 -66
- package/src/client/components/__tests__/jant-compose-editor.test.ts +254 -19
- package/src/client/components/__tests__/jant-compose-fullscreen.test.ts +127 -1
- package/src/client/components/__tests__/jant-media-lightbox.test.ts +237 -0
- package/src/client/components/__tests__/jant-nav-manager.test.ts +326 -0
- package/src/client/components/__tests__/jant-post-menu.test.ts +372 -2
- package/src/client/components/__tests__/jant-settings-avatar.test.ts +61 -5
- package/src/client/components/__tests__/jant-settings-general.test.ts +29 -28
- package/src/client/components/__tests__/jant-text-preview.test.ts +206 -0
- package/src/client/components/collection-manager-types.ts +18 -1
- package/src/client/components/compose-types.ts +48 -0
- package/src/client/components/jant-collection-directory.ts +1448 -0
- package/src/client/components/jant-collection-form.ts +65 -17
- package/src/client/components/jant-command-palette.ts +544 -0
- package/src/client/components/jant-compose-dialog.ts +2490 -375
- package/src/client/components/jant-compose-editor.ts +420 -75
- package/src/client/components/jant-compose-fullscreen.ts +59 -8
- package/src/client/components/jant-media-lightbox.ts +373 -18
- package/src/client/components/jant-nav-manager.ts +624 -286
- package/src/client/components/jant-post-menu.ts +451 -126
- package/src/client/components/jant-repo-picker-types.ts +39 -0
- package/src/client/components/jant-repo-picker.ts +799 -0
- package/src/client/components/jant-settings-avatar.ts +16 -2
- package/src/client/components/jant-settings-general.ts +137 -35
- package/src/client/components/jant-text-preview.ts +239 -55
- package/src/client/components/nav-manager-types.ts +25 -9
- package/src/client/components/settings-types.ts +13 -2
- package/src/client/compose-bridge.ts +428 -68
- package/src/client/compose-launch.ts +41 -4
- package/src/client/compose-shortcuts.ts +77 -5
- package/src/client/feed-video-player.ts +374 -0
- package/src/client/image-processor.ts +53 -127
- package/src/client/media-lightbox-events.ts +1 -0
- package/src/client/media-scroll-hint.ts +66 -0
- package/src/client/multipart-upload.ts +4 -1
- package/src/client/palette-shortcuts.ts +35 -0
- package/src/client/search-rank.ts +74 -0
- package/src/client/settings-bridge.ts +7 -0
- package/src/client/site-header-nav.d.ts +1 -0
- package/src/client/site-header-nav.js +139 -51
- package/src/client/sortable-list.ts +2 -0
- package/src/client/thread-context.ts +54 -11
- package/src/client/tiptap/__tests__/block-insertion.test.ts +65 -0
- package/src/client/tiptap/__tests__/footnotes.test.ts +692 -0
- package/src/client/tiptap/__tests__/insert-paragraph-around.test.ts +228 -0
- package/src/client/tiptap/__tests__/link-toolbar.test.ts +120 -4
- package/src/client/tiptap/__tests__/markdown-clipboard.test.ts +48 -0
- package/src/client/tiptap/block-insertion.ts +36 -0
- package/src/client/tiptap/bubble-menu.ts +34 -4
- package/src/client/tiptap/create-editor.ts +49 -30
- package/src/client/tiptap/embed-dialog.ts +301 -0
- package/src/client/tiptap/embed-node.ts +373 -0
- package/src/client/tiptap/embed-paste.ts +91 -0
- package/src/client/tiptap/extensions.ts +80 -28
- package/src/client/tiptap/footnotes.ts +718 -0
- package/src/client/tiptap/html-block-node.ts +222 -0
- package/src/client/tiptap/image-node.ts +49 -6
- package/src/client/tiptap/inline-image-upload.ts +200 -40
- package/src/client/tiptap/insert-paragraph-around.ts +79 -0
- package/src/client/tiptap/link-input-rules.ts +50 -16
- package/src/client/tiptap/link-toolbar.ts +156 -133
- package/src/client/tiptap/markdown-clipboard.ts +120 -0
- package/src/client/tiptap/more-break.ts +36 -3
- package/src/client/tiptap/paste-media.ts +6 -0
- package/src/client/tiptap/slash-commands.ts +167 -14
- package/src/client/tiptap/tab-indent.ts +184 -0
- package/src/client/tiptap/toolbar-mode.ts +15 -2
- package/src/client/tiptap/wrapping-input-rules.ts +102 -0
- package/src/client/types/sortablejs.d.ts +9 -1
- package/src/client/upload-session.ts +2 -1
- package/src/client/video-processor.ts +177 -20
- package/src/client-auth.ts +9 -1
- package/src/client-site.ts +15 -0
- package/src/client.ts +2 -0
- package/src/db/__tests__/demo-canonical-snapshot.test.ts +1 -1
- package/src/db/__tests__/migration-rehearsal.test.ts +5 -1
- package/src/db/__tests__/migrations.test.ts +25 -2
- package/src/db/backfills/0001_strip_collection_path_c_prefix.sql +11 -0
- package/src/db/backfills/0002_strip_collection_path_c_prefix_fix.sql +12 -0
- package/src/db/backfills/0003_clear_system_nav_default_labels.sql +15 -0
- package/src/db/dialect.ts +40 -0
- package/src/db/migrations/0005_busy_the_phantom.sql +39 -0
- package/src/db/migrations/0006_adorable_magdalene.sql +3 -0
- package/src/db/migrations/0007_unusual_warstar.sql +1 -0
- package/src/db/migrations/0008_nasty_lockheed.sql +1 -0
- package/src/db/migrations/0009_clear_fixer.sql +31 -0
- package/src/db/migrations/0010_futuristic_preak.sql +31 -0
- package/src/db/migrations/0011_bizarre_smasher.sql +30 -0
- package/src/db/migrations/0012_furry_thena.sql +52 -0
- package/src/db/migrations/0013_mixed_lightspeed.sql +39 -0
- package/src/db/migrations/0014_high_the_santerians.sql +41 -0
- package/src/db/migrations/0015_skinny_shinobi_shaw.sql +1 -0
- package/src/db/migrations/0016_remarkable_nicolaos.sql +17 -0
- package/src/db/migrations/0017_powerful_moonstone.sql +14 -0
- package/src/db/migrations/0018_yummy_franklin_richards.sql +6 -0
- package/src/db/migrations/meta/0005_snapshot.json +1904 -0
- package/src/db/migrations/meta/0006_snapshot.json +1925 -0
- package/src/db/migrations/meta/0007_snapshot.json +1933 -0
- package/src/db/migrations/meta/0008_snapshot.json +1940 -0
- package/src/db/migrations/meta/0009_snapshot.json +1952 -0
- package/src/db/migrations/meta/0010_snapshot.json +1952 -0
- package/src/db/migrations/meta/0011_snapshot.json +1948 -0
- package/src/db/migrations/meta/0012_snapshot.json +1955 -0
- package/src/db/migrations/meta/0013_snapshot.json +1977 -0
- package/src/db/migrations/meta/0014_snapshot.json +1988 -0
- package/src/db/migrations/meta/0015_snapshot.json +1995 -0
- package/src/db/migrations/meta/0016_snapshot.json +2104 -0
- package/src/db/migrations/meta/0017_snapshot.json +2188 -0
- package/src/db/migrations/meta/0018_snapshot.json +2225 -0
- package/src/db/migrations/meta/_journal.json +98 -0
- package/src/db/migrations/pg/0003_motionless_norrin_radd.sql +21 -0
- package/src/db/migrations/pg/0004_nervous_captain_midlands.sql +3 -0
- package/src/db/migrations/pg/0005_romantic_mesmero.sql +1 -0
- package/src/db/migrations/pg/0006_perpetual_bruce_banner.sql +1 -0
- package/src/db/migrations/pg/0007_nav_item_placement.sql +1 -0
- package/src/db/migrations/pg/0008_yielding_frightful_four.sql +3 -0
- package/src/db/migrations/pg/0009_outstanding_ogun.sql +1 -0
- package/src/db/migrations/pg/0010_overjoyed_gertrude_yorkes.sql +28 -0
- package/src/db/migrations/pg/0011_fixed_hulk.sql +19 -0
- package/src/db/migrations/pg/0012_cute_shockwave.sql +2 -0
- package/src/db/migrations/pg/0013_bizarre_obadiah_stane.sql +1 -0
- package/src/db/migrations/pg/0014_tearful_grim_reaper.sql +16 -0
- package/src/db/migrations/pg/0015_daffy_mikhail_rasputin.sql +14 -0
- package/src/db/migrations/pg/0016_familiar_lionheart.sql +6 -0
- package/src/db/migrations/pg/meta/0003_snapshot.json +2482 -0
- package/src/db/migrations/pg/meta/0004_snapshot.json +2500 -0
- package/src/db/migrations/pg/meta/0005_snapshot.json +2507 -0
- package/src/db/migrations/pg/meta/0006_snapshot.json +2513 -0
- package/src/db/migrations/pg/meta/0008_snapshot.json +2524 -0
- package/src/db/migrations/pg/meta/0009_snapshot.json +2520 -0
- package/src/db/migrations/pg/meta/0010_snapshot.json +2526 -0
- package/src/db/migrations/pg/meta/0011_snapshot.json +2563 -0
- package/src/db/migrations/pg/meta/0012_snapshot.json +2573 -0
- package/src/db/migrations/pg/meta/0013_snapshot.json +2579 -0
- package/src/db/migrations/pg/meta/0014_snapshot.json +2702 -0
- package/src/db/migrations/pg/meta/0015_snapshot.json +2803 -0
- package/src/db/migrations/pg/meta/0016_snapshot.json +2840 -0
- package/src/db/migrations/pg/meta/_journal.json +98 -0
- package/src/db/pg/__tests__/node.test.ts +38 -0
- package/src/db/pg/node.ts +149 -0
- package/src/db/pg/schema.ts +148 -12
- package/src/db/rehearsal-fixtures/demo-current.sql +47 -3
- package/src/db/schema.ts +162 -12
- package/src/i18n/Trans.tsx +3 -3
- package/src/i18n/__tests__/context.test.tsx +9 -6
- package/src/i18n/__tests__/detect.test.ts +65 -48
- package/src/i18n/__tests__/fallback.test.ts +40 -0
- package/src/i18n/__tests__/message-placeholders.test.ts +43 -0
- package/src/i18n/__tests__/middleware.test.ts +83 -0
- package/src/i18n/context.tsx +9 -44
- package/src/i18n/detect.ts +95 -28
- package/src/i18n/i18n.ts +41 -17
- package/src/i18n/index.ts +7 -5
- package/src/i18n/locales/{en.po → public/en.po} +284 -1151
- package/src/i18n/locales/public/en.ts +1 -0
- package/src/i18n/locales/public/zh-Hans.po +2327 -0
- package/src/i18n/locales/public/zh-Hans.ts +1 -0
- package/src/i18n/locales/public/zh-Hant.po +2327 -0
- package/src/i18n/locales/public/zh-Hant.ts +1 -0
- package/src/i18n/locales/settings/en.po +1622 -0
- package/src/i18n/locales/settings/en.ts +1 -0
- package/src/i18n/locales/settings/zh-Hans.po +1622 -0
- package/src/i18n/locales/settings/zh-Hans.ts +1 -0
- package/src/i18n/locales/settings/zh-Hant.po +1622 -0
- package/src/i18n/locales/settings/zh-Hant.ts +1 -0
- package/src/i18n/middleware.ts +37 -24
- package/src/index.ts +4 -6
- package/src/lib/__tests__/collection-groups.test.ts +17 -0
- package/src/lib/__tests__/constants.test.ts +2 -24
- package/src/lib/__tests__/csp-builder.test.ts +68 -0
- package/src/lib/__tests__/display-text.test.ts +27 -0
- package/src/lib/__tests__/embed-providers.test.ts +104 -0
- package/src/lib/__tests__/embed-render.test.ts +60 -0
- package/src/lib/__tests__/feed.test.ts +125 -63
- package/src/lib/__tests__/hosted-signin.test.ts +41 -2
- package/src/lib/__tests__/markdown-roundtrip-embed.test.ts +88 -0
- package/src/lib/__tests__/markdown-to-tiptap.test.ts +59 -0
- package/src/lib/__tests__/markdown.test.ts +47 -5
- package/src/lib/__tests__/navigation.test.ts +132 -0
- package/src/lib/__tests__/post-display.test.ts +3 -2
- package/src/lib/__tests__/post-meta.test.ts +3 -0
- package/src/lib/__tests__/rate-limit-d1.test.ts +82 -0
- package/src/lib/__tests__/rate-limit-memory.test.ts +69 -0
- package/src/lib/__tests__/resolve-config.test.ts +30 -25
- package/src/lib/__tests__/schemas.test.ts +20 -3
- package/src/lib/__tests__/summary.test.ts +540 -1
- package/src/lib/__tests__/theme.test.ts +41 -2
- package/src/lib/__tests__/timeline.test.ts +33 -27
- package/src/lib/__tests__/tiptap-render.test.ts +224 -0
- package/src/lib/__tests__/tiptap-to-markdown.test.ts +51 -0
- package/src/lib/__tests__/video-playback.test.ts +48 -0
- package/src/lib/__tests__/view.test.ts +181 -8
- package/src/lib/__tests__/worker-response-cache.test.ts +311 -0
- package/src/lib/__tests__/youtube.test.ts +99 -0
- package/src/lib/api-media.ts +55 -0
- package/src/lib/api-posts.ts +99 -0
- package/src/lib/api-search.ts +47 -0
- package/src/lib/api-settings.ts +122 -0
- package/src/lib/asset-path.ts +20 -2
- package/src/lib/collection-groups.ts +2 -1
- package/src/lib/collection-paths.ts +40 -0
- package/src/lib/constants.ts +3 -24
- package/src/lib/csp-builder.ts +99 -0
- package/src/lib/decorative-quote-mark.ts +11 -0
- package/src/lib/display-text.ts +56 -0
- package/src/lib/embed-providers.ts +289 -0
- package/src/lib/embed-render.ts +151 -0
- package/src/lib/env.ts +70 -1
- package/src/lib/excerpt.ts +11 -2
- package/src/lib/feed.ts +154 -95
- package/src/lib/footnotes.ts +146 -0
- package/src/lib/github-api.ts +423 -0
- package/src/lib/github-app-state.ts +135 -0
- package/src/lib/github-app.ts +487 -0
- package/src/lib/github-sync-queue-handler.ts +69 -0
- package/src/lib/github-sync-site-config.ts +57 -0
- package/src/lib/github-sync-status.tsx +87 -0
- package/src/lib/github-sync-trigger.ts +199 -0
- package/src/lib/github-sync-worker.ts +72 -0
- package/src/lib/hosted-signin.ts +9 -3
- package/src/lib/hugo-markdown.ts +255 -0
- package/src/lib/job-queue-cf.ts +18 -0
- package/src/lib/job-queue-db.ts +149 -0
- package/src/lib/job-queue.ts +35 -0
- package/src/lib/markdown-manager.ts +864 -0
- package/src/lib/markdown-to-tiptap.ts +4 -323
- package/src/lib/markdown.ts +16 -24
- package/src/lib/media-helpers.ts +1 -0
- package/src/lib/navigation.ts +50 -19
- package/src/lib/post-display.ts +10 -22
- package/src/lib/post-meta.ts +20 -2
- package/src/lib/public-storage.ts +6 -1
- package/src/lib/rate-limit-d1.ts +99 -0
- package/src/lib/rate-limit-memory.ts +105 -0
- package/src/lib/rate-limit.ts +63 -0
- package/src/lib/render.tsx +14 -2
- package/src/lib/resolve-config.ts +32 -11
- package/src/lib/rich-image.ts +89 -0
- package/src/lib/schemas.ts +180 -13
- package/src/lib/summary.ts +162 -20
- package/src/lib/theme.ts +119 -0
- package/src/lib/timeline.ts +63 -42
- package/src/lib/tiptap-render.ts +265 -126
- package/src/lib/tiptap-to-markdown.ts +6 -329
- package/src/lib/upload.ts +97 -6
- package/src/lib/url.ts +34 -0
- package/src/lib/version.ts +18 -0
- package/src/lib/video-playback.ts +73 -0
- package/src/lib/view.ts +168 -94
- package/src/lib/webhook-signature.ts +65 -0
- package/src/lib/worker-response-cache.ts +220 -0
- package/src/lib/youtube.ts +119 -0
- package/src/middleware/__tests__/auth.test.ts +44 -4
- package/src/middleware/__tests__/onboarding.test.ts +3 -3
- package/src/middleware/__tests__/rate-limit.test.ts +113 -0
- package/src/middleware/__tests__/secure-headers.test.ts +28 -1
- package/src/middleware/__tests__/session.test.ts +85 -0
- package/src/middleware/auth.ts +62 -25
- package/src/middleware/config.ts +7 -10
- package/src/middleware/cors.ts +49 -0
- package/src/middleware/rate-limit.ts +54 -0
- package/src/middleware/secure-headers.ts +45 -55
- package/src/middleware/session.ts +36 -0
- package/src/node/__tests__/cli-migrate.test.ts +111 -0
- package/src/node/__tests__/cli-runtime-target.test.ts +1 -1
- package/src/node/__tests__/cli-site-token-env.test.ts +2 -2
- package/src/preset.css +316 -12
- package/src/routes/__tests__/compose.test.ts +4 -2
- package/src/routes/api/__tests__/collections.test.ts +81 -27
- package/src/routes/api/__tests__/mcp.test.ts +389 -0
- package/src/routes/api/__tests__/nav-items.test.ts +163 -2
- package/src/routes/api/__tests__/posts.test.ts +190 -0
- package/src/routes/api/__tests__/search.test.ts +48 -0
- package/src/routes/api/__tests__/settings.test.ts +99 -3
- package/src/routes/api/__tests__/upload-multipart.test.ts +21 -6
- package/src/routes/api/__tests__/upload.test.ts +132 -0
- package/src/routes/api/__tests__/uploads.test.ts +5 -3
- package/src/routes/api/collections.ts +67 -26
- package/src/routes/api/custom-urls.ts +1 -0
- package/src/routes/api/export.ts +8 -4
- package/src/routes/api/github-sync.tsx +380 -0
- package/src/routes/api/internal/__tests__/sites.test.ts +45 -2
- package/src/routes/api/internal/search-reindex.ts +40 -0
- package/src/routes/api/internal/sites.ts +2 -0
- package/src/routes/api/internal/text-attachments.ts +60 -0
- package/src/routes/api/internal/uploads.ts +5 -1
- package/src/routes/api/mcp.ts +30 -0
- package/src/routes/api/nav-items.ts +30 -11
- package/src/routes/api/palette.ts +17 -0
- package/src/routes/api/posts.ts +37 -106
- package/src/routes/api/public/__tests__/posts.test.ts +372 -0
- package/src/routes/api/public/posts.ts +302 -0
- package/src/routes/api/search.ts +13 -46
- package/src/routes/api/settings.ts +44 -58
- package/src/routes/api/upload-multipart.ts +15 -5
- package/src/routes/api/upload.ts +62 -49
- package/src/routes/api/uploads.ts +1 -0
- package/src/routes/auth/__tests__/hosted-sso.test.ts +33 -0
- package/src/routes/auth/__tests__/setup.test.ts +29 -4
- package/src/routes/auth/dev.ts +1 -1
- package/src/routes/auth/hosted-sso-expired-page.tsx +89 -0
- package/src/routes/auth/hosted-sso.ts +42 -1
- package/src/routes/auth/reset.tsx +46 -32
- package/src/routes/auth/setup.tsx +53 -43
- package/src/routes/auth/signin.tsx +57 -29
- package/src/routes/compose.tsx +114 -1
- package/src/routes/dash/__tests__/font-theme.test.ts +6 -27
- package/src/routes/dash/__tests__/settings-avatar.test.ts +2 -3
- package/src/routes/dash/custom-urls.tsx +250 -112
- package/src/routes/dash/settings.tsx +1182 -75
- package/src/routes/feed/__tests__/{rss.test.ts → feed.test.ts} +62 -123
- package/src/routes/feed/__tests__/sitemap.test.ts +320 -4
- package/src/routes/feed/feed.ts +386 -0
- package/src/routes/feed/manifest.ts +67 -0
- package/src/routes/feed/sitemap.ts +211 -27
- package/src/routes/pages/__tests__/collection-routing.test.ts +176 -0
- package/src/routes/pages/__tests__/collections.test.ts +2 -3
- package/src/routes/pages/__tests__/featured.test.ts +2 -3
- package/src/routes/pages/__tests__/page-canonical.test.ts +101 -0
- package/src/routes/pages/archive.tsx +523 -127
- package/src/routes/pages/collection.tsx +197 -43
- package/src/routes/pages/collections.tsx +5 -1
- package/src/routes/pages/home.tsx +26 -17
- package/src/routes/pages/latest.tsx +3 -3
- package/src/routes/pages/page.tsx +255 -3
- package/src/routes/pages/partials.tsx +8 -14
- package/src/routes/pages/search.tsx +17 -1
- package/src/routes/pages/theme-sample.tsx +1 -2
- package/src/runtime/__tests__/node.test.ts +1 -1
- package/src/runtime/cloudflare.ts +4 -0
- package/src/runtime/node.ts +16 -0
- package/src/runtime/readiness.ts +1 -1
- package/src/runtime/site.ts +9 -1
- package/src/services/__tests__/collection.test.ts +137 -54
- package/src/services/__tests__/github-app-installations.test.ts +181 -0
- package/src/services/__tests__/github-sync-classify.test.ts +189 -0
- package/src/services/__tests__/github-sync-push.test.ts +159 -0
- package/src/services/__tests__/media.test.ts +444 -7
- package/src/services/__tests__/navigation.test.ts +271 -14
- package/src/services/__tests__/post-timeline.test.ts +31 -21
- package/src/services/__tests__/post.test.ts +288 -9
- package/src/services/__tests__/search.test.ts +44 -0
- package/src/services/__tests__/settings.test.ts +43 -21
- package/src/services/auth.ts +2 -2
- package/src/services/bootstrap.ts +10 -7
- package/src/services/collection.ts +591 -193
- package/src/services/custom-url.ts +34 -7
- package/src/services/export-theme/assets/client-site.css +2 -0
- package/src/services/export-theme/assets/client-site.js +145 -0
- package/src/services/export-theme/layouts/_default/alias.html +49 -0
- package/src/services/export-theme/layouts/_default/baseof.html +21 -0
- package/src/services/export-theme/layouts/_default/list.html +104 -0
- package/src/services/export-theme/layouts/_default/rss.xml +160 -0
- package/src/services/export-theme/layouts/_default/single.html +15 -0
- package/src/services/export-theme/layouts/archive/list.html +31 -0
- package/src/services/export-theme/layouts/collection/single.html +19 -0
- package/src/services/export-theme/layouts/collections/list.html +111 -0
- package/src/services/export-theme/layouts/featured/list.html +28 -0
- package/src/services/export-theme/layouts/index.html +41 -0
- package/src/services/export-theme/layouts/partials/feed-post-content.xml +107 -0
- package/src/services/export-theme/layouts/partials/footer.html +32 -0
- package/src/services/export-theme/layouts/partials/head.html +52 -0
- package/src/services/export-theme/layouts/partials/header.html +115 -0
- package/src/services/export-theme/layouts/partials/media-gallery.html +246 -0
- package/src/services/export-theme/layouts/partials/pagination.html +55 -0
- package/src/services/export-theme/layouts/partials/post-card.html +127 -0
- package/src/services/export-theme/layouts/partials/reply.html +91 -0
- package/src/services/export-theme/layouts/partials/thread-preview.html +82 -0
- package/src/services/export-theme/layouts/post/list.html +129 -0
- package/src/services/export-theme/styles/main.css +1981 -0
- package/src/services/export-theme/theme.toml +12 -0
- package/src/services/export.ts +1372 -2412
- package/src/services/github-app-installations.ts +302 -0
- package/src/services/github-sync.ts +769 -0
- package/src/services/index.ts +15 -0
- package/src/services/mcp.ts +1324 -0
- package/src/services/media.ts +336 -25
- package/src/services/navigation.ts +115 -23
- package/src/services/path.ts +129 -28
- package/src/services/post.ts +858 -197
- package/src/services/search.ts +3 -0
- package/src/services/settings.ts +29 -15
- package/src/services/site-admin.ts +26 -6
- package/src/services/upload-session.ts +69 -34
- package/src/style-cjk-jp.css +3 -0
- package/src/style-cjk-kr.css +3 -0
- package/src/styles/components.css +160 -59
- package/src/styles/fonts/et-book/et-book-bold-line-figures.woff +0 -0
- package/src/styles/fonts/et-book/et-book-display-italic-old-style-figures.woff +0 -0
- package/src/styles/fonts/et-book/et-book-roman-line-figures.woff +0 -0
- package/src/styles/fonts/et-book.css +31 -0
- package/src/styles/fonts/latin.css +1 -0
- package/src/styles/fonts/noto-serif-jp/400/057a6a98bda7fe57105ddaa99ec82015.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/0acd1fe2b2ea1ad1bfee7ae1fa139d27.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/0c2ab6b295e55f356f8020d4e7747522.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/0c5f9492af03a4fa42c784de94649de1.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/0d9a936885a4c39077438effd3779cbd.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/0e645da524f7cfc0e8c3c03fb2b08428.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/135e83b403475c5dc9e49b43501a5b84.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/13b2c53b30e6a3e4de7132dbc18dbdfc.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/15d95680dc31cc6ce20e0a5464106dc4.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/1733f27476507ca68b68a803bc533cc2.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/188c2db794f3dd7a45889ddbc81da9bb.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/1cf737900dd49c2e88f1b3221a82a602.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/233d3a685ee18276b319363474599d47.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/284b53bbefb06924cf236d24c8ed5641.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/2a56eaf19d1d38a6b57e2a388f733676.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/2abfbab82b6a7c04426afc054d2464da.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/2e6f4bb71ef6b38765d51acc5a79f638.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/31194d303a67561926a544ed0e072aee.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/3623d96698132c61e861f984ce11cc23.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/39211b02f24c69cbd1b54f6d41b01933.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/3e24063b19abd2b051af986a6870046d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/4410f876d27c9821839474b6b1059fd7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/479329a39a3eb6c0047e6b0981919855.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/527ff336279465617aafbafe1a830632.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/56b40518ea0608e62826bd44ee27f5f9.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/5acc7f736217259db79a42bf44241c48.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/5c05f8b75c9d7f25b3c04ca711cf43b3.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/5e0124c7265f1b4cf0fc797b94efbebc.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/615a7e0d63f257109d599b2a1977de68.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/61a2c80d0c924d5a6187c02e8d1d1642.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/62fc94f8d4c5a750b7f25e7387539910.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/639c7c6b0b0c27c738702741cfa4b8c0.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/68a3a4bf337f8a0722be76676e20b850.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/6abdd163d2c4b85698db8aa1ce149361.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/73b794d885b88f7befb7ea8ab1780a15.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/743f290baf027d4626a86e22f3d44600.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/757019c01c5e2c2b0f54293cea3b5636.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/76f117a3baacda304a71965a17266a13.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/77747f17625a259fb405b7bbdd84ac4b.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/788498548ddfb710d6ef3c7bff79fa97.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/78ea6c40923ce95367e3517dd1e5a849.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/7f9a6c9286b68de9c72b0024f7beeb40.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/823d1bb11097331238d103c7f72138dc.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/888ba53510213d5d1f6a7deb60e569b6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/892aa49b2529c89bb4076d4aa51fe30f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/95ff41191c76ef893ac70a1043e95e44.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/96c75862c8cec51a7c05ff025fea86cd.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/9b21e8244f5930c48ad5073f83777b6c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/9fce02d1a09f464c36c9fcfb14a354c5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/a0a6755c7e3f9e4cf03387027dd9f16c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/a4475c442c6b259354c9eda62e64e7df.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/a4a39b5f048671aa37c2b2a9581ab08a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/a534449d67561ac9b06489f64e57ad98.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/a919a8c6fec6d7e8bb21177965f2e9d7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/ad67113f88b59582991cb0c5d33ea19f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/adb6e613fb99c6dd660b727101f554a8.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/adc4a98a89870ef984ee8f4b57c8a3a5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/b12379ab782468d725519cd07a7d15bc.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/b55486f8a459c838fe329d4e79a8c211.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/bb0b15492b8cdbbec57c0bcfa0aa9241.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/bedc74b423b7293b6ad0bdecc61c42cc.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/bfb00d4a4c48661bd0be99f300a0faae.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/c1b0df29ae41d764904df84e9ac83d1e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/c42c67070cfe99cf823df92d81c7fa6e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/c4d749e45ecd5a5aed5a0bb3ebfd355d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/c96d83978add28b356c22c4c84916733.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/cb7ccd6494256f7a2977b7c2b0225592.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/ce41d70ce6a069a498525c9e15c45cf2.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/d509ee5c8241bc7c3de2039d75564fa5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/d675d717cd329bfd4c0524f76ae1579c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/dc8f6256445e68199540be9ade33529f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/e8c15be62faf978d208925e79ea6a10d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/e91c4d941ed9f5c2f3e27f205a3a225e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/e9c566be7a5d38a9085225f7372bc82b.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/e9ebc567a711eeb29019ddae3e0ce7fe.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/ecb8875a56c7b038b35432fda41ae128.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/f1661731474b78bdf81114daf10b254f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/f2c58dee206ba9355046fc23d05491f7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/f50ac27ea4358d67fdda403c2bb52467.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/f55334112f8e8be82d65db29887a663f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/f740e93f3c0277ecc616594103bca683.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/f7633b5af033d76ff2fb3c3c266d77c5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/f7d8468cba2335a83ee414ea68291bab.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/fc9ae5b600fb711f2d67e93ce768cba4.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/fd1bceb55d3e0183ac2454b8532fec7d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/02611a045a7fe83a12014e3debc9f731.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/0f5e1a18987dbc84ca05188c129e1936.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/112743a4ab5fdd1498dfdf2b11336380.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/1211f03d3ff5759a702631445793f60e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/147b24c2f08d03bbed30887d4cb3311c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/16663e567f81f4725a1522f37e18f71f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/1824321da801e6257b902f3d1d09054c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/1a3a92c7c060a71a6c35819b9ebcdbb9.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/1fba7ec0e412e911bf31841de5a8a4e4.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/28a97af9ab9d38983d20f39ff09840e1.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/2bfaadaf3479c72286248e6de0be0ec9.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/2c8c55e4cec85ce77e95cac9d330f5cf.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/2ed9981d2e8983365bd051159b976b6d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/2f4c633e923ba30c6ba376367379fc91.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/349965eee0a9b6c984a319ab96a4ece9.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/36cda3eae13370b934bba4429fab9078.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/3aa13f1843d7c8fa16109b6395a9a29d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/3e2c06bdd9dcab29aeaeb0cfb7fa608a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/424123f9371ba357087363263eeafcda.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/43b3bbf43055c3e65db9cdf2c5b18c1c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/44704471b60c18b3ecec5300a88f1102.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/46604090efbb99c2db1800928af0a239.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/46998df85d31e629c0142f0556f5e7c5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/49902f1c41b281f8eb3771abdd4dccb8.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/4db4f31a16965baa42cb7dad667a8f04.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/572b9e28f1e6d89b765a16d809db84a1.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/5921cef36750ff6092d94b83a802823b.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/59e95d8b5332dcdae894a19946ab767d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/5ec349ff62b9ee5fe0c6dae867704dc4.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/601f812e8b1eafecd5ba585a6a6d7962.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/6025ddaf99d86cc8bfd781006b19fbd6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/60f6e28b3f0400b0e8fe32c3c7116102.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/64a404d675f1d726f0891b7a80794595.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/7158022889c6c177062ac85036e7af10.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/7e38ca789a9e76ac91d9256374e154f0.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/815a0b797465190f60d8b1a04656c42e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/8726558cf297cbda831cc19514897205.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/8855472f3c02f6c7ebb3216617ebe4af.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/8e231d707f0c4f8e3cde90a6b52a79aa.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/9064c12fd72c7aba235d8c3881755b91.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/911b9e53e9814de2998c60bf3115f560.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/92fdc376bce277874e75db666f175b44.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/930a23430f0eb64480d7fe5f82834e21.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/93bed0e5f2dd5a21cf73304fcfbc149d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/93f3ea6918533d96d9c252378b9a4af0.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/95a0951d2a2722ff773a1a45e8c474d6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/982cd1765ca6bbb3e6547e47834d5148.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/9b0c3caac458c7bc9c9133e40405ddea.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/9f3bdab4025dc7c5ba1a5871e62e8918.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/a00e94d4f04df6aa659db9c4954c7efe.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/a2b0597837e382aca19919c4b74e58c8.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/a373e85c96aa0dc303092429fb837e51.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/a6e044424780a57610833cc856c15bf5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/a7c58070d68dc1724e9d4e781afb78db.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/accc43762ad05edfbff66ba1380d192a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/ad47bcd6d4663026d648c132d969318d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/af57a2e8c72f9ba0efb1af771d32c124.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/b0ae5f374e43dac992a4a5d334cebc0b.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/b91234c10fd8b8c8abc88e03afe66a1f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/b9317198f118a1dfd8ddf2b82ec028f3.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/ba825ae79b1a6f7e0cce5215fcb5c96f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/bc6183ac08f0fac78c46f80c10cf7c92.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/bc8ccea5abf6598cf3cfa97eb59804bb.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/bc95a792cbca6639214c9b0da13392ff.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/bcfa62f35731856246c146d3a6932bf3.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/c063897793f593eb26d6ff7b7baaba18.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/c6c2971ad1f3221f6cf84028aa0f477e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/c97f41eef722121d86f55d553c056a39.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/ca62704509932d3232d62918de97af3f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/d1f064825fa5784b5c930652bd831cce.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/d2faabcedd19f016e7b21bce073c0ec8.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/d320171e57480510f87dbbc7d5264b0a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/d3a72a99d365dddfbca8d017a8011368.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/d42aafa0f246ad3b4c16fe96d3a3a432.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/d4aaf23c13ae808a4bed617afd13aa07.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/d6bb686bddfbe8f38a36a68e609a8667.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/d9020ff69a83b2a6ee1f42ae480f7db0.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/da90a9012ab2d98f759e3fa0820ef502.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/df10d94bea357a43313e20da5d84bd30.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/e34b2b141e472dc776c86fdf8eea23b0.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/e444fd88f1390636e603d0d681538ac8.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/e79898628283edc27180fc39d0d769c1.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/eaf332445a40942928e5f5750c1c3116.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/f91cd722855f4269256eae1187df64ec.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/fc895f5ce66b656f4a933097bf2a8775.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/fca6720fd14c467d29a90f18ef3859b9.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/fd1bb507bcbf04856eeb7f7fd47ea579.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/noto-serif-jp.css +3349 -0
- package/src/styles/fonts/noto-serif-kr/400/033466ef683afe931f7f520cfb42d928.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/05da12edb9d52210581dc6ec4541031f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/0968e4861204b51f62a2f8e9f15dd5e0.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/0d5dec931dc885f07fe5cd5af8bed675.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/1076f0f6f66d28d7a2f16427faad4413.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/12ef1ba76bd20b004b865266a1aa70b3.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/152395634a207579552f8cb54db88599.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/1a08931435f885e707edb85978ea3bb6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/31a197213ae61d7043c81013f52d10d6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/37dbd564820cce2f7b3331e442da0df3.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/37dc8505b20b37a2477a9121b16d44a7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/3ac0a07878cacdc98bb889f40f5970d3.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/491bbba374093fff045f55803a22bfef.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/4d38e56738156625329d93bd5c8cc74a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/57e9a86651366c1ba299e47aa7e358c2.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/57f89adeae55aa7fe2add3fc1801ce03.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/5df9ce98c75f8c50fb4fff7f01a23925.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/60020a830d809c26970cf8e48ba1eeda.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/60bcfa3ea7446eb42394aab02d28be40.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/63fafcb069520613d0ea35ad3e6b1e42.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/65a60d87c64228d258a123cbe85f5f31.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/6968e889e18891d912809fe484c2e745.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/70714891ad3fbfc3d5f10a8669dacc5a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/761d84afca8d7f34eebefe538adba827.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/76e07530323418ec723c5d7634c9bcca.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/773819b7b9b8fd404a929867c0fd677e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/7817dd16805145d8538ad57590f69f5a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/7b59f0ec7792b18458dc5a361e37884c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/7c3945788a689a69356c1a622d69d48b.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/835b6505bb9eea9678925a1fa885353d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/8b5c9ef81159f31d2ab35f45ca4373e0.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/8b91c7c2ed390f1278b9befa3aa87233.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/8fb45117a62d92dce44a80f0b729ead5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/90b7848e9b1623b77bdcf155e90b839c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/985d4d41afa0934a4eb2de35473fb9a2.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/9bb38fd05201de666b88b82d56a386f7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/9eff2ab2a9d2ced47ab761bf6fdb11ec.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/9f1963fe8d4d6878d717d872a8f3fdb5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/a0d00fe4816c95a8c7dffd445ef00b03.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/a0f199077fa1a33bc2a1e01c64de7fe6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/a7c35e42a659347e490c3cb7983b2e7f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/aa662092cf249707123ca4d575e2764b.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/aae1eda193285ab817a2d1b408440326.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/b0cb664cb2e1371efda8943c0b7dcd1c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/b0d1cdced482352cf0d3ae58638aacb9.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/b320f5d185b2cff933ac549c184031c5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/ba9c59d7dfa4494db1bb764ada81467d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/bab547459d514f46206e340c4bb2dc88.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/bcc39eda837bb7a7a3d37c8c60fffb81.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/bd73264d7f98776708d5d6f3c9b78fcc.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/bed610b217d500f5975cfc9fe6157570.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/bf8901f8f11d4f433ea17dabc9370ea6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/c4143bb9f2fe77b6ccf20088a8904650.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/c7ff3f6bbdcd5f604b7343602ab904df.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/c945c62368357d05a53206620460fb30.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/ce022e18a1377ac509443c3c3790b431.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/cf9cffe56636322f62b40d61130fbc5e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/d20d8944bc0b85f5b2aae4b24f343516.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/d36644d6502527e1fff205d0c7eca434.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/d40ab99c7a38026f411c8f112f742b48.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/dc6e234ded795e91f76d6647f628fbf0.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/dd0cfdfdac0866e66d587e2b5a9e9961.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/de304c8a02e45ded4f8dcc479d167198.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/e8c364c16daa04835bf32d293d2598db.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/ec86e23683052da5cfdc3b77641bd15a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/eda1b0cb6d1719dd9bedcf3216a9e8de.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/ee6fce20b420a480714607c66d7f97e5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/eee3836d6ac17ebb2c450bbcbc9db121.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/f5738255e92d8dd34a46d1bcdf4c4074.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/fa6e58ce4b52695e7ae19bbea6336ec8.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/fd2069e6e8588c70b4f11364093b81f2.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/04b8bde59cff68eeee74fb1914eb09c7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/05ac821472e235943ed1435e4bb8ecad.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/074c1f8483d5a4d8c45c8c5f3e4cbb32.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/1380210a722ac9aeef54005ad7b015c9.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/17c7f5d0a45e92ede0e5dec3a2c77efa.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/1be602ad456b0d75902d352116cb35fd.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/2612c60f9dc5459ac42763591240a1d6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/2ab8cf1f23a5ac7939a7876054e711a7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/34e6750e00c3a911ef87dc66b336594d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/36f3df4730cfca4fc77fe52b52e6a126.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/37887deb7a9c466cf2af6ee7ff372ea6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/3aba89c4d4281553078de4622b498cbb.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/3d88558097559d775231194d43745ba7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/3dc3e8c35524cad2d1236f3393104ccf.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/45140742dfa8686b58db2899ce89f89a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/469d2754e315e77270c12fa0ab28026c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/4c0760284569b87d8f2290c324953973.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/4cba92c942694bdc479bc54101bb1aa4.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/4e7d263c30bb48604069a3b4404d4eeb.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/5279c7e465d9871edf51fd02e691eee7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/54bfdb6f21f9d6191e5100580239e14f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/56be5e32fd01668fe5ba814d94905839.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/615b38e0b5bb3ea68426f44f47706e6f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/65e9c4585d71bf48a5c62f367010da16.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/6b75213eb0be40ce84241eb2bb438a2e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/7520ca6ca8c60eb1e62d50e71c8d30f1.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/759a647b77791b1e98f99bc0ab5317a7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/76e51630143b95b6322ef93ad330da7a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/79aa7c8c842c4a27cf57c0a3a1ffca5a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/7cc72fd2c9105560422b6a67c6162945.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/7d0ea0690e432462f4d05a23d720ec19.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/7dffa5c1bec57e0903fd62357401ff1a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/82f6ccc063960eed1cdfd1d61ada8862.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/845b4b67564d62cf5cad242f7ac08ea5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/84c8e7bc0931008ed91f44ed12dfea94.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/887a11290ba78b1e66c6d2f67043005e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/88db1d042074fb6e66821ffc10941930.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/8dc18c0cebe6aa4bf4c45dbb831048ab.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/919a879bd2d580d8491a31a449390689.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/91da6cb174bebfb96976e2f1316be2fe.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/934dfdbed2bb2c4b6129199d699a34fa.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/97d7a17234f2b5030ae9697ae00aded2.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/995c4fda62d25c3b79dd5987737df6ae.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/9d0afa53dc2f92ba42024f55f11f6779.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/9e13a47242926f1cd7d68c08d9ef8889.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/a379ada89abd750c587ded29f77e731c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/a48be56ce5d3a99dc8f8331398004412.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/a806759e72987951d6d08b7cbdf36a1b.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/a88481ec8bc7be11cb66e46781a79bb9.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/a9a016d4a93409f65278327e8a8bb38d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/ab92d41062a7644faa45f50bf384454a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/b326a8a9c33b554db570da94f60bc380.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/b6117ff2993b11bb1fdc7ea3588a010c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/b61982951bd51b724143c30dfaaa9fe9.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/c0098958e20db68cab90097b5e62516f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/c4bfaa5e50798246e3770718b7a7c84a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/c84598999133455503042e06f4ab79cb.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/ca9a533988d7019597a60d4e17127e0c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/cf2b28f90f47276f7e2688a65e88a101.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/d689b1861d7e4377dd72ad3013482612.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/d9047070d72a816b3dba9d40c2d85e69.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/da04549f3f4ed28076b01b8cd710d313.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/da7af303f8c645f9a9dbae0e6e32dd35.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/df9568257eb29b156449fdd4bec5ec76.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/e067cd0ed76c90cd0a93c9339253f20b.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/e08b07772e7bed3cec2832d43f7fd339.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/e12150d5a39b30be8f567968c7a527b0.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/e53fcb2381eee345db4f6f973dd95a3e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/e5bd313ef81f687d398aacb11cec3069.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/eb5afb3d952b8593782caec6026514b6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/edd6a4f608d04fc0351d7688cfc321e4.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/f9695c6c4df2bf6bc03045ff79d4f01f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/fada6eaa68ff8816afe43d2a36c5423e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/fbcc4bf5367218951172bdee6f77d7a6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/noto-serif-kr.css +2601 -0
- package/src/styles/site-media.css +823 -0
- package/src/styles/tokens.css +292 -21
- package/src/styles/ui.css +4188 -1857
- package/src/types/app-context.ts +20 -0
- package/src/types/bindings.ts +3 -1
- package/src/types/config.ts +147 -9
- package/src/types/constants.ts +46 -11
- package/src/types/entities.ts +24 -8
- package/src/types/operations.ts +80 -1
- package/src/types/props.ts +10 -9
- package/src/types/raw-assets.d.ts +39 -0
- package/src/types/views.ts +28 -7
- package/src/ui/__tests__/color-themes.test.ts +7 -40
- package/src/ui/__tests__/font-themes.test.ts +30 -11
- package/src/ui/color-themes.ts +311 -270
- package/src/ui/compose/ComposeDialog.tsx +712 -394
- package/src/ui/compose/ComposePrompt.tsx +15 -10
- package/src/ui/dash/ActionButtons.tsx +27 -18
- package/src/ui/dash/DangerZone.tsx +15 -10
- package/src/ui/dash/FormatBadge.tsx +21 -8
- package/src/ui/dash/StatusBadge.tsx +33 -22
- package/src/ui/dash/appearance/AdvancedContent.tsx +44 -30
- package/src/ui/dash/appearance/CodeInjectionContent.tsx +164 -0
- package/src/ui/dash/appearance/ColorThemeContent.tsx +127 -160
- package/src/ui/dash/appearance/FontThemeContent.tsx +20 -15
- package/src/ui/dash/appearance/NavigationContent.tsx +368 -225
- package/src/ui/dash/appearance/__tests__/NavigationContent.test.tsx +54 -0
- package/src/ui/dash/settings/AccountContent.tsx +34 -23
- package/src/ui/dash/settings/AccountMenuContent.tsx +226 -137
- package/src/ui/dash/settings/ApiTokensContent.tsx +122 -79
- package/src/ui/dash/settings/AvatarContent.tsx +73 -45
- package/src/ui/dash/settings/DeleteAccountContent.tsx +106 -66
- package/src/ui/dash/settings/GeneralContent.tsx +274 -157
- package/src/ui/dash/settings/GitHubSyncContent.tsx +558 -0
- package/src/ui/dash/settings/SessionsContent.tsx +79 -38
- package/src/ui/dash/settings/SettingsRootContent.tsx +209 -109
- package/src/ui/dash/settings/__tests__/AccountMenuContent.test.tsx +67 -0
- package/src/ui/dash/settings/__tests__/GeneralContent.test.tsx +75 -0
- package/src/ui/feed/CuratedThreadPreview.tsx +24 -14
- package/src/ui/feed/LinkCard.tsx +116 -84
- package/src/ui/feed/LinkPreview.tsx +75 -0
- package/src/ui/feed/NoteCard.tsx +60 -16
- package/src/ui/feed/PostStatusBadges.tsx +15 -0
- package/src/ui/feed/QuoteCard.tsx +10 -3
- package/src/ui/feed/ThreadPreview.tsx +62 -36
- package/src/ui/feed/TimelineFeed.tsx +2 -1
- package/src/ui/feed/__tests__/thread-preview.test.ts +220 -11
- package/src/ui/feed/__tests__/timeline-cards.test.ts +186 -5
- package/src/ui/feed/thread-preview-state.ts +23 -10
- package/src/ui/font-themes.ts +57 -97
- package/src/ui/layouts/BaseLayout.tsx +230 -136
- package/src/ui/layouts/SiteLayout.tsx +431 -143
- package/src/ui/layouts/__tests__/BaseLayout.test.tsx +76 -6
- package/src/ui/layouts/__tests__/SiteLayout.test.tsx +76 -0
- package/src/ui/pages/ArchivePage.tsx +320 -207
- package/src/ui/pages/BrandPage.tsx +499 -341
- package/src/ui/pages/CollectionEditorPage.tsx +50 -30
- package/src/ui/pages/CollectionPage.tsx +151 -78
- package/src/ui/pages/CollectionsPage.tsx +27 -18
- package/src/ui/pages/ComposePage.tsx +15 -10
- package/src/ui/pages/FeaturedPage.tsx +16 -11
- package/src/ui/pages/HomePage.tsx +15 -10
- package/src/ui/pages/PostPage.tsx +5 -1
- package/src/ui/pages/SearchPage.tsx +93 -43
- package/src/ui/pages/ThemeSamplePage.tsx +758 -520
- package/src/ui/pages/__tests__/ArchivePage.test.tsx +72 -0
- package/src/ui/shared/CollectionDirectory.tsx +215 -34
- package/src/ui/shared/CollectionsManager.tsx +155 -75
- package/src/ui/shared/DecorativeQuoteMark.tsx +12 -9
- package/src/ui/shared/MediaGallery.tsx +350 -256
- package/src/ui/shared/Pagination.tsx +36 -25
- package/src/ui/shared/PostFooter.tsx +139 -96
- package/src/ui/shared/__tests__/media-gallery.test.ts +27 -0
- package/src/ui/shared/__tests__/navigation-labels.test.ts +79 -28
- package/src/ui/shared/__tests__/post-footer.test.ts +42 -17
- package/src/ui/shared/collection-management-labels.ts +109 -33
- package/src/ui/shared/navigation-labels.ts +78 -8
- package/bin/lib/site-localize-media.js +0 -427
- package/dist/client/_assets/client-auth.js +0 -3260
- package/dist/client/_assets/client.css +0 -2
- package/dist/client/_assets/client.js +0 -380
- package/src/__tests__/site-localize-media.test.ts +0 -150
- package/src/client/components/jant-collection-sidebar.ts +0 -816
- package/src/i18n/locales/en.ts +0 -1
- package/src/i18n/locales/zh-Hans.po +0 -3197
- package/src/i18n/locales/zh-Hans.ts +0 -1
- package/src/i18n/locales/zh-Hant.po +0 -3197
- package/src/i18n/locales/zh-Hant.ts +0 -1
- package/src/routes/feed/rss.ts +0 -216
- package/src/types/lingui-react-macro.d.ts +0 -34
- /package/dist/client/_assets/chunks/{heic-to-XcUDQvtx.js → heic-to-DUUaO23q.js} +0 -0
- /package/dist/client/_assets/chunks/{module-ChVQstFd.js → module-DcsAZQZ_.js} +0 -0
- /package/dist/client/_assets/chunks/{native-CR5HLOyf.js → native-DpcrFAPh.js} +0 -0
- /package/dist/client/_assets/{client-cjk.css → client-cjk-B7Z0snDu.css} +0 -0
- /package/dist/client/_assets/{client-cjk-tc.css → client-cjk-tc-BesJYrb2.css} +0 -0
package/src/services/export.ts
CHANGED
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Export Service
|
|
3
3
|
*
|
|
4
|
-
* Generates a ready-to-use
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Generates a ready-to-use Hugo static site as a ZIP archive.
|
|
5
|
+
*
|
|
6
|
+
* Content layout:
|
|
7
|
+
* - Each thread root is a Hugo branch bundle
|
|
8
|
+
* (`content/{root-slug}/_index.md`).
|
|
9
|
+
* - Each reply is a nested leaf bundle
|
|
10
|
+
* (`content/{root-slug}/{reply-slug}/index.md`) with
|
|
11
|
+
* `build: { render: never, list: local }` so only the parent thread
|
|
12
|
+
* page renders it while it still appears in `.Pages`.
|
|
13
|
+
* - `/{reply-slug}/` URLs redirect to the parent thread via Hugo's
|
|
14
|
+
* `aliases:` mechanism and a custom `_default/alias.html` that injects
|
|
15
|
+
* the reply anchor at runtime.
|
|
16
|
+
* - Media is emitted next to each bundle as Hugo page resources.
|
|
17
|
+
*
|
|
18
|
+
* Real Hugo templates and CSS are scaffolded as placeholders here and
|
|
19
|
+
* filled in by Commit 5.
|
|
7
20
|
*/
|
|
8
21
|
|
|
9
22
|
import type { PostService } from "./post.js";
|
|
@@ -11,41 +24,87 @@ import type { PathService } from "./path.js";
|
|
|
11
24
|
import type { CollectionService } from "./collection.js";
|
|
12
25
|
import type { MediaService } from "./media.js";
|
|
13
26
|
import {
|
|
14
|
-
buildJantLogoSvgMarkup,
|
|
15
27
|
getDefaultJantAppleTouchIconBytes,
|
|
16
28
|
getDefaultJantFaviconIcoBytes,
|
|
17
|
-
HOME_BRANDING_LINK_LABEL,
|
|
18
|
-
HOME_BRANDING_PREFIX,
|
|
19
|
-
JANT_REPO_URL,
|
|
20
29
|
} from "../lib/jant-branding.js";
|
|
21
30
|
import { tiptapJsonToMarkdown } from "../lib/tiptap-to-markdown.js";
|
|
31
|
+
import { extractBodyText } from "../lib/summary.js";
|
|
22
32
|
import { getMediaUrl, getPublicUrlForProvider } from "../lib/image.js";
|
|
23
|
-
import { FEATURED_SPARKLE_PATH } from "../lib/featured-icons.js";
|
|
24
|
-
import { escapeHtml } from "../lib/html.js";
|
|
25
33
|
import { render as renderMarkdown } from "../lib/markdown.js";
|
|
26
|
-
import { toISOString } from "../lib/time.js";
|
|
34
|
+
import { formatRelativeAge, toISOString } from "../lib/time.js";
|
|
35
|
+
import {
|
|
36
|
+
formatFrontMatter,
|
|
37
|
+
type HugoCollectionRef,
|
|
38
|
+
type HugoFrontMatter,
|
|
39
|
+
type JantMedia,
|
|
40
|
+
} from "../lib/hugo-markdown.js";
|
|
41
|
+
// Shared design tokens — single source of truth for colors, typography,
|
|
42
|
+
// and layout variables. Consumed verbatim by both the main site (via
|
|
43
|
+
// Tailwind) and the Hugo export (written to static/tokens.css). Using
|
|
44
|
+
// ?raw inlines the file contents as a string at build time so the
|
|
45
|
+
// Worker bundle ships without any filesystem access.
|
|
46
|
+
import TOKENS_CSS from "../styles/tokens.css?raw";
|
|
47
|
+
|
|
48
|
+
// Placeholder Hugo theme files — real templates and styles land in Commit 5.
|
|
49
|
+
// We import them as Vite `?raw` strings so the Worker bundle has no runtime
|
|
50
|
+
// filesystem dependency.
|
|
51
|
+
import THEME_TOML from "./export-theme/theme.toml?raw";
|
|
52
|
+
import THEME_STYLE_MAIN_CSS from "./export-theme/styles/main.css?raw";
|
|
53
|
+
// Static-site client bundle — Lit-based lightbox, feed video autoplay, audio
|
|
54
|
+
// waveform, and gallery scroll hints. Built by `vite.config.site.ts` into
|
|
55
|
+
// `export-theme/assets/client-site.{js,css}` and shipped under the theme's
|
|
56
|
+
// reserved `_jant/` static directory so the theme stays version-aligned with
|
|
57
|
+
// `@jant/core`. The assets folder is regenerated on every build, so the
|
|
58
|
+
// checked-in files are source-of-truth for the most recent release.
|
|
59
|
+
import CLIENT_SITE_JS from "./export-theme/assets/client-site.js?raw";
|
|
60
|
+
import CLIENT_SITE_CSS from "./export-theme/assets/client-site.css?raw";
|
|
61
|
+
import LAYOUT_BASEOF from "./export-theme/layouts/_default/baseof.html?raw";
|
|
62
|
+
import LAYOUT_SINGLE from "./export-theme/layouts/_default/single.html?raw";
|
|
63
|
+
import LAYOUT_LIST from "./export-theme/layouts/_default/list.html?raw";
|
|
64
|
+
import LAYOUT_ALIAS from "./export-theme/layouts/_default/alias.html?raw";
|
|
65
|
+
import LAYOUT_INDEX from "./export-theme/layouts/index.html?raw";
|
|
66
|
+
import LAYOUT_POST_LIST from "./export-theme/layouts/post/list.html?raw";
|
|
67
|
+
import LAYOUT_FEATURED_LIST from "./export-theme/layouts/featured/list.html?raw";
|
|
68
|
+
import LAYOUT_ARCHIVE_LIST from "./export-theme/layouts/archive/list.html?raw";
|
|
69
|
+
import LAYOUT_COLLECTIONS_LIST from "./export-theme/layouts/collections/list.html?raw";
|
|
70
|
+
import LAYOUT_COLLECTION_SINGLE from "./export-theme/layouts/collection/single.html?raw";
|
|
71
|
+
import PARTIAL_HEAD from "./export-theme/layouts/partials/head.html?raw";
|
|
72
|
+
import PARTIAL_HEADER from "./export-theme/layouts/partials/header.html?raw";
|
|
73
|
+
import PARTIAL_FOOTER from "./export-theme/layouts/partials/footer.html?raw";
|
|
74
|
+
import PARTIAL_PAGINATION from "./export-theme/layouts/partials/pagination.html?raw";
|
|
75
|
+
import PARTIAL_POST_CARD from "./export-theme/layouts/partials/post-card.html?raw";
|
|
76
|
+
import PARTIAL_MEDIA_GALLERY from "./export-theme/layouts/partials/media-gallery.html?raw";
|
|
77
|
+
import PARTIAL_REPLY from "./export-theme/layouts/partials/reply.html?raw";
|
|
78
|
+
import PARTIAL_THREAD_PREVIEW from "./export-theme/layouts/partials/thread-preview.html?raw";
|
|
79
|
+
import LAYOUT_RSS from "./export-theme/layouts/_default/rss.xml?raw";
|
|
80
|
+
import PARTIAL_FEED_POST_CONTENT from "./export-theme/layouts/partials/feed-post-content.xml?raw";
|
|
81
|
+
|
|
27
82
|
import type { StorageDriver } from "../lib/storage.js";
|
|
28
83
|
import { base64ToUint8Array } from "../lib/favicon.js";
|
|
29
|
-
import type {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
84
|
+
import type { Post, Collection, Media, NavItem } from "../types.js";
|
|
85
|
+
|
|
86
|
+
/** A file entry in the exported Hugo site. */
|
|
87
|
+
export interface ExportFile {
|
|
88
|
+
path: string;
|
|
89
|
+
content: string | Uint8Array;
|
|
90
|
+
}
|
|
36
91
|
|
|
37
92
|
export interface ExportService {
|
|
38
|
-
|
|
93
|
+
/** Generate a flat list of files for a complete Hugo site. */
|
|
94
|
+
generateHugoFiles(): Promise<ExportFile[]>;
|
|
95
|
+
/** Generate a ZIP archive of the Hugo site. */
|
|
96
|
+
generateHugoSite(): Promise<Uint8Array>;
|
|
39
97
|
}
|
|
40
98
|
|
|
41
|
-
interface SiteConfig {
|
|
99
|
+
export interface SiteConfig {
|
|
42
100
|
siteName: string;
|
|
43
101
|
siteUrl: string;
|
|
44
102
|
siteDescription: string;
|
|
45
103
|
siteLanguage: string;
|
|
46
104
|
showJantBrandingOnHome: boolean;
|
|
47
105
|
homeDefaultView: string;
|
|
48
|
-
|
|
106
|
+
/** "latest" or "featured" — drives the default RSS nav link in the exported site. */
|
|
107
|
+
mainRssFeed: string;
|
|
49
108
|
siteFooter: string;
|
|
50
109
|
showHeaderAvatar: boolean;
|
|
51
110
|
siteAvatarUrl: string;
|
|
@@ -66,31 +125,58 @@ interface SiteConfig {
|
|
|
66
125
|
sitePathPrefix?: string;
|
|
67
126
|
navItems: Pick<
|
|
68
127
|
NavItem,
|
|
69
|
-
"type" | "systemKey" | "label" | "url" | "position"
|
|
128
|
+
"type" | "systemKey" | "label" | "url" | "position" | "placement"
|
|
70
129
|
>[];
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
mimeType: string;
|
|
78
|
-
originalName: string;
|
|
79
|
-
size: number;
|
|
80
|
-
width: number | null;
|
|
81
|
-
height: number | null;
|
|
82
|
-
alt: string | null;
|
|
83
|
-
position: string;
|
|
84
|
-
blurhash: string | null;
|
|
85
|
-
waveform: string | null;
|
|
86
|
-
summary: string | null;
|
|
87
|
-
chars: number | null;
|
|
88
|
-
contentFormat?: TextAttachmentContent["contentFormat"];
|
|
89
|
-
content?: string;
|
|
130
|
+
/** Items per page for Hugo pagination — kept in sync with the main site's PAGE_SIZE. */
|
|
131
|
+
pageSize: number;
|
|
132
|
+
/** Items per archive page — kept in sync with the main site's ARCHIVE_PAGE_SIZE. */
|
|
133
|
+
archivePageSize: number;
|
|
134
|
+
/** Max items per Atom feed — kept in sync with the main site's rssFeedLimit. */
|
|
135
|
+
rssFeedLimit: number;
|
|
90
136
|
}
|
|
91
137
|
|
|
92
138
|
type IconExportMode = "default" | "custom";
|
|
93
139
|
|
|
140
|
+
type ExportedCollectionDirectoryItem =
|
|
141
|
+
| {
|
|
142
|
+
type: "collection";
|
|
143
|
+
sequence: string;
|
|
144
|
+
slug: string;
|
|
145
|
+
title: string;
|
|
146
|
+
/** Rendered HTML of the collection description, or null if empty. */
|
|
147
|
+
descriptionHtml?: string | null;
|
|
148
|
+
entryCount?: number;
|
|
149
|
+
recentActivityLabel?: string | null;
|
|
150
|
+
recentActivityIso?: string | null;
|
|
151
|
+
}
|
|
152
|
+
| {
|
|
153
|
+
type: "divider";
|
|
154
|
+
label: string | null;
|
|
155
|
+
}
|
|
156
|
+
| {
|
|
157
|
+
type: "link";
|
|
158
|
+
sequence: string;
|
|
159
|
+
label: string;
|
|
160
|
+
url: string;
|
|
161
|
+
/** Rendered HTML of the link description, or null if empty. */
|
|
162
|
+
descriptionHtml?: string | null;
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
interface ExportCollectionDirectorySourceItem {
|
|
166
|
+
type: "collection" | "divider" | "link";
|
|
167
|
+
label?: string | null;
|
|
168
|
+
url?: string | null;
|
|
169
|
+
description?: string | null;
|
|
170
|
+
collection?: {
|
|
171
|
+
id: string;
|
|
172
|
+
slug: string;
|
|
173
|
+
title: string;
|
|
174
|
+
description?: string | null;
|
|
175
|
+
postCount?: number;
|
|
176
|
+
recentActivityAt?: number;
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
94
180
|
interface SiteIconAssets {
|
|
95
181
|
faviconBytes: Uint8Array;
|
|
96
182
|
faviconMode: IconExportMode;
|
|
@@ -98,6 +184,41 @@ interface SiteIconAssets {
|
|
|
98
184
|
appleTouchMode: IconExportMode;
|
|
99
185
|
}
|
|
100
186
|
|
|
187
|
+
interface ExportedCollectionMetrics {
|
|
188
|
+
postCount: number;
|
|
189
|
+
recentActivityAt: number;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* A single `collections:` front-matter entry, already resolved to its
|
|
194
|
+
* Hugo-visible slug. Assembled from
|
|
195
|
+
* `collectionService.getCollectionEntriesByPostIds` + `collectionSlugMap`.
|
|
196
|
+
*/
|
|
197
|
+
interface ExportedCollectionEntry {
|
|
198
|
+
slug: string;
|
|
199
|
+
/**
|
|
200
|
+
* Denormalized collection title. Kept alongside the slug so the exported
|
|
201
|
+
* front matter can render a tag label without templates having to resolve
|
|
202
|
+
* another page. Optional because legacy call sites may not supply it.
|
|
203
|
+
*/
|
|
204
|
+
title?: string;
|
|
205
|
+
/** Unix seconds. */
|
|
206
|
+
collectedAt: number;
|
|
207
|
+
position: number;
|
|
208
|
+
/** Unix seconds, or null when not pinned in this collection. */
|
|
209
|
+
pinnedAt: number | null;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function buildDefaultAppleTouchAsset(): Pick<
|
|
213
|
+
SiteIconAssets,
|
|
214
|
+
"appleTouchBytes" | "appleTouchMode"
|
|
215
|
+
> {
|
|
216
|
+
return {
|
|
217
|
+
appleTouchBytes: getDefaultJantAppleTouchIconBytes(),
|
|
218
|
+
appleTouchMode: "default",
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
101
222
|
export function createExportService(
|
|
102
223
|
services: {
|
|
103
224
|
posts: PostService;
|
|
@@ -109,15 +230,22 @@ export function createExportService(
|
|
|
109
230
|
deps: { storage?: StorageDriver | null } = {},
|
|
110
231
|
): ExportService {
|
|
111
232
|
return {
|
|
112
|
-
async
|
|
233
|
+
async generateHugoFiles() {
|
|
234
|
+
const collectionDirectoryDataPromise =
|
|
235
|
+
typeof services.collections.listDirectoryData === "function"
|
|
236
|
+
? services.collections.listDirectoryData()
|
|
237
|
+
: Promise.resolve(null);
|
|
238
|
+
|
|
113
239
|
// 1. Query all data
|
|
114
|
-
const [allPosts, allCollections] =
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
240
|
+
const [allPosts, allCollections, collectionDirectoryData] =
|
|
241
|
+
await Promise.all([
|
|
242
|
+
services.posts.list({
|
|
243
|
+
excludeReplies: false,
|
|
244
|
+
limit: 10000,
|
|
245
|
+
}),
|
|
246
|
+
services.collections.list(),
|
|
247
|
+
collectionDirectoryDataPromise,
|
|
248
|
+
]);
|
|
121
249
|
|
|
122
250
|
const allPostIds = allPosts.map((p) => p.id);
|
|
123
251
|
const roots = allPosts.filter((p) => p.replyToId === null);
|
|
@@ -126,23 +254,45 @@ export function createExportService(
|
|
|
126
254
|
|
|
127
255
|
const [
|
|
128
256
|
collectionsByPost,
|
|
257
|
+
collectionEntriesByPost,
|
|
129
258
|
rawMediaByPost,
|
|
130
259
|
slugMap,
|
|
131
260
|
aliasMap,
|
|
132
261
|
collectionSlugMap,
|
|
133
262
|
] = await Promise.all([
|
|
134
263
|
services.collections.getCollectionsByPostIds(allPostIds),
|
|
264
|
+
services.collections.getCollectionEntriesByPostIds(allPostIds),
|
|
135
265
|
services.media.getByPostIds(allPostIds),
|
|
136
266
|
services.paths.getPostSlugMap(allPostIds),
|
|
137
267
|
services.paths.getPostAliases(rootPostIds),
|
|
138
268
|
services.paths.getCollectionSlugMap(allCollections.map((c) => c.id)),
|
|
139
269
|
]);
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
);
|
|
270
|
+
// Denormalized title lookup so front-matter collection refs can
|
|
271
|
+
// include a title label without templates having to resolve another
|
|
272
|
+
// page. Source of truth is still `slug` on round-trip; `title` is
|
|
273
|
+
// refreshed from DB on every export.
|
|
274
|
+
const collectionTitleMap = new Map<string, string>();
|
|
275
|
+
for (const collection of allCollections) {
|
|
276
|
+
collectionTitleMap.set(collection.id, collection.title);
|
|
277
|
+
}
|
|
278
|
+
|
|
145
279
|
const iconAssets = await buildSiteIconAssets(siteConfig, deps.storage);
|
|
280
|
+
const collectionMetrics = buildExportedCollectionMetrics(
|
|
281
|
+
allCollections,
|
|
282
|
+
allPosts,
|
|
283
|
+
collectionsByPost,
|
|
284
|
+
);
|
|
285
|
+
const exportedCollectionDirectoryItems =
|
|
286
|
+
buildExportedCollectionDirectoryItems(
|
|
287
|
+
collectionDirectoryData?.items ??
|
|
288
|
+
allCollections.map((collection) => ({
|
|
289
|
+
id: collection.id,
|
|
290
|
+
type: "collection" as const,
|
|
291
|
+
collection,
|
|
292
|
+
})),
|
|
293
|
+
collectionSlugMap,
|
|
294
|
+
collectionMetrics,
|
|
295
|
+
);
|
|
146
296
|
|
|
147
297
|
// 2. Group replies by threadId
|
|
148
298
|
const repliesByThread = new Map<string, Post[]>();
|
|
@@ -156,90 +306,236 @@ export function createExportService(
|
|
|
156
306
|
list.sort((a, b) => a.createdAt - b.createdAt);
|
|
157
307
|
}
|
|
158
308
|
|
|
159
|
-
// 3. Build
|
|
160
|
-
const
|
|
161
|
-
const files: Record<string, Uint8Array> = {};
|
|
309
|
+
// 3. Build file list
|
|
310
|
+
const exportFiles: ExportFile[] = [];
|
|
162
311
|
|
|
163
|
-
// Generate
|
|
312
|
+
// Generate thread bundles (root _index.md + per-reply index.md).
|
|
164
313
|
for (const root of roots) {
|
|
165
314
|
const slug = slugMap.get(root.id) ?? root.slug;
|
|
166
315
|
const threadReplies = repliesByThread.get(root.id) ?? [];
|
|
167
|
-
const postCollections = collectionsByPost.get(root.id) ?? [];
|
|
168
316
|
const rootAliases = [...(aliasMap.get(root.id) ?? [])];
|
|
169
|
-
const zolaAliases = [...rootAliases];
|
|
170
|
-
const rootMedia = rawMediaByPost.get(root.id) ?? [];
|
|
171
|
-
|
|
172
|
-
// Reply URLs must resolve back to the merged thread page in Zola, but
|
|
173
|
-
// they are not root aliases when round-tripping into Jant.
|
|
174
|
-
for (const reply of threadReplies) {
|
|
175
|
-
const replySlug = slugMap.get(reply.id) ?? reply.slug;
|
|
176
|
-
zolaAliases.push(`/${replySlug}`);
|
|
177
|
-
}
|
|
178
317
|
|
|
179
|
-
const
|
|
318
|
+
const rootCollectionEntries = buildExportedCollectionEntriesForPost(
|
|
319
|
+
root.id,
|
|
320
|
+
collectionEntriesByPost,
|
|
321
|
+
collectionSlugMap,
|
|
322
|
+
collectionTitleMap,
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
const bundleFiles = await buildThreadBundle(
|
|
180
326
|
root,
|
|
181
327
|
threadReplies,
|
|
182
|
-
|
|
183
|
-
|
|
328
|
+
slug,
|
|
329
|
+
rootAliases,
|
|
330
|
+
rootCollectionEntries,
|
|
184
331
|
slugMap,
|
|
332
|
+
collectionEntriesByPost,
|
|
185
333
|
collectionSlugMap,
|
|
186
|
-
|
|
334
|
+
collectionTitleMap,
|
|
187
335
|
rawMediaByPost,
|
|
188
336
|
siteConfig,
|
|
189
|
-
|
|
337
|
+
deps.storage ?? null,
|
|
190
338
|
);
|
|
191
|
-
|
|
192
|
-
files[`content/${slug}/index.md`] = new TextEncoder().encode(markdown);
|
|
339
|
+
exportFiles.push(...bundleFiles);
|
|
193
340
|
}
|
|
194
341
|
|
|
342
|
+
// Collection landing pages (`content/{slug}/_index.md`).
|
|
195
343
|
for (const collection of allCollections) {
|
|
196
344
|
const slug = collectionSlugMap.get(collection.id) ?? collection.slug;
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
345
|
+
const entryCount = collectionMetrics.get(collection.id)?.postCount ?? 0;
|
|
346
|
+
exportFiles.push({
|
|
347
|
+
path: `content/${slug}/_index.md`,
|
|
348
|
+
content: await buildCollectionSection(collection, slug, entryCount),
|
|
349
|
+
});
|
|
201
350
|
}
|
|
202
351
|
|
|
203
|
-
//
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
);
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
);
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
);
|
|
352
|
+
// Section + home scaffolding.
|
|
353
|
+
exportFiles.push({
|
|
354
|
+
path: "hugo.toml",
|
|
355
|
+
content: buildHugoToml(siteConfig),
|
|
356
|
+
});
|
|
357
|
+
exportFiles.push({
|
|
358
|
+
path: "content/_index.md",
|
|
359
|
+
content: await buildHomeSection(siteConfig),
|
|
360
|
+
});
|
|
361
|
+
exportFiles.push({
|
|
362
|
+
path: "content/collections/_index.md",
|
|
363
|
+
content: await buildCollectionsSection(),
|
|
364
|
+
});
|
|
365
|
+
exportFiles.push({
|
|
366
|
+
path: "content/archive/_index.md",
|
|
367
|
+
content: await buildArchiveSection(),
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
const usedSlugs = new Set<string>();
|
|
371
|
+
for (const s of slugMap.values()) usedSlugs.add(s);
|
|
372
|
+
for (const s of collectionSlugMap.values()) usedSlugs.add(s);
|
|
373
|
+
if (!usedSlugs.has("featured")) {
|
|
374
|
+
exportFiles.push({
|
|
375
|
+
path: "content/featured/_index.md",
|
|
376
|
+
content: await buildFeaturedSection(),
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Single data file consumed by templates via `hugo.Data.jant`. The
|
|
381
|
+
// collection directory lives on the same object as `directory` so
|
|
382
|
+
// everything Jant owns round-trips in one place.
|
|
383
|
+
exportFiles.push({
|
|
384
|
+
path: "data/jant.toml",
|
|
385
|
+
content: buildJantDataToml(
|
|
386
|
+
siteConfig,
|
|
387
|
+
iconAssets,
|
|
388
|
+
exportedCollectionDirectoryItems,
|
|
389
|
+
),
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
// Theme scaffolding (real templates + styles land in Commit 5).
|
|
393
|
+
exportFiles.push({
|
|
394
|
+
path: "themes/jant/theme.toml",
|
|
395
|
+
content: THEME_TOML,
|
|
396
|
+
});
|
|
397
|
+
exportFiles.push({
|
|
398
|
+
path: "themes/jant/layouts/_default/baseof.html",
|
|
399
|
+
content: LAYOUT_BASEOF,
|
|
400
|
+
});
|
|
401
|
+
exportFiles.push({
|
|
402
|
+
path: "themes/jant/layouts/_default/single.html",
|
|
403
|
+
content: LAYOUT_SINGLE,
|
|
404
|
+
});
|
|
405
|
+
exportFiles.push({
|
|
406
|
+
path: "themes/jant/layouts/_default/list.html",
|
|
407
|
+
content: LAYOUT_LIST,
|
|
408
|
+
});
|
|
409
|
+
exportFiles.push({
|
|
410
|
+
path: "themes/jant/layouts/_default/alias.html",
|
|
411
|
+
content: LAYOUT_ALIAS,
|
|
412
|
+
});
|
|
413
|
+
exportFiles.push({
|
|
414
|
+
path: "themes/jant/layouts/index.html",
|
|
415
|
+
content: LAYOUT_INDEX,
|
|
416
|
+
});
|
|
417
|
+
exportFiles.push({
|
|
418
|
+
path: "themes/jant/layouts/post/list.html",
|
|
419
|
+
content: LAYOUT_POST_LIST,
|
|
420
|
+
});
|
|
421
|
+
exportFiles.push({
|
|
422
|
+
path: "themes/jant/layouts/featured/list.html",
|
|
423
|
+
content: LAYOUT_FEATURED_LIST,
|
|
424
|
+
});
|
|
425
|
+
exportFiles.push({
|
|
426
|
+
path: "themes/jant/layouts/archive/list.html",
|
|
427
|
+
content: LAYOUT_ARCHIVE_LIST,
|
|
428
|
+
});
|
|
429
|
+
exportFiles.push({
|
|
430
|
+
path: "themes/jant/layouts/collections/list.html",
|
|
431
|
+
content: LAYOUT_COLLECTIONS_LIST,
|
|
432
|
+
});
|
|
433
|
+
exportFiles.push({
|
|
434
|
+
path: "themes/jant/layouts/collection/single.html",
|
|
435
|
+
content: LAYOUT_COLLECTION_SINGLE,
|
|
436
|
+
});
|
|
437
|
+
exportFiles.push({
|
|
438
|
+
path: "themes/jant/layouts/partials/head.html",
|
|
439
|
+
content: PARTIAL_HEAD,
|
|
440
|
+
});
|
|
441
|
+
exportFiles.push({
|
|
442
|
+
path: "themes/jant/layouts/partials/header.html",
|
|
443
|
+
content: PARTIAL_HEADER,
|
|
444
|
+
});
|
|
445
|
+
exportFiles.push({
|
|
446
|
+
path: "themes/jant/layouts/partials/footer.html",
|
|
447
|
+
content: PARTIAL_FOOTER,
|
|
448
|
+
});
|
|
449
|
+
exportFiles.push({
|
|
450
|
+
path: "themes/jant/layouts/partials/pagination.html",
|
|
451
|
+
content: PARTIAL_PAGINATION,
|
|
452
|
+
});
|
|
453
|
+
exportFiles.push({
|
|
454
|
+
path: "themes/jant/layouts/partials/post-card.html",
|
|
455
|
+
content: PARTIAL_POST_CARD,
|
|
456
|
+
});
|
|
457
|
+
exportFiles.push({
|
|
458
|
+
path: "themes/jant/layouts/partials/media-gallery.html",
|
|
459
|
+
content: PARTIAL_MEDIA_GALLERY,
|
|
460
|
+
});
|
|
461
|
+
exportFiles.push({
|
|
462
|
+
path: "themes/jant/layouts/partials/reply.html",
|
|
463
|
+
content: PARTIAL_REPLY,
|
|
464
|
+
});
|
|
465
|
+
exportFiles.push({
|
|
466
|
+
path: "themes/jant/layouts/partials/thread-preview.html",
|
|
467
|
+
content: PARTIAL_THREAD_PREVIEW,
|
|
468
|
+
});
|
|
469
|
+
exportFiles.push({
|
|
470
|
+
path: "themes/jant/layouts/_default/rss.xml",
|
|
471
|
+
content: LAYOUT_RSS,
|
|
472
|
+
});
|
|
473
|
+
exportFiles.push({
|
|
474
|
+
path: "themes/jant/layouts/partials/feed-post-content.xml",
|
|
475
|
+
content: PARTIAL_FEED_POST_CONTENT,
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
// Static assets. Load order in the template's <head> is
|
|
479
|
+
// tokens → main → theme → custom (wired up by the Commit 5 partial).
|
|
480
|
+
exportFiles.push({
|
|
481
|
+
path: "themes/jant/static/tokens.css",
|
|
482
|
+
content: TOKENS_CSS,
|
|
483
|
+
});
|
|
484
|
+
exportFiles.push({
|
|
485
|
+
path: "themes/jant/static/main.css",
|
|
486
|
+
content: THEME_STYLE_MAIN_CSS,
|
|
487
|
+
});
|
|
488
|
+
exportFiles.push({
|
|
489
|
+
path: "themes/jant/static/theme.css",
|
|
490
|
+
content: siteConfig.themeCss ?? "",
|
|
491
|
+
});
|
|
492
|
+
exportFiles.push({
|
|
493
|
+
path: "themes/jant/static/custom.css",
|
|
494
|
+
content: siteConfig.customCss ?? "",
|
|
495
|
+
});
|
|
496
|
+
// Client-side interactions: media lightbox, feed video autoplay,
|
|
497
|
+
// audio waveform, gallery scroll hints. Reserved namespace keeps these
|
|
498
|
+
// from colliding with user-authored static files.
|
|
499
|
+
exportFiles.push({
|
|
500
|
+
path: "themes/jant/static/_jant/client-site.js",
|
|
501
|
+
content: CLIENT_SITE_JS,
|
|
502
|
+
});
|
|
503
|
+
exportFiles.push({
|
|
504
|
+
path: "themes/jant/static/_jant/client-site.css",
|
|
505
|
+
content: CLIENT_SITE_CSS,
|
|
506
|
+
});
|
|
507
|
+
exportFiles.push({
|
|
508
|
+
path: "themes/jant/static/favicon.ico",
|
|
509
|
+
content: iconAssets.faviconBytes,
|
|
510
|
+
});
|
|
511
|
+
exportFiles.push({
|
|
512
|
+
path: "themes/jant/static/apple-touch-icon.png",
|
|
513
|
+
content: iconAssets.appleTouchBytes,
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
exportFiles.push({
|
|
517
|
+
path: "README.md",
|
|
518
|
+
content: buildReadme(siteConfig.siteName),
|
|
519
|
+
});
|
|
520
|
+
exportFiles.push({
|
|
521
|
+
path: ".gitignore",
|
|
522
|
+
content: buildGitignore(),
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
return exportFiles;
|
|
526
|
+
},
|
|
242
527
|
|
|
528
|
+
async generateHugoSite() {
|
|
529
|
+
const exportFiles = await this.generateHugoFiles();
|
|
530
|
+
const { zipSync } = await import("fflate");
|
|
531
|
+
const encoder = new TextEncoder();
|
|
532
|
+
const files: Record<string, Uint8Array> = {};
|
|
533
|
+
for (const file of exportFiles) {
|
|
534
|
+
files[file.path] =
|
|
535
|
+
typeof file.content === "string"
|
|
536
|
+
? encoder.encode(file.content)
|
|
537
|
+
: file.content;
|
|
538
|
+
}
|
|
243
539
|
return zipSync(files);
|
|
244
540
|
},
|
|
245
541
|
};
|
|
@@ -272,25 +568,38 @@ async function buildSiteIconAssets(
|
|
|
272
568
|
return {
|
|
273
569
|
faviconBytes,
|
|
274
570
|
faviconMode,
|
|
275
|
-
|
|
276
|
-
appleTouchMode: "default",
|
|
571
|
+
...buildDefaultAppleTouchAsset(),
|
|
277
572
|
};
|
|
278
573
|
}
|
|
279
574
|
|
|
280
575
|
if (!storage) {
|
|
281
|
-
|
|
282
|
-
|
|
576
|
+
return {
|
|
577
|
+
faviconBytes,
|
|
578
|
+
faviconMode,
|
|
579
|
+
...buildDefaultAppleTouchAsset(),
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
let appleTouchBytes: Uint8Array | null;
|
|
584
|
+
try {
|
|
585
|
+
appleTouchBytes = await readStorageObjectBytes(
|
|
586
|
+
storage,
|
|
587
|
+
config.appleTouchIconStorageKey,
|
|
283
588
|
);
|
|
589
|
+
} catch {
|
|
590
|
+
return {
|
|
591
|
+
faviconBytes,
|
|
592
|
+
faviconMode,
|
|
593
|
+
...buildDefaultAppleTouchAsset(),
|
|
594
|
+
};
|
|
284
595
|
}
|
|
285
596
|
|
|
286
|
-
const appleTouchBytes = await readStorageObjectBytes(
|
|
287
|
-
storage,
|
|
288
|
-
config.appleTouchIconStorageKey,
|
|
289
|
-
);
|
|
290
597
|
if (!appleTouchBytes) {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
598
|
+
return {
|
|
599
|
+
faviconBytes,
|
|
600
|
+
faviconMode,
|
|
601
|
+
...buildDefaultAppleTouchAsset(),
|
|
602
|
+
};
|
|
294
603
|
}
|
|
295
604
|
|
|
296
605
|
return {
|
|
@@ -302,563 +611,1039 @@ async function buildSiteIconAssets(
|
|
|
302
611
|
}
|
|
303
612
|
|
|
304
613
|
// ---------------------------------------------------------------------------
|
|
305
|
-
//
|
|
614
|
+
// Thread bundle generation
|
|
306
615
|
// ---------------------------------------------------------------------------
|
|
307
616
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
617
|
+
function buildExportedCollectionEntriesForPost(
|
|
618
|
+
postId: string,
|
|
619
|
+
collectionEntriesByPost: Map<
|
|
620
|
+
string,
|
|
621
|
+
{
|
|
622
|
+
collectionId: string;
|
|
623
|
+
createdAt: number;
|
|
624
|
+
position: number;
|
|
625
|
+
pinnedAt: number | null;
|
|
626
|
+
}[]
|
|
627
|
+
>,
|
|
628
|
+
collectionSlugMap: Map<string, string>,
|
|
629
|
+
collectionTitleMap: Map<string, string>,
|
|
630
|
+
): ExportedCollectionEntry[] {
|
|
631
|
+
const entries = collectionEntriesByPost.get(postId) ?? [];
|
|
632
|
+
const resolved: ExportedCollectionEntry[] = [];
|
|
633
|
+
for (const entry of entries) {
|
|
634
|
+
const slug = collectionSlugMap.get(entry.collectionId);
|
|
635
|
+
if (!slug) continue;
|
|
636
|
+
const title = collectionTitleMap.get(entry.collectionId);
|
|
637
|
+
resolved.push({
|
|
638
|
+
slug,
|
|
639
|
+
title,
|
|
640
|
+
collectedAt: entry.createdAt,
|
|
641
|
+
position: entry.position,
|
|
642
|
+
pinnedAt: entry.pinnedAt,
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
return resolved;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
function collectionEntriesToRefs(
|
|
649
|
+
entries: readonly ExportedCollectionEntry[],
|
|
650
|
+
): HugoCollectionRef[] {
|
|
651
|
+
return entries.map((entry) => ({
|
|
652
|
+
slug: entry.slug,
|
|
653
|
+
title: entry.title,
|
|
654
|
+
collected_at: toISOString(entry.collectedAt),
|
|
655
|
+
position: entry.position,
|
|
656
|
+
pinned_at: entry.pinnedAt !== null ? toISOString(entry.pinnedAt) : null,
|
|
657
|
+
}));
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
interface MediaEmission {
|
|
661
|
+
/** Flat `media:` front-matter entry. */
|
|
662
|
+
entry: JantMedia;
|
|
663
|
+
/**
|
|
664
|
+
* Site-relative path under `static/` where the primary bytes should
|
|
665
|
+
* land, or null when the media links to a remote public URL and no
|
|
666
|
+
* bytes need to be emitted.
|
|
667
|
+
*/
|
|
668
|
+
inlinePath: string | null;
|
|
669
|
+
/**
|
|
670
|
+
* Site-relative path under `static/` where the poster bytes should
|
|
671
|
+
* land, or null when there's no poster or the poster is linked via
|
|
672
|
+
* a public URL.
|
|
673
|
+
*/
|
|
674
|
+
inlinePosterPath: string | null;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
/**
|
|
678
|
+
* Build a flat `media:` entry for a Media record plus a decision about
|
|
679
|
+
* whether the primary bytes (and poster) should be bundled into the
|
|
680
|
+
* export's `static/media/` directory.
|
|
681
|
+
*
|
|
682
|
+
* When the media's provider has a reachable public URL (R2/S3/local
|
|
683
|
+
* proxy configured with a `*_public_url`), `src` points at that absolute
|
|
684
|
+
* URL and no bytes are emitted — the exported site stays small and the
|
|
685
|
+
* media keeps being served from wherever it already lives. Without a
|
|
686
|
+
* public URL the bytes are written to `static/media/{id}.ext` and `src`
|
|
687
|
+
* is the site-relative path.
|
|
688
|
+
*/
|
|
689
|
+
function buildMediaEmission(
|
|
690
|
+
media: Media,
|
|
691
|
+
siteConfig: SiteConfig,
|
|
692
|
+
): MediaEmission {
|
|
693
|
+
const publicUrl = getPublicUrlForProvider(
|
|
694
|
+
media.provider,
|
|
695
|
+
siteConfig.r2PublicUrl,
|
|
696
|
+
siteConfig.s3PublicUrl,
|
|
697
|
+
siteConfig.localPublicUrl,
|
|
698
|
+
);
|
|
699
|
+
const hasPublic = Boolean(publicUrl);
|
|
700
|
+
|
|
701
|
+
const ext = extOfFilename(media.filename);
|
|
702
|
+
const localName = `${media.id}${ext}`;
|
|
703
|
+
const localPath = `/media/${localName}`;
|
|
704
|
+
const src = hasPublic ? getMediaUrl(media.storageKey, publicUrl) : localPath;
|
|
705
|
+
|
|
706
|
+
const entry: JantMedia = {
|
|
707
|
+
id: media.id,
|
|
708
|
+
kind: media.mediaKind,
|
|
709
|
+
src,
|
|
710
|
+
position: parsePositionForSort(media.position),
|
|
711
|
+
};
|
|
712
|
+
if (media.alt !== null && media.alt !== "") entry.alt = media.alt;
|
|
713
|
+
if (media.width !== null) entry.width = media.width;
|
|
714
|
+
if (media.height !== null) entry.height = media.height;
|
|
715
|
+
if (media.blurhash !== null && media.blurhash !== "")
|
|
716
|
+
entry.blurhash = media.blurhash;
|
|
717
|
+
if (media.originalName) entry.original_name = media.originalName;
|
|
718
|
+
if (media.mimeType) entry.mime_type = media.mimeType;
|
|
719
|
+
if (typeof media.size === "number") entry.size = media.size;
|
|
720
|
+
if (media.waveform) entry.waveform = media.waveform;
|
|
721
|
+
if (media.summary) entry.summary = media.summary;
|
|
722
|
+
if (typeof media.chars === "number") entry.chars = media.chars;
|
|
723
|
+
if (media.durationSeconds !== null && media.durationSeconds !== undefined) {
|
|
724
|
+
entry.duration_seconds = media.durationSeconds;
|
|
725
|
+
}
|
|
726
|
+
entry.provider = media.provider;
|
|
727
|
+
entry.storage_key = media.storageKey;
|
|
728
|
+
|
|
729
|
+
let inlinePosterPath: string | null = null;
|
|
730
|
+
if (media.posterKey) {
|
|
731
|
+
const posterExt = extOfStorageKey(media.posterKey);
|
|
732
|
+
const posterLocalName = `${media.id}-poster.${posterExt}`;
|
|
733
|
+
entry.poster = hasPublic
|
|
734
|
+
? getMediaUrl(media.posterKey, publicUrl)
|
|
735
|
+
: `/media/${posterLocalName}`;
|
|
736
|
+
entry.poster_key = media.posterKey;
|
|
737
|
+
if (!hasPublic) {
|
|
738
|
+
inlinePosterPath = `static/media/${posterLocalName}`;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
return {
|
|
743
|
+
entry,
|
|
744
|
+
inlinePath: hasPublic ? null : `static/media/${localName}`,
|
|
745
|
+
inlinePosterPath,
|
|
746
|
+
};
|
|
315
747
|
}
|
|
316
748
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
//
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
value === "" ||
|
|
325
|
-
value === "true" ||
|
|
326
|
-
value === "false" ||
|
|
327
|
-
value === "null"
|
|
328
|
-
) {
|
|
329
|
-
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n")}"`;
|
|
749
|
+
function parsePositionForSort(position: string): number {
|
|
750
|
+
// Fractional indexing keys sort lexicographically, but downstream
|
|
751
|
+
// consumers (and the UI) expect a numeric fallback. Keep a stable
|
|
752
|
+
// ordering by hashing the string into an integer.
|
|
753
|
+
let hash = 0;
|
|
754
|
+
for (let i = 0; i < position.length; i++) {
|
|
755
|
+
hash = (hash * 31 + position.charCodeAt(i)) | 0;
|
|
330
756
|
}
|
|
331
|
-
return
|
|
757
|
+
return hash;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
function extOfFilename(filename: string): string {
|
|
761
|
+
const dot = filename.lastIndexOf(".");
|
|
762
|
+
return dot >= 0 ? filename.slice(dot) : "";
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
function extOfStorageKey(key: string): string {
|
|
766
|
+
const dot = key.lastIndexOf(".");
|
|
767
|
+
return dot >= 0 ? key.slice(dot + 1) : "webp";
|
|
332
768
|
}
|
|
333
769
|
|
|
334
|
-
|
|
770
|
+
/**
|
|
771
|
+
* Build a complete set of ExportFile entries for a single thread bundle:
|
|
772
|
+
* the root `_index.md`, one `index.md` per reply, and resource blobs for
|
|
773
|
+
* attached media when the storage driver can fetch them.
|
|
774
|
+
*/
|
|
775
|
+
async function buildThreadBundle(
|
|
335
776
|
root: Post,
|
|
336
777
|
threadReplies: Post[],
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
zolaAliases: string[];
|
|
341
|
-
},
|
|
778
|
+
rootSlug: string,
|
|
779
|
+
rootAliases: string[],
|
|
780
|
+
rootCollectionEntries: ExportedCollectionEntry[],
|
|
342
781
|
slugMap: Map<string, string>,
|
|
782
|
+
collectionEntriesByPost: Map<
|
|
783
|
+
string,
|
|
784
|
+
{
|
|
785
|
+
collectionId: string;
|
|
786
|
+
createdAt: number;
|
|
787
|
+
position: number;
|
|
788
|
+
pinnedAt: number | null;
|
|
789
|
+
}[]
|
|
790
|
+
>,
|
|
343
791
|
collectionSlugMap: Map<string, string>,
|
|
344
|
-
|
|
792
|
+
collectionTitleMap: Map<string, string>,
|
|
345
793
|
mediaByPost: Map<string, Media[]>,
|
|
346
794
|
siteConfig: SiteConfig,
|
|
347
|
-
|
|
348
|
-
):
|
|
349
|
-
const
|
|
350
|
-
|
|
351
|
-
//
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
parts.push(`date: ${toISOString(date)}`);
|
|
359
|
-
}
|
|
360
|
-
if (root.updatedAt && root.updatedAt !== root.publishedAt) {
|
|
361
|
-
parts.push(`updated: ${toISOString(root.updatedAt)}`);
|
|
362
|
-
}
|
|
363
|
-
if (root.status === "draft" || root.visibility === "private") {
|
|
364
|
-
parts.push("draft: true");
|
|
795
|
+
storage: StorageDriver | null,
|
|
796
|
+
): Promise<ExportFile[]> {
|
|
797
|
+
const files: ExportFile[] = [];
|
|
798
|
+
|
|
799
|
+
// Root aliases = historical root slugs + every reply slug (so
|
|
800
|
+
// /{reply-slug}/ gets a Hugo alias page that redirects/anchors to
|
|
801
|
+
// the thread root).
|
|
802
|
+
const aliases = [...rootAliases];
|
|
803
|
+
for (const reply of threadReplies) {
|
|
804
|
+
const replySlug = slugMap.get(reply.id) ?? reply.slug;
|
|
805
|
+
aliases.push(`/${replySlug}/`);
|
|
365
806
|
}
|
|
366
807
|
|
|
367
|
-
|
|
368
|
-
|
|
808
|
+
// Root front matter.
|
|
809
|
+
const rootMedia = mediaByPost.get(root.id) ?? [];
|
|
810
|
+
const rootEmissions = rootMedia.map((m) => buildMediaEmission(m, siteConfig));
|
|
811
|
+
const rootMediaList = rootEmissions.map((e) => e.entry);
|
|
812
|
+
const rootFrontMatter: HugoFrontMatter = {
|
|
813
|
+
id: root.id,
|
|
814
|
+
title: root.format !== "quote" ? (root.title ?? undefined) : undefined,
|
|
815
|
+
date:
|
|
816
|
+
root.publishedAt !== null
|
|
817
|
+
? toISOString(root.publishedAt)
|
|
818
|
+
: toISOString(root.createdAt),
|
|
819
|
+
updated:
|
|
820
|
+
root.updatedAt && root.updatedAt !== root.publishedAt
|
|
821
|
+
? toISOString(root.updatedAt)
|
|
822
|
+
: undefined,
|
|
823
|
+
// `updated` only reflects edits to the root post itself; when a reply
|
|
824
|
+
// lands, it does NOT bump. For RSS we want the thread's last activity
|
|
825
|
+
// (max of all published reply timestamps) so readers re-surface a
|
|
826
|
+
// thread when a new reply appears. Kept alongside `updated` so the
|
|
827
|
+
// importer round-trips cleanly.
|
|
828
|
+
last_activity_at:
|
|
829
|
+
root.lastActivityAt !== null && root.lastActivityAt !== root.publishedAt
|
|
830
|
+
? toISOString(root.lastActivityAt)
|
|
831
|
+
: undefined,
|
|
832
|
+
slug: rootSlug,
|
|
833
|
+
type: "post",
|
|
834
|
+
draft:
|
|
835
|
+
root.status === "draft" || root.visibility === "private"
|
|
836
|
+
? true
|
|
837
|
+
: undefined,
|
|
838
|
+
aliases: aliases.length > 0 ? aliases : undefined,
|
|
839
|
+
format: root.format,
|
|
840
|
+
status: root.status,
|
|
841
|
+
visibility: root.visibility,
|
|
842
|
+
summary_text: getArchiveSummaryText(root) ?? undefined,
|
|
843
|
+
link_url: root.format === "link" && root.url ? root.url : undefined,
|
|
844
|
+
source_name: root.format === "quote" && root.title ? root.title : undefined,
|
|
845
|
+
source_url: root.format === "quote" && root.url ? root.url : undefined,
|
|
846
|
+
quote_text: root.quoteText ?? undefined,
|
|
847
|
+
rating: root.rating ?? undefined,
|
|
848
|
+
featured_at:
|
|
849
|
+
root.featuredAt !== null ? toISOString(root.featuredAt) : undefined,
|
|
850
|
+
pinned_at: root.pinnedAt !== null ? toISOString(root.pinnedAt) : undefined,
|
|
851
|
+
root_aliases: rootAliases.length > 0 ? rootAliases : undefined,
|
|
852
|
+
collections:
|
|
853
|
+
rootCollectionEntries.length > 0
|
|
854
|
+
? collectionEntriesToRefs(rootCollectionEntries)
|
|
855
|
+
: undefined,
|
|
856
|
+
media: rootMediaList.length > 0 ? rootMediaList : undefined,
|
|
857
|
+
};
|
|
369
858
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
859
|
+
const rootBody = root.body ? tiptapJsonToMarkdown(root.body) : "";
|
|
860
|
+
files.push({
|
|
861
|
+
path: `content/${rootSlug}/_index.md`,
|
|
862
|
+
content: `${await formatFrontMatter(rootFrontMatter)}\n${rootBody}${rootBody.endsWith("\n") ? "" : "\n"}`,
|
|
863
|
+
});
|
|
864
|
+
|
|
865
|
+
// Emit media bytes under static/media/ for any media without a
|
|
866
|
+
// reachable public URL. Media whose provider has a configured public
|
|
867
|
+
// URL keeps `src` pointing at that absolute URL and skips inlining —
|
|
868
|
+
// this avoids re-downloading every attachment when the site is going
|
|
869
|
+
// to keep serving media from the existing CDN/proxy anyway.
|
|
870
|
+
for (const { emission, media } of rootEmissions.map((e, i) => ({
|
|
871
|
+
emission: e,
|
|
872
|
+
media: rootMedia[i] as Media,
|
|
873
|
+
}))) {
|
|
874
|
+
if (emission.inlinePath) {
|
|
875
|
+
const file = await readMediaResourceFile(
|
|
876
|
+
storage,
|
|
877
|
+
media.storageKey,
|
|
878
|
+
emission.inlinePath,
|
|
879
|
+
);
|
|
880
|
+
if (file) files.push(file);
|
|
374
881
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
const colSlug = collectionSlugMap.get(c.id) ?? c.slug;
|
|
383
|
-
parts.push(` - ${yamlString(colSlug)}`);
|
|
882
|
+
if (emission.inlinePosterPath && media.posterKey) {
|
|
883
|
+
const posterFile = await readMediaResourceFile(
|
|
884
|
+
storage,
|
|
885
|
+
media.posterKey,
|
|
886
|
+
emission.inlinePosterPath,
|
|
887
|
+
);
|
|
888
|
+
if (posterFile) files.push(posterFile);
|
|
384
889
|
}
|
|
385
890
|
}
|
|
386
891
|
|
|
387
|
-
//
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
}
|
|
402
|
-
if (root.format === "quote" && root.url) {
|
|
403
|
-
parts.push(` source_url: ${yamlString(root.url)}`);
|
|
404
|
-
}
|
|
405
|
-
if (root.quoteText) {
|
|
406
|
-
parts.push(` quote_text: ${yamlString(root.quoteText)}`);
|
|
407
|
-
}
|
|
408
|
-
if (root.rating !== null) {
|
|
409
|
-
parts.push(` rating: ${root.rating}`);
|
|
410
|
-
}
|
|
411
|
-
if (root.pinnedAt !== null) {
|
|
412
|
-
parts.push(" pinned: true");
|
|
413
|
-
}
|
|
414
|
-
if (root.featuredAt !== null) {
|
|
415
|
-
parts.push(" featured: true");
|
|
416
|
-
}
|
|
417
|
-
if (aliasData.rootAliases.length > 0) {
|
|
418
|
-
parts.push(" jant:");
|
|
419
|
-
parts.push(" root_aliases:");
|
|
420
|
-
for (const alias of aliasData.rootAliases) {
|
|
421
|
-
parts.push(` - ${yamlString(alias)}`);
|
|
422
|
-
}
|
|
423
|
-
}
|
|
892
|
+
// Replies as nested leaf bundles.
|
|
893
|
+
for (const reply of threadReplies) {
|
|
894
|
+
const replySlug = slugMap.get(reply.id) ?? reply.slug;
|
|
895
|
+
const replyMedia = mediaByPost.get(reply.id) ?? [];
|
|
896
|
+
const replyEmissions = replyMedia.map((m) =>
|
|
897
|
+
buildMediaEmission(m, siteConfig),
|
|
898
|
+
);
|
|
899
|
+
const replyMediaList = replyEmissions.map((e) => e.entry);
|
|
900
|
+
const replyCollectionEntries = buildExportedCollectionEntriesForPost(
|
|
901
|
+
reply.id,
|
|
902
|
+
collectionEntriesByPost,
|
|
903
|
+
collectionSlugMap,
|
|
904
|
+
collectionTitleMap,
|
|
905
|
+
);
|
|
424
906
|
|
|
425
|
-
|
|
426
|
-
|
|
907
|
+
const replyFrontMatter: HugoFrontMatter = {
|
|
908
|
+
id: reply.id,
|
|
909
|
+
title: reply.format !== "quote" ? (reply.title ?? undefined) : undefined,
|
|
910
|
+
date:
|
|
911
|
+
reply.publishedAt !== null
|
|
912
|
+
? toISOString(reply.publishedAt)
|
|
913
|
+
: toISOString(reply.createdAt),
|
|
914
|
+
updated:
|
|
915
|
+
reply.updatedAt && reply.updatedAt !== reply.publishedAt
|
|
916
|
+
? toISOString(reply.updatedAt)
|
|
917
|
+
: undefined,
|
|
918
|
+
slug: replySlug,
|
|
919
|
+
type: "post",
|
|
920
|
+
draft:
|
|
921
|
+
reply.status === "draft" || reply.visibility === "private"
|
|
922
|
+
? true
|
|
923
|
+
: undefined,
|
|
924
|
+
build: { render: "never", list: "local" },
|
|
925
|
+
format: reply.format,
|
|
926
|
+
status: reply.status,
|
|
927
|
+
visibility: reply.visibility,
|
|
928
|
+
summary_text: getArchiveSummaryText(reply) ?? undefined,
|
|
929
|
+
link_url: reply.format === "link" && reply.url ? reply.url : undefined,
|
|
930
|
+
source_name:
|
|
931
|
+
reply.format === "quote" && reply.title ? reply.title : undefined,
|
|
932
|
+
source_url: reply.format === "quote" && reply.url ? reply.url : undefined,
|
|
933
|
+
quote_text: reply.quoteText ?? undefined,
|
|
934
|
+
rating: reply.rating ?? undefined,
|
|
935
|
+
featured_at:
|
|
936
|
+
reply.featuredAt !== null ? toISOString(reply.featuredAt) : undefined,
|
|
937
|
+
pinned_at:
|
|
938
|
+
reply.pinnedAt !== null ? toISOString(reply.pinnedAt) : undefined,
|
|
939
|
+
collections:
|
|
940
|
+
replyCollectionEntries.length > 0
|
|
941
|
+
? collectionEntriesToRefs(replyCollectionEntries)
|
|
942
|
+
: undefined,
|
|
943
|
+
media: replyMediaList.length > 0 ? replyMediaList : undefined,
|
|
944
|
+
};
|
|
427
945
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
946
|
+
const replyBody = reply.body ? tiptapJsonToMarkdown(reply.body) : "";
|
|
947
|
+
files.push({
|
|
948
|
+
path: `content/${rootSlug}/${replySlug}/index.md`,
|
|
949
|
+
content: `${await formatFrontMatter(replyFrontMatter)}\n${replyBody}${replyBody.endsWith("\n") ? "" : "\n"}`,
|
|
950
|
+
});
|
|
951
|
+
|
|
952
|
+
for (const { emission, media } of replyEmissions.map((e, i) => ({
|
|
953
|
+
emission: e,
|
|
954
|
+
media: replyMedia[i] as Media,
|
|
955
|
+
}))) {
|
|
956
|
+
if (emission.inlinePath) {
|
|
957
|
+
const file = await readMediaResourceFile(
|
|
958
|
+
storage,
|
|
959
|
+
media.storageKey,
|
|
960
|
+
emission.inlinePath,
|
|
961
|
+
);
|
|
962
|
+
if (file) files.push(file);
|
|
963
|
+
}
|
|
964
|
+
if (emission.inlinePosterPath && media.posterKey) {
|
|
965
|
+
const posterFile = await readMediaResourceFile(
|
|
966
|
+
storage,
|
|
967
|
+
media.posterKey,
|
|
968
|
+
emission.inlinePosterPath,
|
|
969
|
+
);
|
|
970
|
+
if (posterFile) files.push(posterFile);
|
|
971
|
+
}
|
|
972
|
+
}
|
|
435
973
|
}
|
|
436
974
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
parts.push("");
|
|
975
|
+
return files;
|
|
976
|
+
}
|
|
440
977
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
978
|
+
/**
|
|
979
|
+
* Read a media record's bytes from storage and return an ExportFile so
|
|
980
|
+
* they can be bundled next to the post as a Hugo page resource. Returns
|
|
981
|
+
* null when storage is unavailable or the object cannot be read, in
|
|
982
|
+
* which case the front matter entry still points at the resource name
|
|
983
|
+
* and the CLI's pull-media step (or a later sync) can fill it in.
|
|
984
|
+
*/
|
|
985
|
+
async function readMediaResourceFile(
|
|
986
|
+
storage: StorageDriver | null,
|
|
987
|
+
storageKey: string,
|
|
988
|
+
bundlePath: string,
|
|
989
|
+
): Promise<ExportFile | null> {
|
|
990
|
+
if (!storage) return null;
|
|
991
|
+
try {
|
|
992
|
+
const bytes = await readStorageObjectBytes(storage, storageKey);
|
|
993
|
+
if (!bytes) return null;
|
|
994
|
+
return { path: bundlePath, content: bytes };
|
|
995
|
+
} catch {
|
|
996
|
+
return null;
|
|
997
|
+
}
|
|
998
|
+
}
|
|
445
999
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
if (reply.format === "quote" && reply.quoteText) {
|
|
450
|
-
marker += ` quote_text="${encodeURIComponent(reply.quoteText)}"`;
|
|
451
|
-
}
|
|
452
|
-
if (reply.format === "quote" && reply.title) {
|
|
453
|
-
marker += ` source_name="${esc(reply.title)}"`;
|
|
454
|
-
}
|
|
455
|
-
if (reply.format === "quote" && reply.url) {
|
|
456
|
-
marker += ` source_url="${esc(reply.url)}"`;
|
|
457
|
-
}
|
|
458
|
-
if (reply.rating !== null) {
|
|
459
|
-
marker += ` rating="${reply.rating}"`;
|
|
460
|
-
}
|
|
461
|
-
if (reply.title && reply.format !== "quote") {
|
|
462
|
-
marker += ` title="${esc(reply.title)}"`;
|
|
463
|
-
}
|
|
464
|
-
marker += " -->";
|
|
1000
|
+
// ---------------------------------------------------------------------------
|
|
1001
|
+
// Section + landing pages
|
|
1002
|
+
// ---------------------------------------------------------------------------
|
|
465
1003
|
|
|
466
|
-
|
|
467
|
-
|
|
1004
|
+
async function buildHomeSection(siteConfig: SiteConfig): Promise<string> {
|
|
1005
|
+
const frontMatter: HugoFrontMatter = {
|
|
1006
|
+
title: siteConfig.siteName,
|
|
1007
|
+
type: "home",
|
|
1008
|
+
};
|
|
1009
|
+
return `${await formatFrontMatter(frontMatter)}\n`;
|
|
1010
|
+
}
|
|
468
1011
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
].filter(Boolean);
|
|
477
|
-
if (replyBlocks.length > 0) {
|
|
478
|
-
parts.push(replyBlocks.join("\n\n"));
|
|
479
|
-
}
|
|
480
|
-
}
|
|
1012
|
+
async function buildCollectionsSection(): Promise<string> {
|
|
1013
|
+
const frontMatter: HugoFrontMatter = {
|
|
1014
|
+
title: "Collections",
|
|
1015
|
+
type: "collections",
|
|
1016
|
+
};
|
|
1017
|
+
return `${await formatFrontMatter(frontMatter)}\n`;
|
|
1018
|
+
}
|
|
481
1019
|
|
|
482
|
-
|
|
1020
|
+
async function buildArchiveSection(): Promise<string> {
|
|
1021
|
+
const frontMatter: HugoFrontMatter = {
|
|
1022
|
+
title: "Archive",
|
|
1023
|
+
type: "archive",
|
|
1024
|
+
// Opt into Atom output at /archive/index.xml.
|
|
1025
|
+
outputs: ["html", "rss"],
|
|
1026
|
+
};
|
|
1027
|
+
return `${await formatFrontMatter(frontMatter)}\n`;
|
|
483
1028
|
}
|
|
484
1029
|
|
|
485
|
-
function
|
|
486
|
-
const
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
1030
|
+
async function buildFeaturedSection(): Promise<string> {
|
|
1031
|
+
const frontMatter: HugoFrontMatter = {
|
|
1032
|
+
title: "Featured",
|
|
1033
|
+
type: "featured",
|
|
1034
|
+
// Opt into Atom output at /featured/index.xml.
|
|
1035
|
+
outputs: ["html", "rss"],
|
|
1036
|
+
};
|
|
1037
|
+
return `${await formatFrontMatter(frontMatter)}\n`;
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
async function buildCollectionSection(
|
|
1041
|
+
collection: Collection,
|
|
1042
|
+
slug: string,
|
|
1043
|
+
entryCount: number,
|
|
1044
|
+
): Promise<string> {
|
|
1045
|
+
const frontMatter: HugoFrontMatter = {
|
|
1046
|
+
title: collection.title,
|
|
1047
|
+
slug,
|
|
1048
|
+
type: "collection",
|
|
1049
|
+
summary_text: collection.description ?? undefined,
|
|
1050
|
+
sort_order: collection.sortOrder,
|
|
1051
|
+
entry_count: entryCount,
|
|
1052
|
+
// Opt into Atom output at /{slug}/index.xml.
|
|
1053
|
+
outputs: ["html", "rss"],
|
|
1054
|
+
};
|
|
1055
|
+
return `${await formatFrontMatter(frontMatter)}\n`;
|
|
498
1056
|
}
|
|
499
1057
|
|
|
1058
|
+
// ---------------------------------------------------------------------------
|
|
1059
|
+
// Summary extraction (kept from the previous exporter)
|
|
1060
|
+
// ---------------------------------------------------------------------------
|
|
1061
|
+
|
|
500
1062
|
function normalizeArchiveText(text: string | null | undefined): string {
|
|
501
1063
|
return (text ?? "").replace(/\s+/g, " ").trim();
|
|
502
1064
|
}
|
|
503
1065
|
|
|
504
1066
|
function getArchiveSummaryText(post: Post): string | null {
|
|
1067
|
+
// `summary_text` is a plain-text projection of the post's primary content,
|
|
1068
|
+
// used for `<meta name="description">`, `og:description`, and archive/card
|
|
1069
|
+
// fallbacks when the rendered body is empty. The candidate list differs
|
|
1070
|
+
// per format so the description reflects the right "primary content":
|
|
1071
|
+
//
|
|
1072
|
+
// - Quote: body (commentary) → quoteText. Quotes have no title, so we
|
|
1073
|
+
// fall back to the quote itself to guarantee a meaningful description.
|
|
1074
|
+
// - Link: body only. Links have a title + domain; the URL is already
|
|
1075
|
+
// serialized as `link_url` and rendered as a domain badge, so using
|
|
1076
|
+
// it as `summary_text` would duplicate that information.
|
|
1077
|
+
// - Note: body only. If the body is empty, there's nothing to describe.
|
|
1078
|
+
//
|
|
1079
|
+
// Note: we re-derive the body text from `post.body` (TipTap JSON) rather
|
|
1080
|
+
// than reusing `post.bodyText`, because `bodyText` is written with
|
|
1081
|
+
// `includeLinkHrefs: true` for FTS search indexing — that pollutes the
|
|
1082
|
+
// stored text with trailing link URLs. Here we need clean prose.
|
|
1083
|
+
const cleanBodyText = post.body ? extractBodyText(post.body) : null;
|
|
505
1084
|
const candidates =
|
|
506
1085
|
post.format === "quote"
|
|
507
|
-
? [post.summary,
|
|
508
|
-
: [post.summary,
|
|
1086
|
+
? [post.summary, cleanBodyText, post.quoteText]
|
|
1087
|
+
: [post.summary, cleanBodyText];
|
|
509
1088
|
|
|
510
1089
|
for (const candidate of candidates) {
|
|
511
1090
|
const normalized = normalizeArchiveText(candidate);
|
|
512
1091
|
if (normalized) return normalized;
|
|
513
1092
|
}
|
|
514
|
-
|
|
515
1093
|
return null;
|
|
516
1094
|
}
|
|
517
1095
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
textAttachmentContents: Map<string, TextAttachmentContent>,
|
|
522
|
-
): string {
|
|
523
|
-
if (mediaList.length === 0) return "";
|
|
1096
|
+
// ---------------------------------------------------------------------------
|
|
1097
|
+
// Collection metrics + directory items (kept from the previous exporter)
|
|
1098
|
+
// ---------------------------------------------------------------------------
|
|
524
1099
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
1100
|
+
function formatCollectionActivityLabel(
|
|
1101
|
+
timestamp: number | undefined,
|
|
1102
|
+
): string | null {
|
|
1103
|
+
if (typeof timestamp !== "number") {
|
|
1104
|
+
return null;
|
|
1105
|
+
}
|
|
530
1106
|
|
|
531
|
-
return
|
|
1107
|
+
return formatRelativeAge(timestamp);
|
|
532
1108
|
}
|
|
533
1109
|
|
|
534
|
-
function
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
const meta = buildAttachmentMeta(
|
|
540
|
-
media,
|
|
541
|
-
siteConfig,
|
|
542
|
-
textAttachmentContents.get(media.id),
|
|
543
|
-
);
|
|
544
|
-
const metaJson = safeJsonForHtml(meta);
|
|
545
|
-
const name = escapeHtml(meta.originalName);
|
|
546
|
-
const caption =
|
|
547
|
-
media.summary && media.summary !== media.originalName
|
|
548
|
-
? `<figcaption>${escapeHtml(media.summary)}</figcaption>`
|
|
549
|
-
: "";
|
|
550
|
-
|
|
551
|
-
if (
|
|
552
|
-
meta.kind === "text" &&
|
|
553
|
-
meta.contentFormat === "markdown" &&
|
|
554
|
-
typeof meta.content === "string"
|
|
555
|
-
) {
|
|
556
|
-
const summaryLabel = escapeHtml(
|
|
557
|
-
media.summary?.trim() || meta.originalName || "Text attachment",
|
|
558
|
-
);
|
|
559
|
-
return `<figure data-jant-node="attachment" data-jant-kind="text">
|
|
560
|
-
<script type="application/json" data-jant-meta>${metaJson}</script>
|
|
561
|
-
<details>
|
|
562
|
-
<summary>${summaryLabel}</summary>
|
|
563
|
-
<div class="prose jant-attachment-text-preview">${renderMarkdown(meta.content)}</div>
|
|
564
|
-
</details>
|
|
565
|
-
</figure>`;
|
|
1110
|
+
function formatCollectionActivityIso(
|
|
1111
|
+
timestamp: number | undefined,
|
|
1112
|
+
): string | null {
|
|
1113
|
+
if (typeof timestamp !== "number") {
|
|
1114
|
+
return null;
|
|
566
1115
|
}
|
|
567
1116
|
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
throw new Error(`Attachment ${media.id} is missing an export URL`);
|
|
571
|
-
}
|
|
1117
|
+
return toISOString(timestamp);
|
|
1118
|
+
}
|
|
572
1119
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
1120
|
+
/**
|
|
1121
|
+
* Group-aware sequence labels for the exported collection directory.
|
|
1122
|
+
*
|
|
1123
|
+
* Mirrors the main site's `computeSequenceLabels` in
|
|
1124
|
+
* `ui/shared/CollectionDirectory.tsx` so Hugo exports render the same numeric
|
|
1125
|
+
* indices (e.g. "00" "01" under the first divider, "10" "11" under the
|
|
1126
|
+
* second). Dividers themselves receive an empty string — their slot is
|
|
1127
|
+
* reserved so the returned array is index-aligned with the source list.
|
|
1128
|
+
*/
|
|
1129
|
+
function computeCollectionDirectorySequenceLabels(
|
|
1130
|
+
items: readonly ExportCollectionDirectorySourceItem[],
|
|
1131
|
+
): string[] {
|
|
1132
|
+
const isContentItem = (item: ExportCollectionDirectorySourceItem) =>
|
|
1133
|
+
(item.type === "collection" && item.collection) ||
|
|
1134
|
+
(item.type === "link" && item.label && item.url);
|
|
1135
|
+
|
|
1136
|
+
const groupSizes: number[] = [];
|
|
1137
|
+
let seenDivider = false;
|
|
1138
|
+
let ungroupedCount = 0;
|
|
1139
|
+
for (const item of items) {
|
|
1140
|
+
if (item.type === "divider") {
|
|
1141
|
+
seenDivider = true;
|
|
1142
|
+
groupSizes.push(0);
|
|
1143
|
+
} else if (isContentItem(item)) {
|
|
1144
|
+
if (seenDivider) {
|
|
1145
|
+
const lastGroupIndex = groupSizes.length - 1;
|
|
1146
|
+
const lastGroupSize = groupSizes[lastGroupIndex];
|
|
1147
|
+
if (lastGroupSize !== undefined) {
|
|
1148
|
+
groupSizes[lastGroupIndex] = lastGroupSize + 1;
|
|
1149
|
+
}
|
|
1150
|
+
} else {
|
|
1151
|
+
ungroupedCount += 1;
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
580
1154
|
}
|
|
581
1155
|
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
${caption}
|
|
592
|
-
</figure>`;
|
|
593
|
-
}
|
|
1156
|
+
const hasGroups = groupSizes.length > 0;
|
|
1157
|
+
const maxGroupIndex = Math.max(0, groupSizes.length - 1);
|
|
1158
|
+
const groupWidth = hasGroups
|
|
1159
|
+
? Math.max(1, maxGroupIndex.toString(36).length)
|
|
1160
|
+
: 0;
|
|
1161
|
+
const ungroupedItemWidth = Math.max(
|
|
1162
|
+
2,
|
|
1163
|
+
String(Math.max(0, ungroupedCount - 1)).length,
|
|
1164
|
+
);
|
|
594
1165
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
1166
|
+
const labels: string[] = [];
|
|
1167
|
+
let groupIndex = -1;
|
|
1168
|
+
let itemIndex = 0;
|
|
1169
|
+
|
|
1170
|
+
for (const item of items) {
|
|
1171
|
+
if (item.type === "divider") {
|
|
1172
|
+
groupIndex += 1;
|
|
1173
|
+
itemIndex = 0;
|
|
1174
|
+
labels.push("");
|
|
1175
|
+
} else if (isContentItem(item)) {
|
|
1176
|
+
if (hasGroups) {
|
|
1177
|
+
const g = Math.max(0, groupIndex)
|
|
1178
|
+
.toString(36)
|
|
1179
|
+
.padStart(groupWidth, "0");
|
|
1180
|
+
const i = itemIndex.toString(36);
|
|
1181
|
+
labels.push(g + i);
|
|
1182
|
+
} else {
|
|
1183
|
+
labels.push(String(itemIndex).padStart(ungroupedItemWidth, "0"));
|
|
1184
|
+
}
|
|
1185
|
+
itemIndex += 1;
|
|
1186
|
+
} else {
|
|
1187
|
+
labels.push("");
|
|
1188
|
+
}
|
|
601
1189
|
}
|
|
602
1190
|
|
|
603
|
-
|
|
604
|
-
const figcaption = description
|
|
605
|
-
? `<figcaption>${escapeHtml(description)}</figcaption>`
|
|
606
|
-
: "";
|
|
607
|
-
return `<figure data-jant-node="attachment" data-jant-kind="${escapeHtml(meta.kind)}">
|
|
608
|
-
<script type="application/json" data-jant-meta>${metaJson}</script>
|
|
609
|
-
<a href="${escapeHtml(src)}">${name}</a>
|
|
610
|
-
${figcaption}
|
|
611
|
-
</figure>`;
|
|
1191
|
+
return labels;
|
|
612
1192
|
}
|
|
613
1193
|
|
|
614
|
-
function
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
1194
|
+
function buildExportedCollectionDirectoryItems(
|
|
1195
|
+
items: readonly ExportCollectionDirectorySourceItem[],
|
|
1196
|
+
collectionSlugMap: Map<string, string>,
|
|
1197
|
+
collectionMetrics: Map<string, ExportedCollectionMetrics>,
|
|
1198
|
+
): ExportedCollectionDirectoryItem[] {
|
|
1199
|
+
const sequenceLabels = computeCollectionDirectorySequenceLabels(items);
|
|
1200
|
+
const exportedItems: ExportedCollectionDirectoryItem[] = [];
|
|
1201
|
+
|
|
1202
|
+
items.forEach((item, index) => {
|
|
1203
|
+
if (item.type === "divider") {
|
|
1204
|
+
exportedItems.push({
|
|
1205
|
+
type: "divider",
|
|
1206
|
+
label: item.label ?? null,
|
|
1207
|
+
});
|
|
1208
|
+
return;
|
|
1209
|
+
}
|
|
620
1210
|
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
1211
|
+
if (item.type === "link") {
|
|
1212
|
+
if (!item.label || !item.url) {
|
|
1213
|
+
return;
|
|
1214
|
+
}
|
|
624
1215
|
|
|
625
|
-
|
|
626
|
-
|
|
1216
|
+
const description = item.description?.trim();
|
|
1217
|
+
exportedItems.push({
|
|
1218
|
+
type: "link",
|
|
1219
|
+
sequence: sequenceLabels[index] ?? "",
|
|
1220
|
+
label: item.label,
|
|
1221
|
+
url: item.url,
|
|
1222
|
+
descriptionHtml: description ? renderMarkdown(description) : null,
|
|
1223
|
+
});
|
|
1224
|
+
return;
|
|
1225
|
+
}
|
|
627
1226
|
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
textAttachmentContent?: TextAttachmentContent,
|
|
632
|
-
): AttachmentExportMeta {
|
|
633
|
-
if (media.mimeType === "text/x-tiptap+json") {
|
|
634
|
-
if (!textAttachmentContent) {
|
|
635
|
-
throw new Error(
|
|
636
|
-
`Text attachment ${media.id} content is unavailable for export`,
|
|
637
|
-
);
|
|
1227
|
+
const collection = item.collection;
|
|
1228
|
+
if (!collection?.id) {
|
|
1229
|
+
return;
|
|
638
1230
|
}
|
|
639
1231
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
1232
|
+
const slug = collectionSlugMap.get(collection.id) ?? collection.slug;
|
|
1233
|
+
if (!slug) {
|
|
1234
|
+
return;
|
|
1235
|
+
}
|
|
1236
|
+
const metrics = collectionMetrics.get(collection.id);
|
|
1237
|
+
const activityTimestamp =
|
|
1238
|
+
metrics?.recentActivityAt ?? collection.recentActivityAt;
|
|
1239
|
+
|
|
1240
|
+
const collectionDescription = collection.description?.trim();
|
|
1241
|
+
exportedItems.push({
|
|
1242
|
+
type: "collection",
|
|
1243
|
+
sequence: sequenceLabels[index] ?? "",
|
|
1244
|
+
slug,
|
|
1245
|
+
title: collection.title || slug,
|
|
1246
|
+
descriptionHtml: collectionDescription
|
|
1247
|
+
? renderMarkdown(collectionDescription)
|
|
1248
|
+
: null,
|
|
1249
|
+
entryCount:
|
|
1250
|
+
metrics?.postCount ??
|
|
1251
|
+
(typeof collection.postCount === "number"
|
|
1252
|
+
? collection.postCount
|
|
1253
|
+
: undefined),
|
|
1254
|
+
recentActivityLabel: formatCollectionActivityLabel(activityTimestamp),
|
|
1255
|
+
recentActivityIso: formatCollectionActivityIso(activityTimestamp),
|
|
1256
|
+
});
|
|
1257
|
+
});
|
|
1258
|
+
|
|
1259
|
+
return exportedItems;
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
function buildExportedCollectionMetrics(
|
|
1263
|
+
collections: readonly Collection[],
|
|
1264
|
+
posts: readonly Post[],
|
|
1265
|
+
collectionsByPost: ReadonlyMap<string, readonly Collection[]>,
|
|
1266
|
+
): Map<string, ExportedCollectionMetrics> {
|
|
1267
|
+
const metrics = new Map<string, ExportedCollectionMetrics>();
|
|
1268
|
+
|
|
1269
|
+
for (const collection of collections) {
|
|
1270
|
+
metrics.set(collection.id, {
|
|
1271
|
+
postCount: 0,
|
|
1272
|
+
recentActivityAt: collection.updatedAt,
|
|
1273
|
+
});
|
|
656
1274
|
}
|
|
657
1275
|
|
|
658
|
-
const
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
: null,
|
|
671
|
-
mimeType: media.mimeType,
|
|
672
|
-
originalName: media.originalName,
|
|
673
|
-
size: media.size,
|
|
674
|
-
width: media.width,
|
|
675
|
-
height: media.height,
|
|
676
|
-
alt: media.alt,
|
|
677
|
-
position: media.position,
|
|
678
|
-
blurhash: media.blurhash,
|
|
679
|
-
waveform: media.waveform,
|
|
680
|
-
summary: media.summary,
|
|
681
|
-
chars: media.chars,
|
|
682
|
-
};
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
async function buildTextAttachmentContentMap(
|
|
686
|
-
mediaByPost: Map<string, Media[]>,
|
|
687
|
-
mediaService: Pick<MediaService, "getTextAttachmentContent">,
|
|
688
|
-
storage?: StorageDriver | null,
|
|
689
|
-
): Promise<Map<string, TextAttachmentContent>> {
|
|
690
|
-
const textAttachments = [...mediaByPost.values()]
|
|
691
|
-
.flat()
|
|
692
|
-
.filter((media) => media.mimeType === "text/x-tiptap+json");
|
|
1276
|
+
for (const post of posts) {
|
|
1277
|
+
if (post.deletedAt !== null) {
|
|
1278
|
+
continue;
|
|
1279
|
+
}
|
|
1280
|
+
// Drafts and private posts are excluded — they won't reach Hugo.
|
|
1281
|
+
if (post.status === "draft" || post.visibility === "private") {
|
|
1282
|
+
continue;
|
|
1283
|
+
}
|
|
1284
|
+
// Replies roll up into their thread root for directory metrics.
|
|
1285
|
+
if (post.replyToId !== null) {
|
|
1286
|
+
continue;
|
|
1287
|
+
}
|
|
693
1288
|
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
1289
|
+
const activityAt =
|
|
1290
|
+
post.lastActivityAt ??
|
|
1291
|
+
post.publishedAt ??
|
|
1292
|
+
post.updatedAt ??
|
|
1293
|
+
post.createdAt;
|
|
1294
|
+
const postCollections = collectionsByPost.get(post.id) ?? [];
|
|
1295
|
+
|
|
1296
|
+
for (const collection of postCollections) {
|
|
1297
|
+
const current = metrics.get(collection.id);
|
|
1298
|
+
if (!current) {
|
|
1299
|
+
continue;
|
|
1300
|
+
}
|
|
697
1301
|
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
if (!content) {
|
|
705
|
-
throw new Error(
|
|
706
|
-
`Text attachment ${media.id} content is unavailable for export`,
|
|
1302
|
+
if (current.postCount === 0) {
|
|
1303
|
+
current.recentActivityAt = activityAt;
|
|
1304
|
+
} else {
|
|
1305
|
+
current.recentActivityAt = Math.max(
|
|
1306
|
+
current.recentActivityAt,
|
|
1307
|
+
activityAt,
|
|
707
1308
|
);
|
|
708
1309
|
}
|
|
709
|
-
|
|
710
|
-
}
|
|
711
|
-
|
|
1310
|
+
current.postCount += 1;
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
712
1313
|
|
|
713
|
-
return
|
|
1314
|
+
return metrics;
|
|
714
1315
|
}
|
|
715
1316
|
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
.replace(/"/g, '\\"')
|
|
720
|
-
.replace(/\n/g, "\\n");
|
|
721
|
-
}
|
|
1317
|
+
// ---------------------------------------------------------------------------
|
|
1318
|
+
// Nav item resolution
|
|
1319
|
+
// ---------------------------------------------------------------------------
|
|
722
1320
|
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
1321
|
+
/**
|
|
1322
|
+
* System nav items on the main site store an empty `label` in the DB and
|
|
1323
|
+
* resolve their display text at render time through i18n
|
|
1324
|
+
* (`getNavItemDisplayLabel`). The Hugo export has no i18n runtime, so fall
|
|
1325
|
+
* back to these English defaults when serializing to `data/jant.toml`.
|
|
1326
|
+
* Users can still override by setting a custom label on the nav item.
|
|
1327
|
+
*/
|
|
1328
|
+
const SYSTEM_NAV_FALLBACK_LABELS: Record<string, string> = {
|
|
1329
|
+
latest: "Latest",
|
|
1330
|
+
featured: "Featured",
|
|
1331
|
+
collections: "Collections",
|
|
1332
|
+
archive: "Archive",
|
|
1333
|
+
rss: "RSS",
|
|
1334
|
+
settings: "Settings",
|
|
1335
|
+
};
|
|
1336
|
+
|
|
1337
|
+
function resolveNavItemLabel(item: SiteConfig["navItems"][number]): string {
|
|
1338
|
+
if (item.label) return item.label;
|
|
1339
|
+
if (item.systemKey) {
|
|
1340
|
+
const fallback = SYSTEM_NAV_FALLBACK_LABELS[item.systemKey];
|
|
1341
|
+
if (fallback) return fallback;
|
|
1342
|
+
}
|
|
1343
|
+
return item.label;
|
|
728
1344
|
}
|
|
729
1345
|
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
1346
|
+
/**
|
|
1347
|
+
* Resolve a nav item's final href for the Hugo export.
|
|
1348
|
+
*
|
|
1349
|
+
* Mirrors the runtime logic in `lib/view.ts:toNavItemView`. System URLs
|
|
1350
|
+
* stored in the DB ("/latest", "/featured") are not real routes — they get
|
|
1351
|
+
* rewritten to "/" when they match `homeDefaultView`, otherwise they
|
|
1352
|
+
* resolve to the dedicated path.
|
|
1353
|
+
*
|
|
1354
|
+
* The "rss" system nav item points at whichever Atom feed the site has
|
|
1355
|
+
* configured as its main feed: `mainRssFeed === "featured"` → the featured
|
|
1356
|
+
* section feed at `/featured/index.xml`, otherwise the home feed at
|
|
1357
|
+
* `/index.xml` (which mirrors the homepage's "latest" timeline).
|
|
1358
|
+
*/
|
|
1359
|
+
function resolveNavItemUrl(
|
|
1360
|
+
item: SiteConfig["navItems"][number],
|
|
1361
|
+
homeDefaultView: string,
|
|
1362
|
+
mainRssFeed: string,
|
|
733
1363
|
): string {
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
1364
|
+
if (item.systemKey === "latest") {
|
|
1365
|
+
return homeDefaultView === "latest" ? "/" : "/latest/";
|
|
1366
|
+
}
|
|
1367
|
+
if (item.systemKey === "featured") {
|
|
1368
|
+
return homeDefaultView === "featured" ? "/" : "/featured/";
|
|
1369
|
+
}
|
|
1370
|
+
if (item.systemKey === "collections") return "/collections/";
|
|
1371
|
+
if (item.systemKey === "archive") return "/archive/";
|
|
1372
|
+
if (item.systemKey === "rss") {
|
|
1373
|
+
return mainRssFeed === "featured" ? "/featured/index.xml" : "/index.xml";
|
|
1374
|
+
}
|
|
1375
|
+
return item.url;
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
// ---------------------------------------------------------------------------
|
|
1379
|
+
// hugo.toml + data TOMLs
|
|
1380
|
+
// ---------------------------------------------------------------------------
|
|
1381
|
+
|
|
1382
|
+
/** Escape a string for use inside a TOML double-quoted value. */
|
|
1383
|
+
function escapeTomlString(value: string): string {
|
|
1384
|
+
return value
|
|
1385
|
+
.replace(/\\/g, "\\\\")
|
|
1386
|
+
.replace(/"/g, '\\"')
|
|
1387
|
+
.replace(/\r/g, "\\r")
|
|
1388
|
+
.replace(/\n/g, "\\n");
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
function buildHugoToml(config: SiteConfig): string {
|
|
1392
|
+
const baseUrl = (config.siteUrl || "https://example.com").replace(/\/+$/, "");
|
|
1393
|
+
// Hugo requires language codes to be all lowercase (it rejects the BCP-47
|
|
1394
|
+
// casing `zh-Hant` / `zh-Hans` with "must be all lower case and no spaces").
|
|
1395
|
+
const language = config.siteLanguage.toLowerCase();
|
|
1396
|
+
const parts: string[] = [
|
|
1397
|
+
`baseURL = "${escapeTomlString(baseUrl)}/"`,
|
|
1398
|
+
`title = "${escapeTomlString(config.siteName)}"`,
|
|
1399
|
+
`languageCode = "${escapeTomlString(language)}"`,
|
|
1400
|
+
`defaultContentLanguage = "${escapeTomlString(language)}"`,
|
|
1401
|
+
'theme = "jant"',
|
|
1402
|
+
`paginate = ${config.pageSize}`,
|
|
1403
|
+
"enableRobotsTXT = true",
|
|
1404
|
+
// Disable Hugo's built-in taxonomies — jant has no tags or categories
|
|
1405
|
+
// and the default empty /tags/ and /categories/ pages are noise. This
|
|
1406
|
+
// must stay at the root (before any `[table]` header) so TOML doesn't
|
|
1407
|
+
// nest it under the previous table.
|
|
1408
|
+
"disableKinds = ['taxonomy', 'term']",
|
|
1409
|
+
"",
|
|
1410
|
+
"[permalinks]",
|
|
1411
|
+
' post = "/:slug/"',
|
|
1412
|
+
"",
|
|
1413
|
+
"[markup]",
|
|
1414
|
+
" [markup.goldmark]",
|
|
1415
|
+
" [markup.goldmark.renderer]",
|
|
1416
|
+
" unsafe = true",
|
|
742
1417
|
"",
|
|
743
|
-
|
|
1418
|
+
// Emit an Atom 2005 feed at the site root (/index.xml). Per-section
|
|
1419
|
+
// feeds (featured, archive, each collection) are opted in via each
|
|
1420
|
+
// section's front matter `outputs: ["html", "rss"]` — enabling RSS on
|
|
1421
|
+
// `section` globally here would also create a feed at every root post's
|
|
1422
|
+
// URL, since root posts are themselves branch bundles / sections.
|
|
1423
|
+
// Override the built-in RSS output format to emit Atom instead of
|
|
1424
|
+
// RSS 2.0 so the wire format mirrors the main site's `lib/feed.ts`.
|
|
1425
|
+
"[outputs]",
|
|
1426
|
+
' home = ["html", "rss"]',
|
|
1427
|
+
// Hugo's default for sections is ["html", "rss"] — without overriding
|
|
1428
|
+
// it here every root post (which is a section) would get its own
|
|
1429
|
+
// /{slug}/index.xml. Turn sections off by default and re-enable RSS
|
|
1430
|
+
// on just featured, archive, and each collection via per-section
|
|
1431
|
+
// front matter `outputs: ["html", "rss"]`.
|
|
1432
|
+
' section = ["html"]',
|
|
744
1433
|
"",
|
|
745
|
-
"[
|
|
1434
|
+
"[outputFormats]",
|
|
1435
|
+
" [outputFormats.RSS]",
|
|
1436
|
+
' mediaType = "application/atom+xml"',
|
|
1437
|
+
' baseName = "index"',
|
|
1438
|
+
// Use text/template (not html/template) so Hugo doesn't HTML-escape
|
|
1439
|
+
// the XML prologue, CDATA markers, or tag literals. Every dynamic
|
|
1440
|
+
// value inside the template passes through `transform.XMLEscape`.
|
|
1441
|
+
" isPlainText = true",
|
|
1442
|
+
' rel = "alternate"',
|
|
1443
|
+
"",
|
|
1444
|
+
"[mediaTypes]",
|
|
1445
|
+
' [mediaTypes."application/atom+xml"]',
|
|
1446
|
+
' suffixes = ["xml"]',
|
|
1447
|
+
"",
|
|
1448
|
+
"[params]",
|
|
1449
|
+
` description = "${escapeTomlString(config.siteDescription)}"`,
|
|
1450
|
+
` home_default_view = "${escapeTomlString(config.homeDefaultView)}"`,
|
|
1451
|
+
` main_rss_feed = "${escapeTomlString(config.mainRssFeed)}"`,
|
|
1452
|
+
` show_jant_branding_on_home = ${config.showJantBrandingOnHome}`,
|
|
1453
|
+
` show_header_avatar = ${config.showHeaderAvatar}`,
|
|
1454
|
+
` noindex = ${config.noindex}`,
|
|
1455
|
+
` theme_id = "${escapeTomlString(config.themeId)}"`,
|
|
1456
|
+
` default_theme_id = "${escapeTomlString(config.defaultThemeId)}"`,
|
|
1457
|
+
` font_theme_id = "${escapeTomlString(config.fontThemeId)}"`,
|
|
1458
|
+
` theme_mode = "${escapeTomlString(config.themeMode)}"`,
|
|
1459
|
+
` page_size = ${config.pageSize}`,
|
|
1460
|
+
` archive_page_size = ${config.archivePageSize}`,
|
|
1461
|
+
` rss_feed_limit = ${config.rssFeedLimit}`,
|
|
1462
|
+
];
|
|
1463
|
+
if (config.siteAvatarUrl) {
|
|
1464
|
+
parts.push(
|
|
1465
|
+
` site_avatar_url = "${escapeTomlString(config.siteAvatarUrl)}"`,
|
|
1466
|
+
);
|
|
1467
|
+
}
|
|
1468
|
+
if (config.faviconVersion) {
|
|
1469
|
+
parts.push(
|
|
1470
|
+
` favicon_version = "${escapeTomlString(config.faviconVersion)}"`,
|
|
1471
|
+
);
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
return `${parts.join("\n")}\n`;
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
function buildJantDataToml(
|
|
1478
|
+
config: SiteConfig,
|
|
1479
|
+
iconAssets: SiteIconAssets,
|
|
1480
|
+
directoryItems: readonly ExportedCollectionDirectoryItem[],
|
|
1481
|
+
): string {
|
|
1482
|
+
const footerHtml = config.siteFooter ? renderMarkdown(config.siteFooter) : "";
|
|
1483
|
+
const parts: string[] = [
|
|
746
1484
|
'format = "jant-site"',
|
|
747
1485
|
"version = 1",
|
|
748
|
-
`generated_at = "${
|
|
749
|
-
""
|
|
750
|
-
"
|
|
751
|
-
`
|
|
752
|
-
`
|
|
1486
|
+
`generated_at = "${escapeTomlString(toISOString(Math.floor(Date.now() / 1000)))}"`,
|
|
1487
|
+
`site_name = "${escapeTomlString(config.siteName)}"`,
|
|
1488
|
+
`site_description = "${escapeTomlString(config.siteDescription)}"`,
|
|
1489
|
+
`site_language = "${escapeTomlString(config.siteLanguage)}"`,
|
|
1490
|
+
`home_default_view = "${escapeTomlString(config.homeDefaultView)}"`,
|
|
1491
|
+
`main_rss_feed = "${escapeTomlString(config.mainRssFeed)}"`,
|
|
753
1492
|
`show_jant_branding_on_home = ${config.showJantBrandingOnHome}`,
|
|
754
1493
|
`show_header_avatar = ${config.showHeaderAvatar}`,
|
|
755
1494
|
`noindex = ${config.noindex}`,
|
|
756
1495
|
`site_avatar_mode = "${config.siteAvatarUrl ? "custom" : "none"}"`,
|
|
757
1496
|
`favicon_mode = "${iconAssets.faviconMode}"`,
|
|
758
1497
|
`apple_touch_mode = "${iconAssets.appleTouchMode}"`,
|
|
759
|
-
|
|
760
|
-
`
|
|
761
|
-
`
|
|
762
|
-
`
|
|
763
|
-
`
|
|
1498
|
+
`theme_id = "${escapeTomlString(config.themeId)}"`,
|
|
1499
|
+
`default_theme_id = "${escapeTomlString(config.defaultThemeId)}"`,
|
|
1500
|
+
`font_theme_id = "${escapeTomlString(config.fontThemeId)}"`,
|
|
1501
|
+
`theme_mode = "${escapeTomlString(config.themeMode)}"`,
|
|
1502
|
+
`page_size = ${config.pageSize}`,
|
|
1503
|
+
`archive_page_size = ${config.archivePageSize}`,
|
|
1504
|
+
`rss_feed_limit = ${config.rssFeedLimit}`,
|
|
1505
|
+
'favicon_path = "/favicon.ico"',
|
|
1506
|
+
'apple_touch_icon_path = "/apple-touch-icon.png"',
|
|
764
1507
|
];
|
|
765
|
-
|
|
766
1508
|
if (config.siteAvatarUrl) {
|
|
767
|
-
parts.push(`site_avatar_url = "${
|
|
1509
|
+
parts.push(`site_avatar_url = "${escapeTomlString(config.siteAvatarUrl)}"`);
|
|
768
1510
|
}
|
|
769
1511
|
if (config.faviconVersion) {
|
|
770
|
-
parts.push(
|
|
1512
|
+
parts.push(
|
|
1513
|
+
`favicon_version = "${escapeTomlString(config.faviconVersion)}"`,
|
|
1514
|
+
);
|
|
771
1515
|
}
|
|
772
1516
|
if (footerHtml) {
|
|
773
|
-
parts.push(`site_footer_html = "${
|
|
1517
|
+
parts.push(`site_footer_html = "${escapeTomlString(footerHtml)}"`);
|
|
774
1518
|
}
|
|
775
1519
|
if (config.siteFooter) {
|
|
776
|
-
parts.push(
|
|
1520
|
+
parts.push(
|
|
1521
|
+
`site_footer_markdown = "${escapeTomlString(config.siteFooter)}"`,
|
|
1522
|
+
);
|
|
777
1523
|
}
|
|
778
1524
|
|
|
779
1525
|
for (const item of config.navItems) {
|
|
1526
|
+
// `settings` is authenticated-only and has no corresponding page in the
|
|
1527
|
+
// static Hugo site — drop it at export time so it never shows up in nav.
|
|
1528
|
+
if (item.systemKey === "settings") continue;
|
|
780
1529
|
parts.push("");
|
|
781
|
-
parts.push("[[
|
|
782
|
-
parts.push(`type = "${
|
|
783
|
-
parts.push(`label = "${
|
|
784
|
-
parts.push(
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
}
|
|
1530
|
+
parts.push("[[nav]]");
|
|
1531
|
+
parts.push(`type = "${escapeTomlString(item.type)}"`);
|
|
1532
|
+
parts.push(`label = "${escapeTomlString(resolveNavItemLabel(item))}"`);
|
|
1533
|
+
parts.push(
|
|
1534
|
+
`url = "${escapeTomlString(resolveNavItemUrl(item, config.homeDefaultView, config.mainRssFeed))}"`,
|
|
1535
|
+
);
|
|
1536
|
+
parts.push(`system_key = "${escapeTomlString(item.systemKey ?? "")}"`);
|
|
1537
|
+
parts.push(`placement = "${escapeTomlString(item.placement ?? "header")}"`);
|
|
788
1538
|
}
|
|
789
1539
|
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
1540
|
+
for (const item of directoryItems) {
|
|
1541
|
+
parts.push("");
|
|
1542
|
+
parts.push("[[directory]]");
|
|
1543
|
+
parts.push(`type = "${escapeTomlString(item.type)}"`);
|
|
1544
|
+
if (item.type === "collection") {
|
|
1545
|
+
parts.push(`sequence = "${escapeTomlString(item.sequence)}"`);
|
|
1546
|
+
parts.push(`slug = "${escapeTomlString(item.slug)}"`);
|
|
1547
|
+
parts.push(`title = "${escapeTomlString(item.title)}"`);
|
|
1548
|
+
if (item.descriptionHtml) {
|
|
1549
|
+
parts.push(
|
|
1550
|
+
`description_html = "${escapeTomlString(item.descriptionHtml)}"`,
|
|
1551
|
+
);
|
|
1552
|
+
}
|
|
1553
|
+
if (typeof item.entryCount === "number") {
|
|
1554
|
+
parts.push(`entry_count = ${item.entryCount}`);
|
|
1555
|
+
}
|
|
1556
|
+
if (item.recentActivityLabel) {
|
|
1557
|
+
parts.push(
|
|
1558
|
+
`recent_activity_label = "${escapeTomlString(item.recentActivityLabel)}"`,
|
|
1559
|
+
);
|
|
1560
|
+
}
|
|
1561
|
+
if (item.recentActivityIso) {
|
|
1562
|
+
parts.push(
|
|
1563
|
+
`recent_activity_iso = "${escapeTomlString(item.recentActivityIso)}"`,
|
|
1564
|
+
);
|
|
1565
|
+
}
|
|
1566
|
+
} else if (item.type === "divider") {
|
|
1567
|
+
if (item.label !== null) {
|
|
1568
|
+
parts.push(`label = "${escapeTomlString(item.label)}"`);
|
|
1569
|
+
}
|
|
1570
|
+
} else {
|
|
1571
|
+
parts.push(`sequence = "${escapeTomlString(item.sequence)}"`);
|
|
1572
|
+
parts.push(`label = "${escapeTomlString(item.label)}"`);
|
|
1573
|
+
parts.push(`url = "${escapeTomlString(item.url)}"`);
|
|
1574
|
+
if (item.descriptionHtml) {
|
|
1575
|
+
parts.push(
|
|
1576
|
+
`description_html = "${escapeTomlString(item.descriptionHtml)}"`,
|
|
1577
|
+
);
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
798
1581
|
|
|
799
|
-
return `${parts.join("\n")}
|
|
800
|
-
`;
|
|
1582
|
+
return `${parts.join("\n")}\n`;
|
|
801
1583
|
}
|
|
802
1584
|
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
1585
|
+
// ---------------------------------------------------------------------------
|
|
1586
|
+
// README + .gitignore
|
|
1587
|
+
// ---------------------------------------------------------------------------
|
|
1588
|
+
|
|
1589
|
+
function buildGitignore(): string {
|
|
1590
|
+
return `# Hugo build output
|
|
1591
|
+
public/
|
|
1592
|
+
resources/
|
|
1593
|
+
.hugo_build.lock
|
|
1594
|
+
|
|
1595
|
+
# OS
|
|
1596
|
+
.DS_Store
|
|
1597
|
+
Thumbs.db
|
|
810
1598
|
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
+++
|
|
1599
|
+
# Editors
|
|
1600
|
+
.vscode/
|
|
1601
|
+
.idea/
|
|
1602
|
+
*.swp
|
|
816
1603
|
`;
|
|
817
1604
|
}
|
|
818
1605
|
|
|
819
1606
|
function buildReadme(siteName: string): string {
|
|
820
|
-
return `# ${siteName} —
|
|
1607
|
+
return `# ${siteName} — Hugo Export
|
|
821
1608
|
|
|
822
|
-
This is a static site exported from [Jant](https://github.com/jant-me/jant), ready to build with [
|
|
1609
|
+
This is a static site exported from [Jant](https://github.com/jant-me/jant), ready to build with [Hugo](https://gohugo.io/).
|
|
823
1610
|
|
|
824
|
-
## Install
|
|
1611
|
+
## Install Hugo
|
|
1612
|
+
|
|
1613
|
+
This export targets Hugo **extended 0.160.1+**.
|
|
825
1614
|
|
|
826
1615
|
**macOS (Homebrew):**
|
|
827
1616
|
|
|
828
1617
|
\`\`\`sh
|
|
829
|
-
brew install
|
|
1618
|
+
brew install hugo
|
|
830
1619
|
\`\`\`
|
|
831
1620
|
|
|
832
1621
|
**Windows (Scoop):**
|
|
833
1622
|
|
|
834
1623
|
\`\`\`sh
|
|
835
|
-
scoop install
|
|
1624
|
+
scoop install hugo-extended
|
|
836
1625
|
\`\`\`
|
|
837
1626
|
|
|
838
|
-
**Linux
|
|
839
|
-
|
|
840
|
-
\`\`\`sh
|
|
841
|
-
snap install zola --edge
|
|
842
|
-
\`\`\`
|
|
1627
|
+
**Linux:**
|
|
843
1628
|
|
|
844
|
-
|
|
1629
|
+
Download the extended build from <https://github.com/gohugoio/hugo/releases>.
|
|
845
1630
|
|
|
846
|
-
See the [
|
|
1631
|
+
See the [Hugo installation docs](https://gohugo.io/installation/) for more options.
|
|
847
1632
|
|
|
848
1633
|
## Quick start
|
|
849
1634
|
|
|
850
1635
|
Preview locally:
|
|
851
1636
|
|
|
852
1637
|
\`\`\`sh
|
|
853
|
-
|
|
1638
|
+
hugo serve
|
|
854
1639
|
\`\`\`
|
|
855
1640
|
|
|
856
|
-
Then open <http://
|
|
1641
|
+
Then open <http://localhost:1313> in your browser.
|
|
857
1642
|
|
|
858
1643
|
Build the site for deployment:
|
|
859
1644
|
|
|
860
1645
|
\`\`\`sh
|
|
861
|
-
|
|
1646
|
+
hugo --minify
|
|
862
1647
|
\`\`\`
|
|
863
1648
|
|
|
864
1649
|
The output goes to the \`public/\` directory. Upload it to any static host (Netlify, Vercel, Cloudflare Pages, GitHub Pages, etc.).
|
|
@@ -866,1886 +1651,61 @@ The output goes to the \`public/\` directory. Upload it to any static host (Netl
|
|
|
866
1651
|
## Project structure
|
|
867
1652
|
|
|
868
1653
|
\`\`\`
|
|
869
|
-
|
|
1654
|
+
hugo.toml — Site configuration (baseURL, title, theme, params)
|
|
870
1655
|
content/
|
|
871
|
-
_index.md
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
1656
|
+
_index.md — Home section
|
|
1657
|
+
archive/_index.md — Archive section
|
|
1658
|
+
collections/_index.md — Collections directory section
|
|
1659
|
+
featured/_index.md — Featured section
|
|
1660
|
+
{slug}/
|
|
1661
|
+
_index.md — Thread root (branch bundle)
|
|
1662
|
+
{reply-slug}/
|
|
1663
|
+
index.md — Reply (leaf bundle, not rendered as its own URL)
|
|
1664
|
+
data/
|
|
1665
|
+
jant.toml — Nav items, branding, display preferences, ordered collections directory
|
|
1666
|
+
themes/jant/ — Bundled Hugo theme (overrideable via layouts/ at the site root)
|
|
1667
|
+
static/ — Copy files here to add them to the published site
|
|
881
1668
|
\`\`\`
|
|
882
1669
|
|
|
883
1670
|
## Customizing
|
|
884
1671
|
|
|
885
|
-
- **Site settings** — edit \`
|
|
886
|
-
- **Jant metadata** — \`
|
|
887
|
-
- **Styles** — edit \`static/
|
|
888
|
-
- **Templates** —
|
|
889
|
-
- **Debugging** —
|
|
890
|
-
- **Collections** — posts are tagged with collections via the \`c\` taxonomy. Browse them at \`/c/\`.
|
|
891
|
-
|
|
892
|
-
## Notes
|
|
893
|
-
|
|
894
|
-
- The raw export API only writes content files. The CLI localizes media by default unless you pass \`--no-localize-media\`.
|
|
895
|
-
- Thread replies are merged into the root post as a single page. Reply metadata is preserved in HTML comments (\`<!-- jant:reply ... -->\`).
|
|
896
|
-
- Attachments are preserved as Jant HTML blocks (\`data-jant-node="attachments"\`). Text attachments embed canonical Markdown in the block metadata, while the rendered preview is display-only and ignored by \`jant site import\`.
|
|
897
|
-
- Posts with \`draft: true\` in front matter are only built when you pass the \`--drafts\` flag to \`zola build\` or \`zola serve\`.
|
|
898
|
-
`;
|
|
899
|
-
}
|
|
900
|
-
|
|
901
|
-
// ---------------------------------------------------------------------------
|
|
902
|
-
// Zola theme templates
|
|
903
|
-
// ---------------------------------------------------------------------------
|
|
904
|
-
|
|
905
|
-
const DECORATIVE_QUOTE_MARK_SVG = `<span class="decorative-quote-mark feed-quote-mark" aria-hidden="true">
|
|
906
|
-
<svg viewBox="0 0 96 96" role="presentation" focusable="false">
|
|
907
|
-
<path fill="currentColor" d="M24.4 10.5C16.9 17.7 11.5 26.8 8.2 37.7C4.9 48.7 4.8 58.9 7.8 68.2C10.3 75.7 15.4 79.5 22.9 79.5C28 79.5 32.2 77.8 35.4 74.2C38.6 70.7 40.2 66.5 40.2 61.4C40.2 56.5 38.8 52.6 36 49.6C33.3 46.6 29.7 45.1 25.2 45.1C23.4 45.1 21.8 45.3 20.2 45.8C22.2 37.3 26.7 29.2 33.6 21.4L24.4 10.5Z" />
|
|
908
|
-
<path fill="currentColor" d="M60.8 10.5C53.3 17.7 47.9 26.8 44.6 37.7C41.3 48.7 41.2 58.9 44.2 68.2C46.7 75.7 51.8 79.5 59.3 79.5C64.4 79.5 68.6 77.8 71.8 74.2C75 70.7 76.6 66.5 76.6 61.4C76.6 56.5 75.2 52.6 72.4 49.6C69.7 46.6 66.1 45.1 61.6 45.1C59.8 45.1 58.2 45.3 56.6 45.8C58.6 37.3 63.1 29.2 70 21.4L60.8 10.5Z" />
|
|
909
|
-
</svg>
|
|
910
|
-
</span>`;
|
|
911
|
-
|
|
912
|
-
const TEMPLATE_BASE = `<!DOCTYPE html>
|
|
913
|
-
<html lang="{{ config.default_language }}" data-theme-mode="{{ config.extra.jant.theme_mode | default(value='auto') }}">
|
|
914
|
-
<head>
|
|
915
|
-
<meta charset="utf-8">
|
|
916
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
917
|
-
<title>{% block title %}{{ config.title }}{% endblock %}</title>
|
|
918
|
-
{% if config.description %}
|
|
919
|
-
<meta name="description" content="{{ config.description }}">
|
|
920
|
-
{% endif %}
|
|
921
|
-
{% if config.extra.jant.noindex %}
|
|
922
|
-
<meta name="robots" content="noindex, nofollow">
|
|
923
|
-
{% endif %}
|
|
924
|
-
{% set favicon_href = get_url(path='favicon.ico') %}
|
|
925
|
-
{% set apple_touch_href = get_url(path='apple-touch-icon.png') %}
|
|
926
|
-
{% if config.extra.jant.favicon_version %}
|
|
927
|
-
<link rel="icon" href="{{ favicon_href }}?v={{ config.extra.jant.favicon_version }}" sizes="16x16 32x32">
|
|
928
|
-
<link rel="apple-touch-icon" href="{{ apple_touch_href }}?v={{ config.extra.jant.favicon_version }}">
|
|
929
|
-
{% else %}
|
|
930
|
-
<link rel="icon" href="{{ favicon_href }}" sizes="16x16 32x32">
|
|
931
|
-
<link rel="apple-touch-icon" href="{{ apple_touch_href }}">
|
|
932
|
-
{% endif %}
|
|
933
|
-
<link rel="stylesheet" href="{{ get_url(path='style.css') }}">
|
|
934
|
-
<link rel="stylesheet" href="{{ get_url(path='theme.css') }}">
|
|
935
|
-
<link rel="stylesheet" href="{{ get_url(path='custom.css') }}">
|
|
936
|
-
<link rel="alternate" type="application/atom+xml" title="{{ config.title }}" href="{{ get_url(path='atom.xml') }}">
|
|
937
|
-
</head>
|
|
938
|
-
<body>
|
|
939
|
-
<div class="site-page">
|
|
940
|
-
<header class="site-header">
|
|
941
|
-
<div class="site-header-inner">
|
|
942
|
-
<div class="{% block header_top_class %}site-header-top site-header-top-bordered{% endblock %}">
|
|
943
|
-
<a href="{{ config.base_url }}" class="site-logo">
|
|
944
|
-
{% if config.extra.jant.show_header_avatar and config.extra.jant.site_avatar_url %}
|
|
945
|
-
<img src="{{ config.extra.jant.site_avatar_url }}" class="site-logo-avatar" alt="">
|
|
946
|
-
{% endif %}
|
|
947
|
-
<span>{{ config.title }}</span>
|
|
948
|
-
</a>
|
|
949
|
-
<div class="site-header-right">
|
|
950
|
-
<nav class="site-header-nav" aria-label="Primary">
|
|
951
|
-
{% if config.extra.jant.nav and config.extra.jant.nav | length > 0 %}
|
|
952
|
-
{% for item in config.extra.jant.nav %}
|
|
953
|
-
{% if item.system_key == "settings" %}
|
|
954
|
-
{% elif item.system_key == "collections" %}
|
|
955
|
-
<a href="{{ get_url(path='c') }}" class="site-header-link">{{ item.label }}</a>
|
|
956
|
-
{% elif item.system_key == "rss" %}
|
|
957
|
-
<a href="{{ get_url(path='atom.xml') }}" class="site-header-link">{{ item.label }}</a>
|
|
958
|
-
{% elif item.system_key == "archive" %}
|
|
959
|
-
<a href="{{ get_url(path='archive') }}" class="site-header-link">{{ item.label }}</a>
|
|
960
|
-
{% else %}
|
|
961
|
-
<a href="{{ item.url }}" class="site-header-link">{{ item.label }}</a>
|
|
962
|
-
{% endif %}
|
|
963
|
-
{% endfor %}
|
|
964
|
-
{% else %}
|
|
965
|
-
<a href="{{ config.base_url }}/c/" class="site-header-link">Collections</a>
|
|
966
|
-
<a href="{{ get_url(path='atom.xml') }}" class="site-header-link">RSS</a>
|
|
967
|
-
{% endif %}
|
|
968
|
-
</nav>
|
|
969
|
-
</div>
|
|
970
|
-
</div>
|
|
971
|
-
</div>
|
|
972
|
-
</header>
|
|
973
|
-
|
|
974
|
-
<main class="site-main">
|
|
975
|
-
<div class="site-container">
|
|
976
|
-
<div class="{% block site_content_class %}site-content{% endblock %}">
|
|
977
|
-
{% block content %}{% endblock %}
|
|
978
|
-
</div>
|
|
979
|
-
</div>
|
|
980
|
-
</main>
|
|
981
|
-
|
|
982
|
-
{% if config.extra.jant.site_footer_html %}
|
|
983
|
-
<footer class="site-footer" data-footer>
|
|
984
|
-
<div class="site-container">
|
|
985
|
-
<div class="prose">{{ config.extra.jant.site_footer_html | safe }}</div>
|
|
986
|
-
</div>
|
|
987
|
-
</footer>
|
|
988
|
-
{% endif %}
|
|
989
|
-
</div>
|
|
990
|
-
</body>
|
|
991
|
-
</html>
|
|
992
|
-
`;
|
|
993
|
-
|
|
994
|
-
const TEMPLATE_INDEX = `{% extends "base.html" %}
|
|
995
|
-
{% import "macros.html" as macros %}
|
|
996
|
-
|
|
997
|
-
{% block title %}{{ config.title }}{% endblock %}
|
|
998
|
-
{% block header_top_class %}site-header-top site-header-top-bordered site-header-top-home{% endblock %}
|
|
999
|
-
{% block site_content_class %}site-content site-content-home{% endblock %}
|
|
1000
|
-
|
|
1001
|
-
{% block content %}
|
|
1002
|
-
<div data-page="home">
|
|
1003
|
-
{% if paginator.current_index > 1 %}
|
|
1004
|
-
<p class="page-context-label">Page {{ paginator.current_index }}</p>
|
|
1005
|
-
{% endif %}
|
|
1006
|
-
<div data-feed>
|
|
1007
|
-
<div id="timeline-feed">
|
|
1008
|
-
<div id="timeline-items">
|
|
1009
|
-
{% for page in paginator.pages %}
|
|
1010
|
-
{% if page.extra.visibility | default(value="public") != "latest_hidden" %}
|
|
1011
|
-
<div class="feed-item" data-timeline-item data-timeline-item-content>
|
|
1012
|
-
{% if not loop.first %}<hr class="feed-divider">{% endif %}
|
|
1013
|
-
{{ macros::post_card(page=page) }}
|
|
1014
|
-
</div>
|
|
1015
|
-
{% endif %}
|
|
1016
|
-
{% endfor %}
|
|
1017
|
-
</div>
|
|
1018
|
-
</div>
|
|
1019
|
-
</div>
|
|
1020
|
-
|
|
1021
|
-
{% if paginator.previous or paginator.next %}
|
|
1022
|
-
<nav class="pagination" aria-label="Pagination">
|
|
1023
|
-
<div class="pagination-side">
|
|
1024
|
-
{% if paginator.previous %}
|
|
1025
|
-
<a href="{{ paginator.previous }}" class="pagination-link">← Previous</a>
|
|
1026
|
-
{% endif %}
|
|
1027
|
-
</div>
|
|
1028
|
-
<div class="pagination-center">
|
|
1029
|
-
{% if paginator.number_pagers > 1 %}
|
|
1030
|
-
<span class="pagination-label">Page {{ paginator.current_index }} of {{ paginator.number_pagers }}</span>
|
|
1031
|
-
{% endif %}
|
|
1032
|
-
</div>
|
|
1033
|
-
<div class="pagination-side pagination-side-end">
|
|
1034
|
-
{% if paginator.next %}
|
|
1035
|
-
<a href="{{ paginator.next }}" class="pagination-link">Next →</a>
|
|
1036
|
-
{% endif %}
|
|
1037
|
-
</div>
|
|
1038
|
-
</nav>
|
|
1039
|
-
{% endif %}
|
|
1040
|
-
|
|
1041
|
-
{% if config.extra.jant.show_jant_branding_on_home %}
|
|
1042
|
-
<footer class="home-branding-credit">
|
|
1043
|
-
${HOME_BRANDING_PREFIX}
|
|
1044
|
-
<a href="${JANT_REPO_URL}" target="_blank" rel="noopener noreferrer">
|
|
1045
|
-
${buildJantLogoSvgMarkup("positive")}
|
|
1046
|
-
<span>${HOME_BRANDING_LINK_LABEL}</span>
|
|
1047
|
-
</a>
|
|
1048
|
-
</footer>
|
|
1049
|
-
{% endif %}
|
|
1050
|
-
</div>
|
|
1051
|
-
{% endblock %}
|
|
1052
|
-
`;
|
|
1053
|
-
|
|
1054
|
-
const TEMPLATE_PAGE = `{% extends "base.html" %}
|
|
1055
|
-
{% import "macros.html" as macros %}
|
|
1672
|
+
- **Site settings** — edit \`hugo.toml\` to change the baseURL, title, or pagination.
|
|
1673
|
+
- **Jant metadata** — \`data/jant.toml\` drives nav and the collections directory, and is preserved across round-trip import.
|
|
1674
|
+
- **Styles** — edit \`themes/jant/static/main.css\`, or drop a \`static/main.css\` at the site root to override.
|
|
1675
|
+
- **Templates** — add files under \`layouts/\` at the site root to override the bundled theme.
|
|
1676
|
+
- **Debugging** — from a Jant site project, run \`npx jant site export --directory ./my-site\`, then \`cd my-site && hugo serve\`.
|
|
1056
1677
|
|
|
1057
|
-
|
|
1678
|
+
## Fetching media locally
|
|
1058
1679
|
|
|
1059
|
-
|
|
1060
|
-
<div data-page="post">
|
|
1061
|
-
{{ macros::post_card(page=page, detail=true) }}
|
|
1062
|
-
</div>
|
|
1063
|
-
{% endblock %}
|
|
1064
|
-
`;
|
|
1065
|
-
|
|
1066
|
-
const TEMPLATE_SECTION = `{% extends "base.html" %}
|
|
1067
|
-
{% import "macros.html" as macros %}
|
|
1068
|
-
|
|
1069
|
-
{% block title %}{{ section.title }} — {{ config.title }}{% endblock %}
|
|
1070
|
-
|
|
1071
|
-
{% block content %}
|
|
1072
|
-
<div class="section-shell">
|
|
1073
|
-
<header class="section-header">
|
|
1074
|
-
<h1 class="section-title">{{ section.title }}</h1>
|
|
1075
|
-
{% if section.description %}
|
|
1076
|
-
<p class="section-description">{{ section.description }}</p>
|
|
1077
|
-
{% endif %}
|
|
1078
|
-
</header>
|
|
1079
|
-
|
|
1080
|
-
<div data-feed>
|
|
1081
|
-
<div id="timeline-feed">
|
|
1082
|
-
<div id="timeline-items">
|
|
1083
|
-
{% for page in section.pages %}
|
|
1084
|
-
{% if page.extra.visibility | default(value="public") != "latest_hidden" %}
|
|
1085
|
-
<div class="feed-item" data-timeline-item data-timeline-item-content>
|
|
1086
|
-
{% if not loop.first %}<hr class="feed-divider">{% endif %}
|
|
1087
|
-
{{ macros::post_card(page=page) }}
|
|
1088
|
-
</div>
|
|
1089
|
-
{% endif %}
|
|
1090
|
-
{% endfor %}
|
|
1091
|
-
</div>
|
|
1092
|
-
</div>
|
|
1093
|
-
</div>
|
|
1094
|
-
</div>
|
|
1095
|
-
{% endblock %}
|
|
1096
|
-
`;
|
|
1680
|
+
When the source site has a storage provider configured (R2/S3/local proxy), images and attachments in this export link to the provider URL instead of being bundled. That keeps the repo small but means the files aren't on disk — fine if Hugo can reach the internet, not fine if you want a fully self-contained archive.
|
|
1097
1681
|
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
{% block title %}Archive — {{ config.title }}{% endblock %}
|
|
1101
|
-
|
|
1102
|
-
{% block content %}
|
|
1103
|
-
{% set root = get_section(path="_index.md") %}
|
|
1104
|
-
<div class="section-shell archive-shell">
|
|
1105
|
-
<header class="section-header">
|
|
1106
|
-
<h1 class="section-title">Archive</h1>
|
|
1107
|
-
<p class="section-description">Every exported post in one chronological list.</p>
|
|
1108
|
-
</header>
|
|
1109
|
-
|
|
1110
|
-
<div class="archive-list">
|
|
1111
|
-
{% for year, year_pages in root.pages | group_by(attribute="year") %}
|
|
1112
|
-
{% for month, month_pages in year_pages | group_by(attribute="month") %}
|
|
1113
|
-
<section class="archive-month-group">
|
|
1114
|
-
<header class="archive-month-heading">
|
|
1115
|
-
{{ month_pages[0].date | date(format="%B %Y") }}
|
|
1116
|
-
</header>
|
|
1117
|
-
{% for page in month_pages %}
|
|
1118
|
-
{% if page.extra.visibility | default(value="public") == "public" %}
|
|
1119
|
-
{% set archive_title = page.title | default(value="") %}
|
|
1120
|
-
{% if archive_title == "" %}
|
|
1121
|
-
{% set archive_title = page.extra.summary_text | default(value="") %}
|
|
1122
|
-
{% endif %}
|
|
1123
|
-
{% if archive_title == "" %}
|
|
1124
|
-
{% set archive_title = page.summary | default(value=page.content) | striptags | trim %}
|
|
1125
|
-
{% endif %}
|
|
1126
|
-
<article class="archive-entry">
|
|
1127
|
-
<time class="archive-entry-date" datetime="{{ page.date }}">
|
|
1128
|
-
{{ page.date | date(format="%b %e, %Y") }}
|
|
1129
|
-
</time>
|
|
1130
|
-
<div class="archive-entry-main">
|
|
1131
|
-
<a href="{{ page.permalink }}" class="archive-entry-title">
|
|
1132
|
-
{{ archive_title | default(value="Untitled") | truncate(length=92) }}
|
|
1133
|
-
</a>
|
|
1134
|
-
<div class="archive-entry-meta">
|
|
1135
|
-
<span class="archive-entry-format">{{ page.extra.format | default(value="note") }}</span>
|
|
1136
|
-
{% set collections = page.taxonomies.c | default(value=[]) %}
|
|
1137
|
-
{% for col in collections %}
|
|
1138
|
-
{% set col_meta = get_section(path='c/' ~ col ~ '/_index.md') %}
|
|
1139
|
-
<a href="{{ get_taxonomy_url(kind='c', name=col) }}" class="archive-entry-tag">{{ col_meta.title | default(value=col) }}</a>
|
|
1140
|
-
{% endfor %}
|
|
1141
|
-
</div>
|
|
1142
|
-
</div>
|
|
1143
|
-
</article>
|
|
1144
|
-
{% endif %}
|
|
1145
|
-
{% endfor %}
|
|
1146
|
-
</section>
|
|
1147
|
-
{% endfor %}
|
|
1148
|
-
{% endfor %}
|
|
1149
|
-
</div>
|
|
1150
|
-
</div>
|
|
1151
|
-
{% endblock %}
|
|
1152
|
-
`;
|
|
1682
|
+
To download every referenced media file into \`static/media/\` and rewrite the references to local paths, run this from the root of the export:
|
|
1153
1683
|
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
{% block content %}
|
|
1159
|
-
<div class="section-shell">
|
|
1160
|
-
<header class="section-header">
|
|
1161
|
-
<h1 class="section-title">Collections</h1>
|
|
1162
|
-
<p class="section-description">Browse exported posts by collection.</p>
|
|
1163
|
-
</header>
|
|
1164
|
-
<ol class="collection-list">
|
|
1165
|
-
{% for term in terms %}
|
|
1166
|
-
{% set term_meta = get_section(path='c/' ~ term.name ~ '/_index.md') %}
|
|
1167
|
-
{% set latest_page = term.pages | first %}
|
|
1168
|
-
<li class="collection-list-item">
|
|
1169
|
-
<a href="{{ term.permalink }}" class="collection-list-link">
|
|
1170
|
-
<span class="collection-list-sequence" aria-hidden="true"></span>
|
|
1171
|
-
<span class="collection-list-content">
|
|
1172
|
-
<span class="collection-list-title">{{ term_meta.title | default(value=term.name) }}</span>
|
|
1173
|
-
<span class="collection-list-meta">
|
|
1174
|
-
<span>{{ term.pages | length }} entries</span>
|
|
1175
|
-
{% if latest_page %}
|
|
1176
|
-
<span>Updated {{ latest_page.updated | default(value=latest_page.date) | date(format="%Y-%m-%d") }}</span>
|
|
1177
|
-
{% endif %}
|
|
1178
|
-
</span>
|
|
1179
|
-
</span>
|
|
1180
|
-
</a>
|
|
1181
|
-
</li>
|
|
1182
|
-
{% endfor %}
|
|
1183
|
-
</ol>
|
|
1184
|
-
</div>
|
|
1185
|
-
{% endblock %}
|
|
1186
|
-
`;
|
|
1187
|
-
|
|
1188
|
-
const TEMPLATE_TAXONOMY_SINGLE = `{% extends "base.html" %}
|
|
1189
|
-
{% import "macros.html" as macros %}
|
|
1190
|
-
|
|
1191
|
-
{% block title %}{% set term_meta = get_section(path='c/' ~ term.name ~ '/_index.md') %}{{ term_meta.title | default(value=term.name) }} — {{ config.title }}{% endblock %}
|
|
1192
|
-
|
|
1193
|
-
{% block content %}
|
|
1194
|
-
{% set term_meta = get_section(path='c/' ~ term.name ~ '/_index.md') %}
|
|
1195
|
-
<div class="section-shell">
|
|
1196
|
-
<header class="section-header">
|
|
1197
|
-
<h1 class="section-title">{{ term_meta.title | default(value=term.name) }}</h1>
|
|
1198
|
-
{% if term_meta.description %}
|
|
1199
|
-
<p class="section-description">{{ term_meta.description }}</p>
|
|
1200
|
-
{% endif %}
|
|
1201
|
-
</header>
|
|
1202
|
-
<div data-feed>
|
|
1203
|
-
<div id="timeline-feed">
|
|
1204
|
-
<div id="timeline-items">
|
|
1205
|
-
{% for page in term.pages %}
|
|
1206
|
-
{% if page.extra.visibility | default(value="public") != "latest_hidden" %}
|
|
1207
|
-
<div class="feed-item" data-timeline-item data-timeline-item-content>
|
|
1208
|
-
{% if not loop.first %}<hr class="feed-divider">{% endif %}
|
|
1209
|
-
{{ macros::post_card(page=page) }}
|
|
1210
|
-
</div>
|
|
1211
|
-
{% endif %}
|
|
1212
|
-
{% endfor %}
|
|
1213
|
-
</div>
|
|
1214
|
-
</div>
|
|
1215
|
-
</div>
|
|
1216
|
-
</div>
|
|
1217
|
-
{% endblock %}
|
|
1218
|
-
`;
|
|
1684
|
+
\`\`\`sh
|
|
1685
|
+
npx @jant/core site pull-media --path .
|
|
1686
|
+
\`\`\`
|
|
1219
1687
|
|
|
1220
|
-
|
|
1221
|
-
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="{{ lang }}">
|
|
1222
|
-
<title>{% if section is defined and section.title %}{{ section.title }} · {% elif term is defined and taxonomy.name == "c" %}{% set term_meta = get_section(path='c/' ~ term.name ~ '/_index.md') %}{{ term_meta.title | default(value=term.name) }} · {% elif term is defined and term.name %}{{ term.name }} · {% endif %}{{ config.title }}</title>
|
|
1223
|
-
{% if config.description %}
|
|
1224
|
-
<subtitle>{{ config.description }}</subtitle>
|
|
1225
|
-
{% endif %}
|
|
1226
|
-
<link rel="self" type="application/atom+xml" href="{{ feed_url | safe }}" />
|
|
1227
|
-
<link rel="alternate" type="text/html" href="{% if section is defined %}{{ section.permalink }}{% elif term is defined %}{{ term.permalink }}{% else %}{{ config.base_url }}{% endif %}" />
|
|
1228
|
-
<id>{{ feed_url | safe }}</id>
|
|
1229
|
-
{% if last_updated is defined %}
|
|
1230
|
-
<updated>{{ last_updated | date(format="%+") }}</updated>
|
|
1231
|
-
{% else %}
|
|
1232
|
-
<updated>{{ config.extra.jant_export.generated_at | default(value="1970-01-01T00:00:00Z") }}</updated>
|
|
1233
|
-
{% endif %}
|
|
1234
|
-
{% set author_name = config.author | default(value="") %}
|
|
1235
|
-
{% if author_name %}
|
|
1236
|
-
<author><name>{{ author_name }}</name></author>
|
|
1237
|
-
{% endif %}
|
|
1238
|
-
{% for page in pages %}
|
|
1239
|
-
{% if page.extra.visibility | default(value="public") == "public" %}
|
|
1240
|
-
<entry>
|
|
1241
|
-
{% set entry_title = page.title | default(value="") %}
|
|
1242
|
-
{% set entry_summary = page.extra.summary_text | default(value="") %}
|
|
1243
|
-
{% if entry_summary == "" %}
|
|
1244
|
-
{% set entry_summary = page.summary | default(value=page.content) | striptags | trim %}
|
|
1245
|
-
{% endif %}
|
|
1246
|
-
{% if entry_summary == "" %}
|
|
1247
|
-
{% set entry_summary = page.permalink %}
|
|
1248
|
-
{% endif %}
|
|
1249
|
-
<title>{{ entry_title }}</title>
|
|
1250
|
-
<link rel="alternate" type="text/html" href="{{ page.permalink | safe }}" />
|
|
1251
|
-
<published>{{ page.date | date(format="%+") }}</published>
|
|
1252
|
-
<updated>{{ page.updated | default(value=page.date) | date(format="%+") }}</updated>
|
|
1253
|
-
<id>{{ page.permalink | safe }}</id>
|
|
1254
|
-
<summary type="text">{{ entry_summary }}</summary>
|
|
1255
|
-
<content type="html"><p>{{ entry_summary }}</p></content>
|
|
1256
|
-
</entry>
|
|
1257
|
-
{% endif %}
|
|
1258
|
-
{% endfor %}
|
|
1259
|
-
</feed>
|
|
1260
|
-
`;
|
|
1688
|
+
Safe to re-run; files already on disk are reused. Anything that fails to download keeps its original URL so the site still builds.
|
|
1261
1689
|
|
|
1262
|
-
|
|
1263
|
-
// Shared macro — single post card used by all list/detail templates
|
|
1264
|
-
// ---------------------------------------------------------------------------
|
|
1690
|
+
## Notes
|
|
1265
1691
|
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
<line x1="12" x2="12" y1="17" y2="22" />
|
|
1271
|
-
<path d="M5 17h14v-1.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V6h1a2 2 0 0 0 0-4H8a2 2 0 0 0 0 4h1v4.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24Z" />
|
|
1272
|
-
</svg>
|
|
1273
|
-
Pinned
|
|
1274
|
-
</span>
|
|
1275
|
-
<span class="post-status-badge post-status-private">
|
|
1276
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
|
|
1277
|
-
<path d="M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49" />
|
|
1278
|
-
<path d="M14.084 14.158a3 3 0 0 1-4.242-4.242" />
|
|
1279
|
-
<path d="M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143" />
|
|
1280
|
-
<path d="m2 2 20 20" />
|
|
1281
|
-
</svg>
|
|
1282
|
-
Private
|
|
1283
|
-
</span>
|
|
1284
|
-
</div>
|
|
1285
|
-
{% endmacro %}
|
|
1286
|
-
|
|
1287
|
-
{% macro post_rating(page) %}
|
|
1288
|
-
{% if page.extra.rating %}
|
|
1289
|
-
<div class="post-rating" aria-label="{{ page.extra.rating }} out of 5">
|
|
1290
|
-
{% for i in range(end=5) %}
|
|
1291
|
-
<span class="{% if i < page.extra.rating %}post-star-filled{% else %}post-star-empty{% endif %}">★</span>
|
|
1292
|
-
{% endfor %}
|
|
1293
|
-
</div>
|
|
1294
|
-
{% endif %}
|
|
1295
|
-
{% endmacro %}
|
|
1296
|
-
|
|
1297
|
-
{% macro post_footer(page, detail=false) %}
|
|
1298
|
-
{% set collections = page.taxonomies.c | default(value=[]) %}
|
|
1299
|
-
<footer class="post-menu-footer{% if detail %} post-footer-detail{% endif %}" data-post-meta>
|
|
1300
|
-
<div class="post-footer-meta">
|
|
1301
|
-
<span class="post-footer-featured" title="Featured">
|
|
1302
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.35" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
1303
|
-
<path d="${FEATURED_SPARKLE_PATH}" />
|
|
1304
|
-
</svg>
|
|
1305
|
-
<span class="sr-only">Featured</span>
|
|
1306
|
-
</span>
|
|
1307
|
-
<a href="{{ page.permalink }}" class="u-url post-date-link">
|
|
1308
|
-
<time class="dt-published" datetime="{{ page.date }}" title="{{ page.date }}">
|
|
1309
|
-
{{ page.date | date(format="%b %e, %Y") }}
|
|
1310
|
-
</time>
|
|
1311
|
-
</a>
|
|
1312
|
-
{% if page.extra.format == "link" and page.extra.link_url %}
|
|
1313
|
-
<a href="{{ page.extra.link_url }}" class="post-footer-external-link" target="_blank" rel="noopener noreferrer" aria-label="Open external link">
|
|
1314
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
1315
|
-
<path d="M7 17 17 7" />
|
|
1316
|
-
<path d="M9 7h8v8" />
|
|
1317
|
-
</svg>
|
|
1318
|
-
</a>
|
|
1319
|
-
{% endif %}
|
|
1320
|
-
{% if collections | length > 0 %}
|
|
1321
|
-
{% set first_collection = collections | first %}
|
|
1322
|
-
{% set first_collection_meta = get_section(path='c/' ~ first_collection ~ '/_index.md') %}
|
|
1323
|
-
{% set hidden_collection_count = collections | length %}
|
|
1324
|
-
<span class="post-collection-tags">
|
|
1325
|
-
<span class="post-collection-sep" aria-hidden="true">·</span>
|
|
1326
|
-
<a href="{{ get_taxonomy_url(kind='c', name=first_collection) }}" class="post-collection-tag">{{ first_collection_meta.title | default(value=first_collection) }}</a>
|
|
1327
|
-
{% if hidden_collection_count > 1 %}
|
|
1328
|
-
<span class="post-collection-more-wrap">
|
|
1329
|
-
<button type="button" class="post-collection-more" aria-haspopup="menu" data-collection-popover-trigger>
|
|
1330
|
-
and {{ hidden_collection_count - 1 }} more
|
|
1331
|
-
</button>
|
|
1332
|
-
<span class="post-collection-popover" role="menu" data-collection-popover>
|
|
1333
|
-
{% for col in collections %}
|
|
1334
|
-
{% if not loop.first %}
|
|
1335
|
-
{% set col_meta = get_section(path='c/' ~ col ~ '/_index.md') %}
|
|
1336
|
-
<a href="{{ get_taxonomy_url(kind='c', name=col) }}" class="post-collection-popover-item" role="menuitem">{{ col_meta.title | default(value=col) }}</a>
|
|
1337
|
-
{% endif %}
|
|
1338
|
-
{% endfor %}
|
|
1339
|
-
</span>
|
|
1340
|
-
</span>
|
|
1341
|
-
{% endif %}
|
|
1342
|
-
</span>
|
|
1343
|
-
{% endif %}
|
|
1344
|
-
</div>
|
|
1345
|
-
</footer>
|
|
1346
|
-
{% endmacro %}
|
|
1347
|
-
|
|
1348
|
-
{% macro note_card(page, detail=false) %}
|
|
1349
|
-
<article
|
|
1350
|
-
class="h-entry post-menu-target {% if detail %}post-detail-shell{% else %}post-card-shell{% endif %}"
|
|
1351
|
-
{% if detail %}data-page="post"{% endif %}
|
|
1352
|
-
data-post
|
|
1353
|
-
data-format="note"
|
|
1354
|
-
data-post-permalink="{{ page.permalink }}"
|
|
1355
|
-
{% if page.title %}data-post-has-title{% endif %}
|
|
1356
|
-
{% if page.extra.pinned %}data-post-pinned{% endif %}
|
|
1357
|
-
{% if page.extra.featured %}data-post-featured{% endif %}
|
|
1358
|
-
data-post-visibility="{{ page.extra.visibility | default(value='public') }}"
|
|
1359
|
-
>
|
|
1360
|
-
{{ self::post_status_badges() }}
|
|
1361
|
-
{% if page.title %}
|
|
1362
|
-
{% if detail %}
|
|
1363
|
-
<div class="post-header-block">
|
|
1364
|
-
<h1 class="p-name detail-title">{{ page.title }}</h1>
|
|
1365
|
-
{{ self::post_rating(page=page) }}
|
|
1366
|
-
</div>
|
|
1367
|
-
{% else %}
|
|
1368
|
-
<h2 class="p-name feed-note-title">
|
|
1369
|
-
<a href="{{ page.permalink }}" class="u-url post-title-link">{{ page.title }}</a>
|
|
1370
|
-
</h2>
|
|
1371
|
-
{% endif %}
|
|
1372
|
-
{% endif %}
|
|
1373
|
-
{% if detail and page.content %}
|
|
1374
|
-
<div class="e-content prose" data-post-body>{{ page.content | safe }}</div>
|
|
1375
|
-
{% elif not detail and page.summary %}
|
|
1376
|
-
<div class="e-content prose {% if page.title %}post-body-summary{% endif %}" data-post-body>{{ page.summary | safe }}</div>
|
|
1377
|
-
{% elif page.content %}
|
|
1378
|
-
<div class="e-content prose {% if page.title and not detail %}post-body-summary{% endif %}" data-post-body>{{ page.content | safe }}</div>
|
|
1379
|
-
{% endif %}
|
|
1380
|
-
{% if not detail or not page.title %}
|
|
1381
|
-
{{ self::post_rating(page=page) }}
|
|
1382
|
-
{% endif %}
|
|
1383
|
-
{{ self::post_footer(page=page, detail=detail) }}
|
|
1384
|
-
</article>
|
|
1385
|
-
{% endmacro %}
|
|
1386
|
-
|
|
1387
|
-
{% macro link_card(page, detail=false) %}
|
|
1388
|
-
<article
|
|
1389
|
-
class="h-entry post-menu-target {% if detail %}post-detail-shell post-detail-link{% else %}feed-link-post{% endif %}"
|
|
1390
|
-
{% if detail %}data-page="post"{% endif %}
|
|
1391
|
-
data-post
|
|
1392
|
-
data-format="link"
|
|
1393
|
-
data-post-permalink="{{ page.permalink }}"
|
|
1394
|
-
{% if page.title %}data-post-has-title{% endif %}
|
|
1395
|
-
{% if page.extra.pinned %}data-post-pinned{% endif %}
|
|
1396
|
-
{% if page.extra.featured %}data-post-featured{% endif %}
|
|
1397
|
-
data-post-visibility="{{ page.extra.visibility | default(value='public') }}"
|
|
1398
|
-
>
|
|
1399
|
-
{% if not detail %}
|
|
1400
|
-
<div class="feed-card feed-card-link">
|
|
1401
|
-
{% endif %}
|
|
1402
|
-
{{ self::post_status_badges() }}
|
|
1403
|
-
{% if page.extra.link_url %}
|
|
1404
|
-
<a href="{{ page.extra.link_url }}" class="feed-link-domain" rel="noopener noreferrer" target="_blank">
|
|
1405
|
-
<svg class="feed-link-domain-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
|
|
1406
|
-
<path d="M13.5 6H5.25A2.25 2.25 0 0 0 3 8.25v10.5A2.25 2.25 0 0 0 5.25 21h10.5A2.25 2.25 0 0 0 18 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25" />
|
|
1407
|
-
</svg>
|
|
1408
|
-
<span>{{ page.extra.link_url | split(pat='//') | nth(n=1) | split(pat='/') | first }}</span>
|
|
1409
|
-
</a>
|
|
1410
|
-
{% endif %}
|
|
1411
|
-
{% if page.title %}
|
|
1412
|
-
{% if detail %}
|
|
1413
|
-
<div class="post-header-block">
|
|
1414
|
-
<h1 class="p-name detail-title feed-link-title">
|
|
1415
|
-
<a href="{{ page.extra.link_url | default(value=page.permalink) }}" class="u-url feed-link-title-link" {% if page.extra.link_url %}target="_blank" rel="noopener noreferrer"{% endif %}>{{ page.title }}</a>
|
|
1416
|
-
</h1>
|
|
1417
|
-
{{ self::post_rating(page=page) }}
|
|
1418
|
-
</div>
|
|
1419
|
-
{% else %}
|
|
1420
|
-
<h2 class="p-name feed-link-title">
|
|
1421
|
-
<a href="{{ page.extra.link_url | default(value=page.permalink) }}" class="u-url feed-link-title-link" {% if page.extra.link_url %}target="_blank" rel="noopener noreferrer"{% endif %}>{{ page.title }}</a>
|
|
1422
|
-
</h2>
|
|
1423
|
-
{% endif %}
|
|
1424
|
-
{% endif %}
|
|
1425
|
-
{% if detail and page.content %}
|
|
1426
|
-
<div class="e-content prose feed-link-summary" data-post-body>{{ page.content | safe }}</div>
|
|
1427
|
-
{% elif not detail and page.summary %}
|
|
1428
|
-
<div class="e-content prose feed-link-summary" data-post-body>{{ page.summary | safe }}</div>
|
|
1429
|
-
{% elif page.content %}
|
|
1430
|
-
<div class="e-content prose feed-link-summary" data-post-body>{{ page.content | safe }}</div>
|
|
1431
|
-
{% endif %}
|
|
1432
|
-
{% if not detail or not page.title %}
|
|
1433
|
-
{{ self::post_rating(page=page) }}
|
|
1434
|
-
{% endif %}
|
|
1435
|
-
{% if not detail %}
|
|
1436
|
-
</div>
|
|
1437
|
-
{% endif %}
|
|
1438
|
-
{{ self::post_footer(page=page, detail=detail) }}
|
|
1439
|
-
</article>
|
|
1440
|
-
{% endmacro %}
|
|
1441
|
-
|
|
1442
|
-
{% macro quote_card(page, detail=false) %}
|
|
1443
|
-
<article
|
|
1444
|
-
class="h-entry post-menu-target feed-quote-post {% if detail %}post-detail-shell{% endif %}"
|
|
1445
|
-
{% if detail %}data-page="post"{% endif %}
|
|
1446
|
-
data-post
|
|
1447
|
-
data-format="quote"
|
|
1448
|
-
data-post-permalink="{{ page.permalink }}"
|
|
1449
|
-
{% if page.extra.pinned %}data-post-pinned{% endif %}
|
|
1450
|
-
{% if page.extra.featured %}data-post-featured{% endif %}
|
|
1451
|
-
data-post-visibility="{{ page.extra.visibility | default(value='public') }}"
|
|
1452
|
-
>
|
|
1453
|
-
{{ self::post_status_badges() }}
|
|
1454
|
-
{% if page.extra.quote_text %}
|
|
1455
|
-
<blockquote class="feed-quote feed-quote-card">
|
|
1456
|
-
${DECORATIVE_QUOTE_MARK_SVG}
|
|
1457
|
-
<div class="e-content feed-quote-content">{{ page.extra.quote_text }}</div>
|
|
1458
|
-
</blockquote>
|
|
1459
|
-
{% endif %}
|
|
1460
|
-
{% if page.extra.source_name or page.extra.source_url %}
|
|
1461
|
-
<div class="feed-quote-attribution">
|
|
1462
|
-
{% if page.extra.source_url %}
|
|
1463
|
-
<a href="{{ page.extra.source_url }}" class="feed-quote-source" target="_blank" rel="noopener noreferrer">
|
|
1464
|
-
{{ page.extra.source_name | default(value=page.extra.source_url | split(pat='//') | nth(n=1) | split(pat='/') | first) }}
|
|
1465
|
-
</a>
|
|
1466
|
-
{% else %}
|
|
1467
|
-
<span>{{ page.extra.source_name }}</span>
|
|
1468
|
-
{% endif %}
|
|
1469
|
-
</div>
|
|
1470
|
-
{% endif %}
|
|
1471
|
-
{% if detail and page.content %}
|
|
1472
|
-
<div class="feed-quote-commentary prose" data-post-body>{{ page.content | safe }}</div>
|
|
1473
|
-
{% elif not detail and page.summary %}
|
|
1474
|
-
<div class="feed-quote-commentary prose" data-post-body>{{ page.summary | safe }}</div>
|
|
1475
|
-
{% elif page.content %}
|
|
1476
|
-
<div class="feed-quote-commentary prose" data-post-body>{{ page.content | safe }}</div>
|
|
1477
|
-
{% endif %}
|
|
1478
|
-
{{ self::post_rating(page=page) }}
|
|
1479
|
-
{{ self::post_footer(page=page, detail=detail) }}
|
|
1480
|
-
</article>
|
|
1481
|
-
{% endmacro %}
|
|
1482
|
-
|
|
1483
|
-
{% macro post_card(page, detail=false) %}
|
|
1484
|
-
{% if page.extra.format == "link" %}
|
|
1485
|
-
{{ self::link_card(page=page, detail=detail) }}
|
|
1486
|
-
{% elif page.extra.format == "quote" %}
|
|
1487
|
-
{{ self::quote_card(page=page, detail=detail) }}
|
|
1488
|
-
{% else %}
|
|
1489
|
-
{{ self::note_card(page=page, detail=detail) }}
|
|
1490
|
-
{% endif %}
|
|
1491
|
-
{% endmacro %}
|
|
1692
|
+
- Each thread is a Hugo branch bundle. Replies live as nested leaf bundles with \`build.render = "never"\` so they do not produce standalone URLs; they render inside the thread page.
|
|
1693
|
+
- \`/{reply-slug}/\` URLs are preserved via \`aliases:\` on the root post, so old links still land on the right thread anchor.
|
|
1694
|
+
- Media is emitted under \`static/media/{id}.ext\` and referenced from a flat \`media:\` array on each post. When a storage provider has a configured public URL (R2/S3/local proxy), the exporter links to the provider URL instead of re-bundling the bytes.
|
|
1695
|
+
- Posts with \`draft: true\` in front matter are only built when you pass \`--buildDrafts\` to \`hugo\` / \`hugo serve\`.
|
|
1492
1696
|
`;
|
|
1697
|
+
}
|
|
1493
1698
|
|
|
1494
1699
|
// ---------------------------------------------------------------------------
|
|
1495
|
-
//
|
|
1700
|
+
// Re-exports for consumers (kept so existing entry points compile)
|
|
1496
1701
|
// ---------------------------------------------------------------------------
|
|
1497
1702
|
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
|
1508
|
-
"Helvetica Neue", Helvetica, Arial, "PingFang TC", "PingFang SC",
|
|
1509
|
-
"Hiragino Sans CNS", "Hiragino Sans GB", "Microsoft JhengHei",
|
|
1510
|
-
"Microsoft YaHei", "Noto Sans CJK TC", "Noto Sans CJK SC", sans-serif;
|
|
1511
|
-
--font-heading:
|
|
1512
|
-
"New York Small", "New York", "Iowan Old Style", Charter,
|
|
1513
|
-
"Bitstream Charter", "Source Serif 4", Cambria, "Sitka Text", Georgia,
|
|
1514
|
-
var(--font-cjk-serif-fallback), ui-serif, serif;
|
|
1515
|
-
--font-site-title:
|
|
1516
|
-
"New York Small", "New York", "Iowan Old Style", Charter,
|
|
1517
|
-
"Bitstream Charter", "Source Serif 4", Cambria, "Sitka Text", Georgia,
|
|
1518
|
-
var(--font-cjk-serif-fallback), ui-serif, serif;
|
|
1519
|
-
--font-serif:
|
|
1520
|
-
var(--font-cjk-serif-fallback), ui-serif, "New York Small", "New York",
|
|
1521
|
-
"Iowan Old Style", Charter, Georgia, "Times New Roman", Times, serif;
|
|
1522
|
-
--font-mono:
|
|
1523
|
-
ui-monospace, Menlo, Monaco, Consolas, "Cascadia Code", "Courier New",
|
|
1524
|
-
monospace;
|
|
1525
|
-
--fw-regular: 400;
|
|
1526
|
-
--fw-medium: 500;
|
|
1527
|
-
--fw-semibold: 600;
|
|
1528
|
-
--text-sm: 0.8125rem;
|
|
1529
|
-
--text-base: 0.9375rem;
|
|
1530
|
-
--feed-note-title-size: 1.25rem;
|
|
1531
|
-
--feed-note-title-leading: 1.3;
|
|
1532
|
-
--type-body-size: var(--text-base);
|
|
1533
|
-
--type-body-leading: 1.66;
|
|
1534
|
-
--type-body-tracking: 0.002em;
|
|
1535
|
-
--type-heading-weight: var(--fw-medium);
|
|
1536
|
-
--type-heading-leading: 1.26;
|
|
1537
|
-
--type-heading-tracking: -0.02em;
|
|
1538
|
-
--type-display-leading: 1.04;
|
|
1539
|
-
--type-label-weight: var(--fw-medium);
|
|
1540
|
-
--type-label-tracking: 0.08em;
|
|
1541
|
-
--site-padding: 1.5rem;
|
|
1542
|
-
--content-gap: 1rem;
|
|
1543
|
-
--space-xl: 2rem;
|
|
1544
|
-
--avatar-size: 28px;
|
|
1545
|
-
--avatar-radius: 50%;
|
|
1546
|
-
--media-radius: 0.5rem;
|
|
1547
|
-
--background: oklch(0.975 0.015 92);
|
|
1548
|
-
--foreground: oklch(0.29 0.01 70);
|
|
1549
|
-
--card: oklch(0.975 0.015 92);
|
|
1550
|
-
--primary: oklch(0.3633 0.0697 159.95);
|
|
1551
|
-
--primary-foreground: oklch(0.985 0.008 92);
|
|
1552
|
-
--muted: oklch(0.942 0.014 96);
|
|
1553
|
-
--muted-foreground: oklch(0.52 0.008 70);
|
|
1554
|
-
--accent: oklch(0.942 0.014 96);
|
|
1555
|
-
--border: oklch(0.892 0.014 98);
|
|
1556
|
-
--site-accent: oklch(0.4406 0.0568 159.95);
|
|
1557
|
-
--site-accent-text: var(--primary-foreground);
|
|
1558
|
-
--site-page-bg: var(--background);
|
|
1559
|
-
--site-elevated-bg: var(--background);
|
|
1560
|
-
--site-nav-hover-bg: var(--accent);
|
|
1561
|
-
--site-text-primary: var(--foreground);
|
|
1562
|
-
--site-text-secondary: var(--muted-foreground);
|
|
1563
|
-
--site-text-placeholder: oklch(from var(--muted-foreground) l c h / 0.5);
|
|
1564
|
-
--site-divider: var(--border);
|
|
1565
|
-
--site-feed-card-bg: color-mix(
|
|
1566
|
-
in srgb,
|
|
1567
|
-
var(--site-elevated-bg) 88%,
|
|
1568
|
-
var(--site-nav-hover-bg)
|
|
1569
|
-
);
|
|
1570
|
-
--site-feed-card-border: color-mix(
|
|
1571
|
-
in srgb,
|
|
1572
|
-
var(--site-divider) 78%,
|
|
1573
|
-
transparent
|
|
1574
|
-
);
|
|
1575
|
-
--site-feed-card-shadow: color-mix(
|
|
1576
|
-
in srgb,
|
|
1577
|
-
var(--site-text-primary) 12%,
|
|
1578
|
-
transparent
|
|
1579
|
-
);
|
|
1580
|
-
--site-feed-divider-color: color-mix(
|
|
1581
|
-
in srgb,
|
|
1582
|
-
var(--site-text-secondary) 30%,
|
|
1583
|
-
transparent
|
|
1584
|
-
);
|
|
1585
|
-
}
|
|
1586
|
-
|
|
1587
|
-
@media (prefers-color-scheme: dark) {
|
|
1588
|
-
:root:not([data-theme-mode="light"]) {
|
|
1589
|
-
color-scheme: dark;
|
|
1590
|
-
--background: oklch(0.182 0.003 95);
|
|
1591
|
-
--foreground: oklch(0.895 0.006 88);
|
|
1592
|
-
--card: oklch(0.182 0.003 95);
|
|
1593
|
-
--primary: oklch(0.6966 0.0528 159.95);
|
|
1594
|
-
--primary-foreground: oklch(0.17 0.003 95);
|
|
1595
|
-
--muted: oklch(0.238 0.003 95);
|
|
1596
|
-
--muted-foreground: oklch(0.67 0.005 88);
|
|
1597
|
-
--accent: oklch(0.238 0.003 95);
|
|
1598
|
-
--border: oklch(0.305 0.003 95);
|
|
1599
|
-
--site-accent: oklch(0.7306 0.0478 159.95);
|
|
1600
|
-
}
|
|
1601
|
-
}
|
|
1602
|
-
|
|
1603
|
-
*,
|
|
1604
|
-
*::before,
|
|
1605
|
-
*::after {
|
|
1606
|
-
box-sizing: border-box;
|
|
1607
|
-
}
|
|
1608
|
-
|
|
1609
|
-
html {
|
|
1610
|
-
font-size: 16px;
|
|
1611
|
-
background-color: var(--site-page-bg);
|
|
1612
|
-
color: var(--site-text-primary);
|
|
1613
|
-
}
|
|
1614
|
-
|
|
1615
|
-
body {
|
|
1616
|
-
margin: 0;
|
|
1617
|
-
font-family: var(--font-body);
|
|
1618
|
-
font-size: var(--type-body-size);
|
|
1619
|
-
line-height: var(--type-body-leading);
|
|
1620
|
-
letter-spacing: var(--type-body-tracking);
|
|
1621
|
-
color: var(--site-text-primary);
|
|
1622
|
-
background: var(--site-page-bg);
|
|
1623
|
-
text-rendering: optimizeLegibility;
|
|
1624
|
-
-webkit-font-smoothing: antialiased;
|
|
1625
|
-
}
|
|
1626
|
-
|
|
1627
|
-
a {
|
|
1628
|
-
color: inherit;
|
|
1629
|
-
text-decoration-thickness: 1px;
|
|
1630
|
-
text-underline-offset: 3px;
|
|
1631
|
-
}
|
|
1632
|
-
|
|
1633
|
-
img,
|
|
1634
|
-
svg,
|
|
1635
|
-
video {
|
|
1636
|
-
display: block;
|
|
1637
|
-
max-width: 100%;
|
|
1638
|
-
}
|
|
1639
|
-
|
|
1640
|
-
img {
|
|
1641
|
-
height: auto;
|
|
1642
|
-
}
|
|
1643
|
-
|
|
1644
|
-
.site-page {
|
|
1645
|
-
min-height: 100vh;
|
|
1646
|
-
min-height: 100dvh;
|
|
1647
|
-
background-color: var(--site-page-bg);
|
|
1648
|
-
}
|
|
1649
|
-
|
|
1650
|
-
.site-header {
|
|
1651
|
-
max-width: var(--site-width);
|
|
1652
|
-
margin: 0 auto;
|
|
1653
|
-
padding: 24px var(--site-padding) 0;
|
|
1654
|
-
}
|
|
1655
|
-
|
|
1656
|
-
.site-header-inner {
|
|
1657
|
-
display: flex;
|
|
1658
|
-
flex-direction: column;
|
|
1659
|
-
align-items: flex-start;
|
|
1660
|
-
}
|
|
1661
|
-
|
|
1662
|
-
.site-header-top {
|
|
1663
|
-
display: flex;
|
|
1664
|
-
align-items: center;
|
|
1665
|
-
justify-content: space-between;
|
|
1666
|
-
gap: 0.85rem;
|
|
1667
|
-
flex-wrap: wrap;
|
|
1668
|
-
min-height: 2.5rem;
|
|
1669
|
-
width: 100%;
|
|
1670
|
-
}
|
|
1671
|
-
|
|
1672
|
-
.site-header-top-bordered {
|
|
1673
|
-
padding-bottom: 15px;
|
|
1674
|
-
border-bottom: 0.5px solid
|
|
1675
|
-
color-mix(in srgb, var(--site-divider) 72%, transparent);
|
|
1676
|
-
}
|
|
1677
|
-
|
|
1678
|
-
.site-header-top-home {
|
|
1679
|
-
border-bottom-color: color-mix(in srgb, var(--site-divider) 72%, transparent);
|
|
1680
|
-
padding-bottom: 14px;
|
|
1681
|
-
}
|
|
1682
|
-
|
|
1683
|
-
.site-header-right {
|
|
1684
|
-
display: flex;
|
|
1685
|
-
align-items: center;
|
|
1686
|
-
gap: 0.55rem;
|
|
1687
|
-
margin-left: auto;
|
|
1688
|
-
min-width: 0;
|
|
1689
|
-
}
|
|
1690
|
-
|
|
1691
|
-
.site-logo {
|
|
1692
|
-
display: inline-flex;
|
|
1693
|
-
align-items: center;
|
|
1694
|
-
gap: 10px;
|
|
1695
|
-
padding: 0.15rem 0;
|
|
1696
|
-
font-size: clamp(1.18rem, 1.08rem + 0.45vw, 1.34rem);
|
|
1697
|
-
font-weight: var(--fw-medium);
|
|
1698
|
-
font-family: var(--font-site-title);
|
|
1699
|
-
letter-spacing: -0.03em;
|
|
1700
|
-
color: var(--site-text-primary);
|
|
1701
|
-
text-decoration: none;
|
|
1702
|
-
line-height: var(--type-display-leading);
|
|
1703
|
-
}
|
|
1704
|
-
|
|
1705
|
-
.site-logo-avatar {
|
|
1706
|
-
width: calc(var(--avatar-size) + 2px);
|
|
1707
|
-
height: calc(var(--avatar-size) + 2px);
|
|
1708
|
-
border-radius: var(--avatar-radius);
|
|
1709
|
-
object-fit: cover;
|
|
1710
|
-
box-shadow: 0 0 0 1px color-mix(in srgb, var(--site-divider) 82%, transparent);
|
|
1711
|
-
}
|
|
1712
|
-
|
|
1713
|
-
.site-header-nav {
|
|
1714
|
-
display: flex;
|
|
1715
|
-
align-items: center;
|
|
1716
|
-
flex-wrap: wrap;
|
|
1717
|
-
justify-content: flex-end;
|
|
1718
|
-
gap: 1rem;
|
|
1719
|
-
}
|
|
1720
|
-
|
|
1721
|
-
.site-header-link {
|
|
1722
|
-
display: inline-flex;
|
|
1723
|
-
align-items: center;
|
|
1724
|
-
position: relative;
|
|
1725
|
-
min-height: 2rem;
|
|
1726
|
-
padding: 0.2rem 0 0.5rem;
|
|
1727
|
-
cursor: pointer;
|
|
1728
|
-
font-size: 0.84rem;
|
|
1729
|
-
line-height: 1;
|
|
1730
|
-
letter-spacing: 0.01em;
|
|
1731
|
-
color: var(--site-text-secondary);
|
|
1732
|
-
text-decoration: none;
|
|
1733
|
-
transition:
|
|
1734
|
-
color 0.15s,
|
|
1735
|
-
opacity 0.15s;
|
|
1736
|
-
}
|
|
1737
|
-
|
|
1738
|
-
.site-header-link::after {
|
|
1739
|
-
content: "";
|
|
1740
|
-
position: absolute;
|
|
1741
|
-
right: 0;
|
|
1742
|
-
bottom: 0;
|
|
1743
|
-
left: 0;
|
|
1744
|
-
height: 1.5px;
|
|
1745
|
-
border-radius: 999px;
|
|
1746
|
-
background: color-mix(in srgb, var(--site-accent) 62%, var(--site-divider));
|
|
1747
|
-
opacity: 0;
|
|
1748
|
-
transform: scaleX(0.52);
|
|
1749
|
-
transform-origin: center;
|
|
1750
|
-
transition:
|
|
1751
|
-
opacity 0.18s ease,
|
|
1752
|
-
transform 0.18s ease;
|
|
1753
|
-
}
|
|
1754
|
-
|
|
1755
|
-
.site-header-link:hover {
|
|
1756
|
-
color: var(--site-text-primary);
|
|
1757
|
-
opacity: 1;
|
|
1758
|
-
}
|
|
1759
|
-
|
|
1760
|
-
.site-header-link:hover::after {
|
|
1761
|
-
opacity: 0.42;
|
|
1762
|
-
transform: scaleX(0.82);
|
|
1763
|
-
}
|
|
1764
|
-
|
|
1765
|
-
.site-container {
|
|
1766
|
-
max-width: var(--site-width);
|
|
1767
|
-
margin: 0 auto;
|
|
1768
|
-
}
|
|
1769
|
-
|
|
1770
|
-
.site-content {
|
|
1771
|
-
background-color: var(--site-elevated-bg);
|
|
1772
|
-
padding: 1rem var(--site-padding) var(--space-xl);
|
|
1773
|
-
}
|
|
1774
|
-
|
|
1775
|
-
.site-content-home {
|
|
1776
|
-
padding-top: 0.75rem;
|
|
1777
|
-
padding-bottom: calc(var(--space-xl) - 0.25rem);
|
|
1778
|
-
border-bottom: 0.5px solid
|
|
1779
|
-
color-mix(in srgb, var(--site-divider) 84%, transparent);
|
|
1780
|
-
}
|
|
1781
|
-
|
|
1782
|
-
.site-home-header {
|
|
1783
|
-
display: flex;
|
|
1784
|
-
flex-direction: column;
|
|
1785
|
-
gap: 0.15rem;
|
|
1786
|
-
margin-bottom: var(--space-xl);
|
|
1787
|
-
}
|
|
1788
|
-
|
|
1789
|
-
.site-browse-nav {
|
|
1790
|
-
display: flex;
|
|
1791
|
-
align-items: baseline;
|
|
1792
|
-
flex-wrap: wrap;
|
|
1793
|
-
gap: 0.55rem;
|
|
1794
|
-
padding: 14px 0 4px;
|
|
1795
|
-
}
|
|
1796
|
-
|
|
1797
|
-
.site-browse-link {
|
|
1798
|
-
font-size: var(--text-base);
|
|
1799
|
-
font-weight: var(--fw-regular);
|
|
1800
|
-
color: var(--site-text-primary);
|
|
1801
|
-
opacity: 0.42;
|
|
1802
|
-
}
|
|
1803
|
-
|
|
1804
|
-
.site-browse-link-active {
|
|
1805
|
-
opacity: 1;
|
|
1806
|
-
font-weight: var(--fw-medium);
|
|
1807
|
-
}
|
|
1808
|
-
|
|
1809
|
-
.page-context-label {
|
|
1810
|
-
margin: 0 0 1rem;
|
|
1811
|
-
color: var(--site-text-secondary);
|
|
1812
|
-
font-size: var(--text-sm);
|
|
1813
|
-
}
|
|
1814
|
-
|
|
1815
|
-
.feed-item {
|
|
1816
|
-
position: relative;
|
|
1817
|
-
}
|
|
1818
|
-
|
|
1819
|
-
.site-content hr.feed-divider {
|
|
1820
|
-
border: none;
|
|
1821
|
-
width: 30px;
|
|
1822
|
-
height: 9px;
|
|
1823
|
-
margin: 2.5rem auto;
|
|
1824
|
-
color: var(--site-feed-divider-color);
|
|
1825
|
-
background-color: currentColor;
|
|
1826
|
-
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 45 13'%3E%3Cpath fill='black' transform='translate(0,0) rotate(90 6 6.5)' d='M6.765.5.177 6.093l2.61 5.966 8.39-3.17L6.765.5Z'/%3E%3Cpath fill='black' transform='translate(16,0) rotate(100 6 6.5)' d='M6.765.5.177 6.093l2.61 5.966 8.39-3.17L6.765.5Z'/%3E%3Cpath fill='black' transform='translate(32,0) rotate(80 6 6.5)' d='M6.765.5.177 6.093l2.61 5.966 8.39-3.17L6.765.5Z'/%3E%3C/svg%3E");
|
|
1827
|
-
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 45 13'%3E%3Cpath fill='black' transform='translate(0,0) rotate(90 6 6.5)' d='M6.765.5.177 6.093l2.61 5.966 8.39-3.17L6.765.5Z'/%3E%3Cpath fill='black' transform='translate(16,0) rotate(100 6 6.5)' d='M6.765.5.177 6.093l2.61 5.966 8.39-3.17L6.765.5Z'/%3E%3Cpath fill='black' transform='translate(32,0) rotate(80 6 6.5)' d='M6.765.5.177 6.093l2.61 5.966 8.39-3.17L6.765.5Z'/%3E%3C/svg%3E");
|
|
1828
|
-
-webkit-mask-repeat: no-repeat;
|
|
1829
|
-
mask-repeat: no-repeat;
|
|
1830
|
-
-webkit-mask-position: center;
|
|
1831
|
-
mask-position: center;
|
|
1832
|
-
-webkit-mask-size: contain;
|
|
1833
|
-
mask-size: contain;
|
|
1834
|
-
}
|
|
1835
|
-
|
|
1836
|
-
.post-menu-target {
|
|
1837
|
-
position: relative;
|
|
1838
|
-
}
|
|
1839
|
-
|
|
1840
|
-
.post-card-shell,
|
|
1841
|
-
.post-detail-shell {
|
|
1842
|
-
position: relative;
|
|
1843
|
-
padding: 0.45rem 0 0.35rem;
|
|
1844
|
-
}
|
|
1845
|
-
|
|
1846
|
-
.feed-card {
|
|
1847
|
-
position: relative;
|
|
1848
|
-
padding: 1rem 1.1rem 0.95rem;
|
|
1849
|
-
border: 1px solid color-mix(in srgb, var(--site-divider) 78%, transparent);
|
|
1850
|
-
border-radius: 18px;
|
|
1851
|
-
background:
|
|
1852
|
-
linear-gradient(
|
|
1853
|
-
180deg,
|
|
1854
|
-
color-mix(in srgb, var(--site-accent) 4%, transparent),
|
|
1855
|
-
transparent 58%
|
|
1856
|
-
),
|
|
1857
|
-
color-mix(in srgb, var(--site-elevated-bg) 88%, var(--site-nav-hover-bg));
|
|
1858
|
-
box-shadow: 0 20px 40px -36px var(--site-feed-card-shadow);
|
|
1859
|
-
}
|
|
1860
|
-
|
|
1861
|
-
.feed-card-link {
|
|
1862
|
-
border-radius: 14px;
|
|
1863
|
-
}
|
|
1864
|
-
|
|
1865
|
-
.feed-link-post {
|
|
1866
|
-
--feed-link-post-footer-inset: 0.65rem;
|
|
1867
|
-
display: flex;
|
|
1868
|
-
flex-direction: column;
|
|
1869
|
-
}
|
|
1870
|
-
|
|
1871
|
-
.feed-link-post > .post-menu-footer {
|
|
1872
|
-
margin-top: 0.5rem;
|
|
1873
|
-
padding-inline: 0;
|
|
1874
|
-
padding-inline-start: var(--feed-link-post-footer-inset);
|
|
1875
|
-
padding-inline-end: 0.15rem;
|
|
1876
|
-
}
|
|
1877
|
-
|
|
1878
|
-
.feed-link-domain {
|
|
1879
|
-
display: inline-flex;
|
|
1880
|
-
align-items: center;
|
|
1881
|
-
gap: 0.25rem;
|
|
1882
|
-
max-width: 100%;
|
|
1883
|
-
margin-bottom: 0.65rem;
|
|
1884
|
-
color: var(--site-text-secondary);
|
|
1885
|
-
font-size: 0.75rem;
|
|
1886
|
-
font-weight: var(--type-label-weight);
|
|
1887
|
-
line-height: 1;
|
|
1888
|
-
letter-spacing: var(--type-label-tracking);
|
|
1889
|
-
text-decoration: none;
|
|
1890
|
-
}
|
|
1891
|
-
|
|
1892
|
-
.feed-link-domain-icon {
|
|
1893
|
-
width: 0.72rem;
|
|
1894
|
-
height: 0.72rem;
|
|
1895
|
-
flex-shrink: 0;
|
|
1896
|
-
}
|
|
1897
|
-
|
|
1898
|
-
.feed-link-title,
|
|
1899
|
-
.feed-note-title,
|
|
1900
|
-
.detail-title,
|
|
1901
|
-
.section-title,
|
|
1902
|
-
.p-name {
|
|
1903
|
-
font-family: var(--font-heading);
|
|
1904
|
-
}
|
|
1905
|
-
|
|
1906
|
-
.feed-link-title {
|
|
1907
|
-
margin: 0;
|
|
1908
|
-
font-size: 0.98rem;
|
|
1909
|
-
font-weight: var(--type-heading-weight);
|
|
1910
|
-
line-height: var(--type-heading-leading);
|
|
1911
|
-
letter-spacing: var(--type-heading-tracking);
|
|
1912
|
-
}
|
|
1913
|
-
|
|
1914
|
-
.feed-link-title-link,
|
|
1915
|
-
.post-title-link {
|
|
1916
|
-
color: inherit;
|
|
1917
|
-
text-decoration: none;
|
|
1918
|
-
}
|
|
1919
|
-
|
|
1920
|
-
.feed-link-title-link:hover,
|
|
1921
|
-
.post-title-link:hover {
|
|
1922
|
-
text-decoration: underline;
|
|
1923
|
-
}
|
|
1924
|
-
|
|
1925
|
-
.feed-note-title {
|
|
1926
|
-
margin: 0 0 0.48rem;
|
|
1927
|
-
font-size: var(--feed-note-title-size);
|
|
1928
|
-
line-height: var(--feed-note-title-leading);
|
|
1929
|
-
letter-spacing: var(--type-heading-tracking);
|
|
1930
|
-
text-wrap: pretty;
|
|
1931
|
-
}
|
|
1932
|
-
|
|
1933
|
-
.post-header-block {
|
|
1934
|
-
margin-bottom: 1rem;
|
|
1935
|
-
}
|
|
1936
|
-
|
|
1937
|
-
.post-header-block .feed-link-title,
|
|
1938
|
-
.post-header-block .feed-note-title,
|
|
1939
|
-
.post-header-block .detail-title {
|
|
1940
|
-
margin-bottom: 0;
|
|
1941
|
-
}
|
|
1942
|
-
|
|
1943
|
-
.post-header-block .post-rating {
|
|
1944
|
-
margin-top: 0.45rem;
|
|
1945
|
-
}
|
|
1946
|
-
|
|
1947
|
-
.detail-title {
|
|
1948
|
-
margin: 0 0 1rem;
|
|
1949
|
-
font-size: clamp(1.56rem, 1.34rem + 1vw, 2.02rem);
|
|
1950
|
-
font-weight: var(--fw-medium);
|
|
1951
|
-
line-height: 1.08;
|
|
1952
|
-
letter-spacing: -0.03em;
|
|
1953
|
-
text-wrap: balance;
|
|
1954
|
-
}
|
|
1955
|
-
|
|
1956
|
-
.feed-quote-post {
|
|
1957
|
-
position: relative;
|
|
1958
|
-
padding: 0.45rem 0 0.35rem;
|
|
1959
|
-
}
|
|
1960
|
-
|
|
1961
|
-
.feed-quote {
|
|
1962
|
-
margin: 0;
|
|
1963
|
-
}
|
|
1964
|
-
|
|
1965
|
-
.feed-quote-card {
|
|
1966
|
-
padding-left: 0;
|
|
1967
|
-
border-left: none;
|
|
1968
|
-
}
|
|
1969
|
-
|
|
1970
|
-
.decorative-quote-mark {
|
|
1971
|
-
display: block;
|
|
1972
|
-
line-height: 0;
|
|
1973
|
-
}
|
|
1974
|
-
|
|
1975
|
-
.decorative-quote-mark svg {
|
|
1976
|
-
display: block;
|
|
1977
|
-
width: 100%;
|
|
1978
|
-
height: auto;
|
|
1979
|
-
}
|
|
1980
|
-
|
|
1981
|
-
.feed-quote-mark {
|
|
1982
|
-
width: clamp(1.46rem, 1.38rem + 0.36vw, 1.76rem);
|
|
1983
|
-
margin-bottom: -0.1rem;
|
|
1984
|
-
margin-left: -0.04rem;
|
|
1985
|
-
color: color-mix(in srgb, var(--site-accent) 14%, var(--site-divider));
|
|
1986
|
-
opacity: 0.66;
|
|
1987
|
-
}
|
|
1988
|
-
|
|
1989
|
-
.feed-quote-content {
|
|
1990
|
-
font-family: var(--font-serif);
|
|
1991
|
-
color: var(--site-text-primary);
|
|
1992
|
-
font-size: clamp(1.34rem, 1.23rem + 0.44vw, 1.58rem);
|
|
1993
|
-
line-height: 1.36;
|
|
1994
|
-
letter-spacing: -0.02em;
|
|
1995
|
-
text-wrap: pretty;
|
|
1996
|
-
}
|
|
1997
|
-
|
|
1998
|
-
.feed-quote-attribution {
|
|
1999
|
-
display: flex;
|
|
2000
|
-
align-items: center;
|
|
2001
|
-
gap: 0.45rem;
|
|
2002
|
-
flex-wrap: wrap;
|
|
2003
|
-
margin-top: 0.95rem;
|
|
2004
|
-
color: var(--site-text-secondary);
|
|
2005
|
-
font-size: 0.75rem;
|
|
2006
|
-
letter-spacing: 0.08em;
|
|
2007
|
-
line-height: 1.4;
|
|
2008
|
-
}
|
|
2009
|
-
|
|
2010
|
-
.feed-quote-attribution::before {
|
|
2011
|
-
content: "";
|
|
2012
|
-
width: 0.9rem;
|
|
2013
|
-
height: 1px;
|
|
2014
|
-
background: color-mix(in srgb, var(--site-text-secondary) 38%, var(--site-divider));
|
|
2015
|
-
}
|
|
2016
|
-
|
|
2017
|
-
.feed-quote-source {
|
|
2018
|
-
color: inherit;
|
|
2019
|
-
text-decoration: underline;
|
|
2020
|
-
text-decoration-color: color-mix(in srgb, var(--site-text-secondary) 55%, transparent);
|
|
2021
|
-
}
|
|
2022
|
-
|
|
2023
|
-
.feed-quote-commentary {
|
|
2024
|
-
position: relative;
|
|
2025
|
-
max-width: 34rem;
|
|
2026
|
-
margin-top: 1.1rem;
|
|
2027
|
-
padding-top: 0.95rem;
|
|
2028
|
-
color: color-mix(in srgb, var(--site-text-secondary) 84%, var(--site-text-primary));
|
|
2029
|
-
}
|
|
2030
|
-
|
|
2031
|
-
.feed-quote-commentary::before {
|
|
2032
|
-
content: "";
|
|
2033
|
-
position: absolute;
|
|
2034
|
-
left: 0;
|
|
2035
|
-
right: 0;
|
|
2036
|
-
top: 0;
|
|
2037
|
-
height: 1px;
|
|
2038
|
-
background: linear-gradient(
|
|
2039
|
-
90deg,
|
|
2040
|
-
transparent 0%,
|
|
2041
|
-
color-mix(in srgb, var(--site-divider) 48%, transparent) 16%,
|
|
2042
|
-
color-mix(in srgb, var(--site-divider) 78%, transparent) 50%,
|
|
2043
|
-
color-mix(in srgb, var(--site-divider) 48%, transparent) 84%,
|
|
2044
|
-
transparent 100%
|
|
2045
|
-
);
|
|
2046
|
-
}
|
|
2047
|
-
|
|
2048
|
-
.post-status-badges {
|
|
2049
|
-
display: none;
|
|
2050
|
-
align-items: center;
|
|
2051
|
-
gap: 6px;
|
|
2052
|
-
margin-bottom: 4px;
|
|
2053
|
-
font-size: 11px;
|
|
2054
|
-
font-weight: 500;
|
|
2055
|
-
letter-spacing: 0.05em;
|
|
2056
|
-
text-transform: uppercase;
|
|
2057
|
-
color: var(--site-text-placeholder);
|
|
2058
|
-
}
|
|
2059
|
-
|
|
2060
|
-
article[data-post-pinned] .post-status-badges,
|
|
2061
|
-
article[data-post-visibility="private"] .post-status-badges {
|
|
2062
|
-
display: flex;
|
|
2063
|
-
}
|
|
2064
|
-
|
|
2065
|
-
.post-status-badge {
|
|
2066
|
-
display: none;
|
|
2067
|
-
align-items: center;
|
|
2068
|
-
gap: 4px;
|
|
2069
|
-
}
|
|
2070
|
-
|
|
2071
|
-
article[data-post-pinned] .post-status-pinned,
|
|
2072
|
-
article[data-post-visibility="private"] .post-status-private {
|
|
2073
|
-
display: inline-flex;
|
|
2074
|
-
}
|
|
2075
|
-
|
|
2076
|
-
.post-status-badge svg {
|
|
2077
|
-
width: 12px;
|
|
2078
|
-
height: 12px;
|
|
2079
|
-
}
|
|
2080
|
-
|
|
2081
|
-
.post-footer-featured {
|
|
2082
|
-
display: none;
|
|
2083
|
-
align-items: center;
|
|
2084
|
-
justify-content: center;
|
|
2085
|
-
color: color-mix(in srgb, var(--search-mark-color) 72%, var(--site-text-secondary));
|
|
2086
|
-
--icon-stroke: 1.35;
|
|
2087
|
-
flex-shrink: 0;
|
|
2088
|
-
}
|
|
2089
|
-
|
|
2090
|
-
article[data-post-featured] .post-footer-featured {
|
|
2091
|
-
display: inline-flex;
|
|
2092
|
-
}
|
|
2093
|
-
|
|
2094
|
-
.post-footer-featured svg {
|
|
2095
|
-
width: 1.12rem;
|
|
2096
|
-
height: 1.12rem;
|
|
2097
|
-
opacity: 0.88;
|
|
2098
|
-
}
|
|
2099
|
-
|
|
2100
|
-
[data-page="featured"] article[data-post-featured] .post-footer-featured {
|
|
2101
|
-
display: none;
|
|
2102
|
-
}
|
|
2103
|
-
|
|
2104
|
-
.sr-only {
|
|
2105
|
-
position: absolute;
|
|
2106
|
-
width: 1px;
|
|
2107
|
-
height: 1px;
|
|
2108
|
-
padding: 0;
|
|
2109
|
-
margin: -1px;
|
|
2110
|
-
overflow: hidden;
|
|
2111
|
-
clip: rect(0, 0, 0, 0);
|
|
2112
|
-
white-space: nowrap;
|
|
2113
|
-
border: 0;
|
|
2114
|
-
}
|
|
2115
|
-
|
|
2116
|
-
.prose {
|
|
2117
|
-
max-width: 35rem;
|
|
2118
|
-
font-size: var(--type-body-size);
|
|
2119
|
-
line-height: var(--type-body-leading);
|
|
2120
|
-
letter-spacing: var(--type-body-tracking);
|
|
2121
|
-
color: var(--site-text-primary);
|
|
2122
|
-
}
|
|
2123
|
-
|
|
2124
|
-
.post-body-summary {
|
|
2125
|
-
color: color-mix(in srgb, var(--site-text-secondary) 88%, var(--site-text-primary));
|
|
2126
|
-
}
|
|
2127
|
-
|
|
2128
|
-
.prose > :first-child {
|
|
2129
|
-
margin-top: 0;
|
|
2130
|
-
}
|
|
2131
|
-
|
|
2132
|
-
.prose > :last-child {
|
|
2133
|
-
margin-bottom: 0;
|
|
2134
|
-
}
|
|
2135
|
-
|
|
2136
|
-
.prose p {
|
|
2137
|
-
margin: 0;
|
|
2138
|
-
}
|
|
2139
|
-
|
|
2140
|
-
.prose p + p,
|
|
2141
|
-
.prose ul,
|
|
2142
|
-
.prose ol,
|
|
2143
|
-
.prose blockquote,
|
|
2144
|
-
.prose pre,
|
|
2145
|
-
.prose table,
|
|
2146
|
-
.prose figure {
|
|
2147
|
-
margin-top: 1.05rem;
|
|
2148
|
-
}
|
|
2149
|
-
|
|
2150
|
-
.prose :is(h1, h2, h3, h4) {
|
|
2151
|
-
margin: 1.25rem 0 0.35rem;
|
|
2152
|
-
font-family: var(--font-heading);
|
|
2153
|
-
font-weight: var(--type-heading-weight);
|
|
2154
|
-
line-height: var(--type-heading-leading);
|
|
2155
|
-
letter-spacing: var(--type-heading-tracking);
|
|
2156
|
-
}
|
|
2157
|
-
|
|
2158
|
-
.prose ul,
|
|
2159
|
-
.prose ol {
|
|
2160
|
-
padding-left: 1.3rem;
|
|
2161
|
-
}
|
|
2162
|
-
|
|
2163
|
-
.prose li {
|
|
2164
|
-
margin: 0.2rem 0;
|
|
2165
|
-
}
|
|
2166
|
-
|
|
2167
|
-
.prose blockquote {
|
|
2168
|
-
padding-left: 0.95rem;
|
|
2169
|
-
border-left: 2px solid color-mix(in srgb, var(--site-divider) 75%, transparent);
|
|
2170
|
-
color: var(--site-text-secondary);
|
|
2171
|
-
}
|
|
2172
|
-
|
|
2173
|
-
.prose code {
|
|
2174
|
-
font-family: var(--font-mono);
|
|
2175
|
-
font-size: 0.875em;
|
|
2176
|
-
background: color-mix(in srgb, var(--site-nav-hover-bg) 80%, transparent);
|
|
2177
|
-
padding: 0.1rem 0.35rem;
|
|
2178
|
-
border-radius: 0.32rem;
|
|
2179
|
-
}
|
|
2180
|
-
|
|
2181
|
-
.prose pre {
|
|
2182
|
-
overflow-x: auto;
|
|
2183
|
-
padding: 0.9rem 1rem;
|
|
2184
|
-
border-radius: 14px;
|
|
2185
|
-
border: 1px solid color-mix(in srgb, var(--site-divider) 82%, transparent);
|
|
2186
|
-
background: color-mix(in srgb, var(--site-nav-hover-bg) 78%, transparent);
|
|
2187
|
-
}
|
|
2188
|
-
|
|
2189
|
-
.prose pre code {
|
|
2190
|
-
background: transparent;
|
|
2191
|
-
padding: 0;
|
|
2192
|
-
}
|
|
2193
|
-
|
|
2194
|
-
.prose table {
|
|
2195
|
-
width: 100%;
|
|
2196
|
-
border-collapse: collapse;
|
|
2197
|
-
font-size: var(--text-sm);
|
|
2198
|
-
}
|
|
2199
|
-
|
|
2200
|
-
.prose th,
|
|
2201
|
-
.prose td {
|
|
2202
|
-
border: 1px solid color-mix(in srgb, var(--site-divider) 86%, transparent);
|
|
2203
|
-
padding: 0.45rem 0.7rem;
|
|
2204
|
-
text-align: left;
|
|
2205
|
-
}
|
|
2206
|
-
|
|
2207
|
-
.prose th {
|
|
2208
|
-
background: color-mix(in srgb, var(--site-nav-hover-bg) 74%, transparent);
|
|
2209
|
-
}
|
|
2210
|
-
|
|
2211
|
-
.prose img,
|
|
2212
|
-
.prose video {
|
|
2213
|
-
width: 100%;
|
|
2214
|
-
border-radius: var(--media-radius);
|
|
2215
|
-
}
|
|
2216
|
-
|
|
2217
|
-
.prose figure[data-jant-node="image"] {
|
|
2218
|
-
margin-inline: 0;
|
|
2219
|
-
}
|
|
2220
|
-
|
|
2221
|
-
.prose figcaption {
|
|
2222
|
-
margin-top: 0.55rem;
|
|
2223
|
-
color: var(--site-text-secondary);
|
|
2224
|
-
font-size: var(--text-sm);
|
|
2225
|
-
}
|
|
2226
|
-
|
|
2227
|
-
[data-jant-node="attachments"] {
|
|
2228
|
-
display: grid;
|
|
2229
|
-
gap: 0.85rem;
|
|
2230
|
-
margin-top: 1rem;
|
|
2231
|
-
}
|
|
2232
|
-
|
|
2233
|
-
[data-jant-node="attachment"] {
|
|
2234
|
-
margin: 0;
|
|
2235
|
-
padding: 0.95rem;
|
|
2236
|
-
border: 1px solid color-mix(in srgb, var(--site-divider) 84%, transparent);
|
|
2237
|
-
border-radius: 16px;
|
|
2238
|
-
background: color-mix(in srgb, var(--site-nav-hover-bg) 66%, transparent);
|
|
2239
|
-
}
|
|
2240
|
-
|
|
2241
|
-
[data-jant-node="attachment"] > script[data-jant-meta] {
|
|
2242
|
-
display: none;
|
|
2243
|
-
}
|
|
2244
|
-
|
|
2245
|
-
[data-jant-node="attachment"][data-jant-kind="image"] {
|
|
2246
|
-
padding: 0;
|
|
2247
|
-
overflow: hidden;
|
|
2248
|
-
background: transparent;
|
|
2249
|
-
}
|
|
2250
|
-
|
|
2251
|
-
[data-jant-node="attachment"] audio,
|
|
2252
|
-
[data-jant-node="attachment"] video {
|
|
2253
|
-
width: 100%;
|
|
2254
|
-
}
|
|
2255
|
-
|
|
2256
|
-
[data-jant-node="attachment"] > a {
|
|
2257
|
-
display: inline-flex;
|
|
2258
|
-
font-weight: var(--fw-medium);
|
|
2259
|
-
text-decoration: none;
|
|
2260
|
-
}
|
|
2261
|
-
|
|
2262
|
-
[data-jant-node="attachment"][data-jant-kind="text"] details {
|
|
2263
|
-
width: 100%;
|
|
2264
|
-
}
|
|
2265
|
-
|
|
2266
|
-
[data-jant-node="attachment"][data-jant-kind="text"] summary {
|
|
2267
|
-
cursor: pointer;
|
|
2268
|
-
font-weight: var(--fw-medium);
|
|
2269
|
-
}
|
|
2270
|
-
|
|
2271
|
-
.jant-attachment-text-preview {
|
|
2272
|
-
margin-top: 0.85rem;
|
|
2273
|
-
}
|
|
2274
|
-
|
|
2275
|
-
.jant-attachment-text-preview > :first-child {
|
|
2276
|
-
margin-top: 0;
|
|
2277
|
-
}
|
|
2278
|
-
|
|
2279
|
-
.jant-attachment-text-preview > :last-child {
|
|
2280
|
-
margin-bottom: 0;
|
|
2281
|
-
}
|
|
2282
|
-
|
|
2283
|
-
.post-rating {
|
|
2284
|
-
display: flex;
|
|
2285
|
-
gap: 1px;
|
|
2286
|
-
margin-top: 8px;
|
|
2287
|
-
font-size: 14px;
|
|
2288
|
-
line-height: 1;
|
|
2289
|
-
}
|
|
2290
|
-
|
|
2291
|
-
.post-star-filled {
|
|
2292
|
-
color: oklch(0.75 0.15 70);
|
|
2293
|
-
}
|
|
2294
|
-
|
|
2295
|
-
.post-star-empty {
|
|
2296
|
-
color: var(--site-divider);
|
|
2297
|
-
}
|
|
2298
|
-
|
|
2299
|
-
.post-menu-footer {
|
|
2300
|
-
display: flex;
|
|
2301
|
-
justify-content: flex-start;
|
|
2302
|
-
align-items: center;
|
|
2303
|
-
margin-top: 0.9rem;
|
|
2304
|
-
}
|
|
2305
|
-
|
|
2306
|
-
.post-footer-detail {
|
|
2307
|
-
margin-top: 1.35rem;
|
|
2308
|
-
padding-top: 1rem;
|
|
2309
|
-
border-top: 1px solid color-mix(in srgb, var(--site-divider) 86%, transparent);
|
|
2310
|
-
}
|
|
2311
|
-
|
|
2312
|
-
.post-footer-meta {
|
|
2313
|
-
display: flex;
|
|
2314
|
-
align-items: center;
|
|
2315
|
-
gap: 5px;
|
|
2316
|
-
flex-wrap: wrap;
|
|
2317
|
-
line-height: 1.35;
|
|
2318
|
-
min-width: 0;
|
|
2319
|
-
}
|
|
2320
|
-
|
|
2321
|
-
.post-date-link {
|
|
2322
|
-
color: var(--site-text-secondary);
|
|
2323
|
-
text-decoration: none;
|
|
2324
|
-
font-size: 13px;
|
|
2325
|
-
white-space: nowrap;
|
|
2326
|
-
flex-shrink: 0;
|
|
2327
|
-
}
|
|
2328
|
-
|
|
2329
|
-
.post-date-link:hover {
|
|
2330
|
-
color: var(--site-text-primary);
|
|
2331
|
-
text-decoration: underline;
|
|
2332
|
-
}
|
|
2333
|
-
|
|
2334
|
-
.post-footer-detail .post-date-link {
|
|
2335
|
-
font-size: inherit;
|
|
2336
|
-
}
|
|
2337
|
-
|
|
2338
|
-
.post-footer-external-link {
|
|
2339
|
-
display: inline-flex;
|
|
2340
|
-
align-items: center;
|
|
2341
|
-
justify-content: center;
|
|
2342
|
-
width: 0.9rem;
|
|
2343
|
-
height: 0.9rem;
|
|
2344
|
-
color: var(--site-text-secondary);
|
|
2345
|
-
flex-shrink: 0;
|
|
2346
|
-
}
|
|
2347
|
-
|
|
2348
|
-
.post-footer-external-link svg {
|
|
2349
|
-
width: 0.82rem;
|
|
2350
|
-
height: 0.82rem;
|
|
2351
|
-
}
|
|
2352
|
-
|
|
2353
|
-
.post-collection-tags {
|
|
2354
|
-
display: inline-flex;
|
|
2355
|
-
align-items: center;
|
|
2356
|
-
gap: 4px;
|
|
2357
|
-
font-size: 13px;
|
|
2358
|
-
min-width: 0;
|
|
2359
|
-
max-width: 100%;
|
|
2360
|
-
flex: 1 1 auto;
|
|
2361
|
-
}
|
|
2362
|
-
|
|
2363
|
-
.post-collection-sep {
|
|
2364
|
-
color: var(--site-text-secondary);
|
|
2365
|
-
}
|
|
2366
|
-
|
|
2367
|
-
.post-collection-tag {
|
|
2368
|
-
display: inline-block;
|
|
2369
|
-
color: var(--site-text-secondary);
|
|
2370
|
-
text-decoration: none;
|
|
2371
|
-
min-width: 0;
|
|
2372
|
-
max-width: min(100%, 22ch);
|
|
2373
|
-
overflow: hidden;
|
|
2374
|
-
text-overflow: ellipsis;
|
|
2375
|
-
white-space: nowrap;
|
|
2376
|
-
}
|
|
2377
|
-
|
|
2378
|
-
.post-collection-tag:hover {
|
|
2379
|
-
color: var(--site-text-primary);
|
|
2380
|
-
text-decoration: underline;
|
|
2381
|
-
}
|
|
2382
|
-
|
|
2383
|
-
.post-collection-more-wrap {
|
|
2384
|
-
position: relative;
|
|
2385
|
-
display: inline-flex;
|
|
2386
|
-
align-items: center;
|
|
2387
|
-
flex-shrink: 0;
|
|
2388
|
-
}
|
|
2389
|
-
|
|
2390
|
-
.post-collection-more-wrap::after {
|
|
2391
|
-
content: "";
|
|
2392
|
-
position: absolute;
|
|
2393
|
-
top: 100%;
|
|
2394
|
-
left: -6px;
|
|
2395
|
-
right: -6px;
|
|
2396
|
-
height: 10px;
|
|
2397
|
-
}
|
|
2398
|
-
|
|
2399
|
-
.post-collection-more {
|
|
2400
|
-
display: inline-flex;
|
|
2401
|
-
align-items: center;
|
|
2402
|
-
background: none;
|
|
2403
|
-
border: none;
|
|
2404
|
-
padding: 0;
|
|
2405
|
-
font: inherit;
|
|
2406
|
-
font-size: inherit;
|
|
2407
|
-
color: var(--site-text-secondary);
|
|
2408
|
-
text-decoration: underline dotted;
|
|
2409
|
-
text-underline-offset: 2px;
|
|
2410
|
-
cursor: pointer;
|
|
2411
|
-
}
|
|
2412
|
-
|
|
2413
|
-
.post-collection-more-wrap:hover .post-collection-more,
|
|
2414
|
-
.post-collection-more-wrap:focus-within .post-collection-more {
|
|
2415
|
-
color: var(--site-text-primary);
|
|
2416
|
-
}
|
|
2417
|
-
|
|
2418
|
-
.post-collection-popover {
|
|
2419
|
-
display: none;
|
|
2420
|
-
position: absolute;
|
|
2421
|
-
top: calc(100% + 4px);
|
|
2422
|
-
left: 0;
|
|
2423
|
-
z-index: 50;
|
|
2424
|
-
flex-direction: column;
|
|
2425
|
-
min-width: 160px;
|
|
2426
|
-
padding: 4px;
|
|
2427
|
-
border-radius: 6px;
|
|
2428
|
-
background: var(--site-elevated-bg);
|
|
2429
|
-
border: 1px solid var(--site-divider);
|
|
2430
|
-
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
|
2431
|
-
}
|
|
2432
|
-
|
|
2433
|
-
.post-collection-more-wrap:hover .post-collection-popover,
|
|
2434
|
-
.post-collection-more-wrap:focus-within .post-collection-popover {
|
|
2435
|
-
display: flex;
|
|
2436
|
-
}
|
|
2437
|
-
|
|
2438
|
-
.post-collection-popover-item {
|
|
2439
|
-
display: flex;
|
|
2440
|
-
align-items: center;
|
|
2441
|
-
gap: 6px;
|
|
2442
|
-
padding: 6px 8px;
|
|
2443
|
-
border-radius: 4px;
|
|
2444
|
-
font-size: 12px;
|
|
2445
|
-
color: var(--site-text-secondary);
|
|
2446
|
-
text-decoration: none;
|
|
2447
|
-
}
|
|
2448
|
-
|
|
2449
|
-
.post-collection-popover-item:hover {
|
|
2450
|
-
background: var(--site-nav-hover-bg);
|
|
2451
|
-
color: var(--site-text-primary);
|
|
2452
|
-
}
|
|
2453
|
-
|
|
2454
|
-
.section-shell {
|
|
2455
|
-
display: flex;
|
|
2456
|
-
flex-direction: column;
|
|
2457
|
-
gap: 1.15rem;
|
|
2458
|
-
}
|
|
2459
|
-
|
|
2460
|
-
.section-header {
|
|
2461
|
-
display: flex;
|
|
2462
|
-
flex-direction: column;
|
|
2463
|
-
gap: 0.45rem;
|
|
2464
|
-
}
|
|
2465
|
-
|
|
2466
|
-
.section-title {
|
|
2467
|
-
margin: 0;
|
|
2468
|
-
font-size: clamp(1.45rem, 1.3rem + 0.5vw, 1.85rem);
|
|
2469
|
-
font-weight: var(--fw-medium);
|
|
2470
|
-
line-height: 1.12;
|
|
2471
|
-
letter-spacing: -0.03em;
|
|
2472
|
-
}
|
|
2473
|
-
|
|
2474
|
-
.section-description {
|
|
2475
|
-
margin: 0;
|
|
2476
|
-
max-width: 38rem;
|
|
2477
|
-
color: var(--site-text-secondary);
|
|
2478
|
-
}
|
|
2479
|
-
|
|
2480
|
-
.collection-list {
|
|
2481
|
-
margin: 0;
|
|
2482
|
-
padding: 0;
|
|
2483
|
-
list-style: none;
|
|
2484
|
-
counter-reset: collection-list;
|
|
2485
|
-
}
|
|
2486
|
-
|
|
2487
|
-
.collection-list-item {
|
|
2488
|
-
counter-increment: collection-list;
|
|
2489
|
-
border-top: 1px solid color-mix(in srgb, var(--site-divider) 84%, transparent);
|
|
2490
|
-
}
|
|
2491
|
-
|
|
2492
|
-
.collection-list-link {
|
|
2493
|
-
display: grid;
|
|
2494
|
-
grid-template-columns: 3.5ch minmax(0, 1fr);
|
|
2495
|
-
gap: 0.8rem;
|
|
2496
|
-
align-items: start;
|
|
2497
|
-
padding: 0.95rem 0;
|
|
2498
|
-
text-decoration: none;
|
|
2499
|
-
}
|
|
2500
|
-
|
|
2501
|
-
.collection-list-link:hover .collection-list-title {
|
|
2502
|
-
text-decoration: underline;
|
|
2503
|
-
}
|
|
2504
|
-
|
|
2505
|
-
.collection-list-link:hover .collection-list-sequence {
|
|
2506
|
-
color: var(--site-text-primary);
|
|
2507
|
-
}
|
|
2508
|
-
|
|
2509
|
-
.collection-list-sequence {
|
|
2510
|
-
display: block;
|
|
2511
|
-
width: 3.5ch;
|
|
2512
|
-
padding-top: 0.2rem;
|
|
2513
|
-
font-family: var(--font-mono);
|
|
2514
|
-
font-size: 0.68rem;
|
|
2515
|
-
font-variant-numeric: tabular-nums;
|
|
2516
|
-
line-height: 1.18;
|
|
2517
|
-
letter-spacing: 0.14em;
|
|
2518
|
-
color: var(--site-text-secondary);
|
|
2519
|
-
transition: color 0.15s ease;
|
|
2520
|
-
}
|
|
2521
|
-
|
|
2522
|
-
.collection-list-sequence::before {
|
|
2523
|
-
content: counter(collection-list, decimal-leading-zero);
|
|
2524
|
-
}
|
|
2525
|
-
|
|
2526
|
-
.collection-list-content {
|
|
2527
|
-
min-width: 0;
|
|
2528
|
-
display: flex;
|
|
2529
|
-
flex-direction: column;
|
|
2530
|
-
gap: 0.28rem;
|
|
2531
|
-
}
|
|
2532
|
-
|
|
2533
|
-
.collection-list-title {
|
|
2534
|
-
min-width: 0;
|
|
2535
|
-
display: inline-flex;
|
|
2536
|
-
align-items: center;
|
|
2537
|
-
gap: 0.45rem;
|
|
2538
|
-
font-family: var(--font-heading);
|
|
2539
|
-
font-size: clamp(1rem, 1.5vw, 1.12rem);
|
|
2540
|
-
font-weight: var(--fw-medium);
|
|
2541
|
-
line-height: 1.18;
|
|
2542
|
-
letter-spacing: -0.01em;
|
|
2543
|
-
}
|
|
2544
|
-
|
|
2545
|
-
.collection-list-meta {
|
|
2546
|
-
display: flex;
|
|
2547
|
-
flex-wrap: wrap;
|
|
2548
|
-
gap: 0.5rem 1rem;
|
|
2549
|
-
color: var(--site-text-secondary);
|
|
2550
|
-
font-size: var(--text-sm);
|
|
2551
|
-
}
|
|
2552
|
-
|
|
2553
|
-
.archive-shell {
|
|
2554
|
-
gap: 1.35rem;
|
|
2555
|
-
}
|
|
2556
|
-
|
|
2557
|
-
.archive-list {
|
|
2558
|
-
display: grid;
|
|
2559
|
-
gap: 0;
|
|
2560
|
-
}
|
|
2561
|
-
|
|
2562
|
-
.archive-month-group + .archive-month-group {
|
|
2563
|
-
margin-top: 1.35rem;
|
|
2564
|
-
}
|
|
2565
|
-
|
|
2566
|
-
.archive-month-heading {
|
|
2567
|
-
margin: 0 0 0.45rem;
|
|
2568
|
-
color: var(--site-text-secondary);
|
|
2569
|
-
font-size: 0.82rem;
|
|
2570
|
-
font-weight: var(--fw-medium);
|
|
2571
|
-
letter-spacing: 0.08em;
|
|
2572
|
-
text-transform: uppercase;
|
|
2573
|
-
}
|
|
2574
|
-
|
|
2575
|
-
.archive-entry {
|
|
2576
|
-
display: grid;
|
|
2577
|
-
grid-template-columns: minmax(6.75rem, auto) minmax(0, 1fr);
|
|
2578
|
-
gap: 0.9rem 1.25rem;
|
|
2579
|
-
align-items: start;
|
|
2580
|
-
padding: 0.9rem 0;
|
|
2581
|
-
border-top: 1px solid color-mix(in srgb, var(--site-divider) 84%, transparent);
|
|
2582
|
-
}
|
|
2583
|
-
|
|
2584
|
-
.archive-entry:first-child {
|
|
2585
|
-
border-top: none;
|
|
2586
|
-
padding-top: 0;
|
|
2587
|
-
}
|
|
2588
|
-
|
|
2589
|
-
.archive-entry-date {
|
|
2590
|
-
color: var(--site-text-secondary);
|
|
2591
|
-
font-size: var(--text-sm);
|
|
2592
|
-
line-height: 1.5;
|
|
2593
|
-
text-decoration: none;
|
|
2594
|
-
}
|
|
2595
|
-
|
|
2596
|
-
.archive-entry-main {
|
|
2597
|
-
min-width: 0;
|
|
2598
|
-
}
|
|
2599
|
-
|
|
2600
|
-
.archive-entry-title {
|
|
2601
|
-
display: inline-block;
|
|
2602
|
-
color: var(--site-text-primary);
|
|
2603
|
-
font-family: var(--font-heading);
|
|
2604
|
-
font-size: 1.03rem;
|
|
2605
|
-
font-weight: var(--type-heading-weight);
|
|
2606
|
-
line-height: 1.34;
|
|
2607
|
-
letter-spacing: var(--type-heading-tracking);
|
|
2608
|
-
text-decoration: none;
|
|
2609
|
-
text-wrap: pretty;
|
|
2610
|
-
}
|
|
2611
|
-
|
|
2612
|
-
.archive-entry-title:hover {
|
|
2613
|
-
text-decoration: underline;
|
|
2614
|
-
}
|
|
2615
|
-
|
|
2616
|
-
.archive-entry-meta {
|
|
2617
|
-
display: flex;
|
|
2618
|
-
align-items: center;
|
|
2619
|
-
gap: 0.45rem;
|
|
2620
|
-
flex-wrap: wrap;
|
|
2621
|
-
margin-top: 0.38rem;
|
|
2622
|
-
color: var(--site-text-secondary);
|
|
2623
|
-
font-size: 0.72rem;
|
|
2624
|
-
line-height: 1.45;
|
|
2625
|
-
letter-spacing: 0.06em;
|
|
2626
|
-
text-transform: uppercase;
|
|
2627
|
-
}
|
|
2628
|
-
|
|
2629
|
-
.archive-entry-format {
|
|
2630
|
-
opacity: 0.7;
|
|
2631
|
-
}
|
|
2632
|
-
|
|
2633
|
-
.archive-entry-tag {
|
|
2634
|
-
color: inherit;
|
|
2635
|
-
text-decoration: none;
|
|
2636
|
-
border-bottom: 0.5px solid color-mix(in srgb, var(--site-divider) 82%, transparent);
|
|
2637
|
-
}
|
|
2638
|
-
|
|
2639
|
-
.archive-entry-tag:hover {
|
|
2640
|
-
color: var(--site-text-primary);
|
|
2641
|
-
border-bottom-color: currentColor;
|
|
2642
|
-
}
|
|
2643
|
-
|
|
2644
|
-
.pagination {
|
|
2645
|
-
display: grid;
|
|
2646
|
-
grid-template-columns: 1fr auto 1fr;
|
|
2647
|
-
align-items: center;
|
|
2648
|
-
gap: 1rem;
|
|
2649
|
-
padding: 2rem 0 0.5rem;
|
|
2650
|
-
}
|
|
2651
|
-
|
|
2652
|
-
.pagination-side-end {
|
|
2653
|
-
text-align: right;
|
|
2654
|
-
}
|
|
2655
|
-
|
|
2656
|
-
.pagination-center {
|
|
2657
|
-
text-align: center;
|
|
2658
|
-
}
|
|
2659
|
-
|
|
2660
|
-
.pagination-link {
|
|
2661
|
-
color: var(--site-text-secondary);
|
|
2662
|
-
text-decoration: underline;
|
|
2663
|
-
}
|
|
2664
|
-
|
|
2665
|
-
.pagination-link:hover {
|
|
2666
|
-
color: var(--site-text-primary);
|
|
2667
|
-
}
|
|
2668
|
-
|
|
2669
|
-
.pagination-label {
|
|
2670
|
-
color: var(--site-text-secondary);
|
|
2671
|
-
font-size: var(--text-sm);
|
|
2672
|
-
}
|
|
2673
|
-
|
|
2674
|
-
.site-footer {
|
|
2675
|
-
max-width: var(--site-width);
|
|
2676
|
-
margin: var(--space-xl) auto 0;
|
|
2677
|
-
padding: 0 var(--site-padding) var(--space-xl);
|
|
2678
|
-
color: var(--site-text-secondary);
|
|
2679
|
-
font-size: var(--text-sm);
|
|
2680
|
-
}
|
|
2681
|
-
|
|
2682
|
-
.site-footer > .site-container {
|
|
2683
|
-
border-top: 0.5px solid var(--site-divider);
|
|
2684
|
-
padding-top: var(--content-gap);
|
|
2685
|
-
}
|
|
2686
|
-
|
|
2687
|
-
.home-branding-credit {
|
|
2688
|
-
margin-top: var(--space-xl);
|
|
2689
|
-
text-align: center;
|
|
2690
|
-
color: var(--site-text-secondary);
|
|
2691
|
-
font-size: var(--text-sm);
|
|
2692
|
-
}
|
|
2693
|
-
|
|
2694
|
-
.home-branding-credit a {
|
|
2695
|
-
display: inline-flex;
|
|
2696
|
-
align-items: center;
|
|
2697
|
-
gap: 0.38rem;
|
|
2698
|
-
color: inherit;
|
|
2699
|
-
text-decoration: none;
|
|
2700
|
-
border-bottom: 0.5px solid
|
|
2701
|
-
color-mix(in srgb, var(--site-text-secondary) 45%, transparent);
|
|
2702
|
-
}
|
|
2703
|
-
|
|
2704
|
-
.home-branding-credit a svg {
|
|
2705
|
-
width: 1rem;
|
|
2706
|
-
height: 1rem;
|
|
2707
|
-
flex: none;
|
|
2708
|
-
}
|
|
2709
|
-
|
|
2710
|
-
@media (min-width: 700px) {
|
|
2711
|
-
.site-header {
|
|
2712
|
-
padding-top: 30px;
|
|
2713
|
-
}
|
|
2714
|
-
|
|
2715
|
-
.site-header-top-bordered {
|
|
2716
|
-
padding-bottom: 18px;
|
|
2717
|
-
}
|
|
2718
|
-
}
|
|
2719
|
-
|
|
2720
|
-
@media (max-width: 699px) {
|
|
2721
|
-
:root {
|
|
2722
|
-
--site-padding: 1.875rem;
|
|
2723
|
-
}
|
|
2724
|
-
|
|
2725
|
-
.site-header-right {
|
|
2726
|
-
width: 100%;
|
|
2727
|
-
justify-content: flex-start;
|
|
2728
|
-
}
|
|
2729
|
-
|
|
2730
|
-
.site-header-nav {
|
|
2731
|
-
justify-content: flex-start;
|
|
2732
|
-
}
|
|
2733
|
-
}
|
|
2734
|
-
|
|
2735
|
-
@media (max-width: 640px) {
|
|
2736
|
-
.archive-entry {
|
|
2737
|
-
grid-template-columns: 1fr;
|
|
2738
|
-
gap: 0.3rem;
|
|
2739
|
-
}
|
|
2740
|
-
|
|
2741
|
-
.pagination {
|
|
2742
|
-
grid-template-columns: 1fr;
|
|
2743
|
-
gap: 0.55rem;
|
|
2744
|
-
}
|
|
2745
|
-
|
|
2746
|
-
.pagination-side-end,
|
|
2747
|
-
.pagination-center {
|
|
2748
|
-
text-align: left;
|
|
2749
|
-
}
|
|
2750
|
-
}
|
|
2751
|
-
`;
|
|
1703
|
+
export {
|
|
1704
|
+
buildSiteIconAssets,
|
|
1705
|
+
buildExportedCollectionMetrics,
|
|
1706
|
+
buildExportedCollectionDirectoryItems,
|
|
1707
|
+
readStorageObjectBytes,
|
|
1708
|
+
getArchiveSummaryText,
|
|
1709
|
+
getMediaUrl,
|
|
1710
|
+
getPublicUrlForProvider,
|
|
1711
|
+
};
|