@jant/core 0.3.39 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -33
- 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.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/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 +698 -0
- package/bin/lib/site-snapshot.js +0 -1
- package/dist/app-B5db9mS0.js +5 -0
- package/dist/{app-BfoG98VD.js → app-CwCepTko.js} +23938 -23014
- 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-BRFl5zQA.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-wCpMcNXs.js +252 -0
- package/dist/github-api-CficQztC.js +176 -0
- package/dist/github-app-F4qZ05xk.js +275 -0
- package/dist/github-sync-zohnA9qv.js +4696 -0
- package/dist/index.js +88 -2
- package/dist/node.js +71 -30
- package/dist/url-FvvgARU9.js +305 -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/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 +65 -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} +145 -8
- 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 -15
- 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__/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 +77 -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/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/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/_journal.json +92 -1
- 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/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/_journal.json +92 -1
- package/src/db/pg/__tests__/node.test.ts +38 -0
- package/src/db/pg/node.ts +149 -0
- package/src/db/pg/schema.ts +130 -12
- package/src/db/rehearsal-fixtures/demo-current.sql +47 -3
- package/src/db/schema.ts +139 -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} +298 -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 -5
- package/src/lib/__tests__/collection-groups.test.ts +80 -0
- package/src/lib/__tests__/constants.test.ts +1 -1
- 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 +11 -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 +148 -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__/resolve-config.test.ts +30 -25
- package/src/lib/__tests__/schemas.test.ts +35 -0
- package/src/lib/__tests__/slug-format.test.ts +8 -0
- package/src/lib/__tests__/summary.test.ts +400 -1
- package/src/lib/__tests__/theme.test.ts +41 -2
- package/src/lib/__tests__/timeline.test.ts +114 -26
- 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 +115 -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 +70 -0
- package/src/lib/collection-paths.ts +40 -0
- package/src/lib/constants.ts +3 -4
- 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 +89 -66
- 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-trigger.ts +199 -0
- package/src/lib/github-sync-worker.ts +72 -0
- package/src/lib/hosted-signin.ts +1 -1
- 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 +40 -8
- package/src/lib/post-display.ts +10 -22
- package/src/lib/public-storage.ts +6 -1
- package/src/lib/render.tsx +5 -2
- package/src/lib/resolve-config.ts +23 -11
- package/src/lib/rich-image.ts +89 -0
- package/src/lib/schemas.ts +186 -12
- package/src/lib/slug-format.ts +8 -0
- package/src/lib/summary.ts +125 -18
- package/src/lib/theme.ts +119 -0
- package/src/lib/timeline.ts +91 -63
- 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/version.ts +18 -0
- package/src/lib/video-playback.ts +73 -0
- package/src/lib/view.ts +130 -90
- 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__/onboarding.test.ts +3 -3
- package/src/middleware/__tests__/secure-headers.test.ts +28 -1
- package/src/middleware/config.ts +7 -10
- package/src/middleware/cors.ts +49 -0
- package/src/middleware/secure-headers.ts +45 -55
- 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 +3 -1
- 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__/settings.test.ts +99 -3
- package/src/routes/api/__tests__/upload-multipart.test.ts +10 -2
- 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 +445 -0
- package/src/routes/api/internal/__tests__/sites.test.ts +45 -2
- 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 +1 -47
- 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/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 +34 -24
- 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 +1163 -70
- package/src/routes/feed/__tests__/{rss.test.ts → feed.test.ts} +62 -123
- package/src/routes/feed/feed.ts +386 -0
- package/src/routes/feed/manifest.ts +67 -0
- package/src/routes/feed/sitemap.ts +11 -2
- 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/archive.tsx +523 -127
- package/src/routes/pages/collection.tsx +265 -59
- package/src/routes/pages/collections.tsx +5 -1
- package/src/routes/pages/home.tsx +2 -2
- package/src/routes/pages/latest.tsx +3 -3
- package/src/routes/pages/page.tsx +221 -3
- package/src/routes/pages/partials.tsx +5 -0
- 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/__tests__/readiness.test.ts +89 -0
- package/src/runtime/readiness.ts +129 -0
- package/src/runtime/site.ts +9 -1
- package/src/services/__tests__/collection.test.ts +181 -53
- 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 +85 -17
- package/src/services/__tests__/post.test.ts +118 -10
- 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 +644 -191
- 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 +1365 -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 +779 -207
- 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 +4217 -1869
- package/src/types/bindings.ts +3 -1
- package/src/types/config.ts +139 -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 +12 -3
- 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 +221 -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 +201 -61
- 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 +232 -37
- 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 -3251
- 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 -803
- package/src/i18n/locales/en.ts +0 -1
- package/src/i18n/locales/zh-Hans.po +0 -3183
- package/src/i18n/locales/zh-Hans.ts +0 -1
- package/src/i18n/locales/zh-Hant.po +0 -3183
- 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,86 @@ 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";
|
|
22
31
|
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
32
|
import { render as renderMarkdown } from "../lib/markdown.js";
|
|
26
|
-
import { toISOString } from "../lib/time.js";
|
|
33
|
+
import { formatRelativeAge, toISOString } from "../lib/time.js";
|
|
34
|
+
import {
|
|
35
|
+
formatFrontMatter,
|
|
36
|
+
type HugoCollectionRef,
|
|
37
|
+
type HugoFrontMatter,
|
|
38
|
+
type JantMedia,
|
|
39
|
+
} from "../lib/hugo-markdown.js";
|
|
40
|
+
// Shared design tokens — single source of truth for colors, typography,
|
|
41
|
+
// and layout variables. Consumed verbatim by both the main site (via
|
|
42
|
+
// Tailwind) and the Hugo export (written to static/tokens.css). Using
|
|
43
|
+
// ?raw inlines the file contents as a string at build time so the
|
|
44
|
+
// Worker bundle ships without any filesystem access.
|
|
45
|
+
import TOKENS_CSS from "../styles/tokens.css?raw";
|
|
46
|
+
|
|
47
|
+
// Placeholder Hugo theme files — real templates and styles land in Commit 5.
|
|
48
|
+
// We import them as Vite `?raw` strings so the Worker bundle has no runtime
|
|
49
|
+
// filesystem dependency.
|
|
50
|
+
import THEME_TOML from "./export-theme/theme.toml?raw";
|
|
51
|
+
import THEME_STYLE_MAIN_CSS from "./export-theme/styles/main.css?raw";
|
|
52
|
+
// Static-site client bundle — Lit-based lightbox, feed video autoplay, audio
|
|
53
|
+
// waveform, and gallery scroll hints. Built by `vite.config.site.ts` into
|
|
54
|
+
// `export-theme/assets/client-site.{js,css}` and shipped under the theme's
|
|
55
|
+
// reserved `_jant/` static directory so the theme stays version-aligned with
|
|
56
|
+
// `@jant/core`. The assets folder is regenerated on every build, so the
|
|
57
|
+
// checked-in files are source-of-truth for the most recent release.
|
|
58
|
+
import CLIENT_SITE_JS from "./export-theme/assets/client-site.js?raw";
|
|
59
|
+
import CLIENT_SITE_CSS from "./export-theme/assets/client-site.css?raw";
|
|
60
|
+
import LAYOUT_BASEOF from "./export-theme/layouts/_default/baseof.html?raw";
|
|
61
|
+
import LAYOUT_SINGLE from "./export-theme/layouts/_default/single.html?raw";
|
|
62
|
+
import LAYOUT_LIST from "./export-theme/layouts/_default/list.html?raw";
|
|
63
|
+
import LAYOUT_ALIAS from "./export-theme/layouts/_default/alias.html?raw";
|
|
64
|
+
import LAYOUT_INDEX from "./export-theme/layouts/index.html?raw";
|
|
65
|
+
import LAYOUT_POST_LIST from "./export-theme/layouts/post/list.html?raw";
|
|
66
|
+
import LAYOUT_FEATURED_LIST from "./export-theme/layouts/featured/list.html?raw";
|
|
67
|
+
import LAYOUT_ARCHIVE_LIST from "./export-theme/layouts/archive/list.html?raw";
|
|
68
|
+
import LAYOUT_COLLECTIONS_LIST from "./export-theme/layouts/collections/list.html?raw";
|
|
69
|
+
import LAYOUT_COLLECTION_SINGLE from "./export-theme/layouts/collection/single.html?raw";
|
|
70
|
+
import PARTIAL_HEAD from "./export-theme/layouts/partials/head.html?raw";
|
|
71
|
+
import PARTIAL_HEADER from "./export-theme/layouts/partials/header.html?raw";
|
|
72
|
+
import PARTIAL_FOOTER from "./export-theme/layouts/partials/footer.html?raw";
|
|
73
|
+
import PARTIAL_PAGINATION from "./export-theme/layouts/partials/pagination.html?raw";
|
|
74
|
+
import PARTIAL_POST_CARD from "./export-theme/layouts/partials/post-card.html?raw";
|
|
75
|
+
import PARTIAL_MEDIA_GALLERY from "./export-theme/layouts/partials/media-gallery.html?raw";
|
|
76
|
+
import PARTIAL_REPLY from "./export-theme/layouts/partials/reply.html?raw";
|
|
77
|
+
import PARTIAL_THREAD_PREVIEW from "./export-theme/layouts/partials/thread-preview.html?raw";
|
|
78
|
+
import LAYOUT_RSS from "./export-theme/layouts/_default/rss.xml?raw";
|
|
79
|
+
import PARTIAL_FEED_POST_CONTENT from "./export-theme/layouts/partials/feed-post-content.xml?raw";
|
|
80
|
+
|
|
27
81
|
import type { StorageDriver } from "../lib/storage.js";
|
|
28
82
|
import { base64ToUint8Array } from "../lib/favicon.js";
|
|
29
|
-
import type {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
83
|
+
import type { Post, Collection, Media, NavItem } from "../types.js";
|
|
84
|
+
|
|
85
|
+
/** A file entry in the exported Hugo site. */
|
|
86
|
+
export interface ExportFile {
|
|
87
|
+
path: string;
|
|
88
|
+
content: string | Uint8Array;
|
|
89
|
+
}
|
|
36
90
|
|
|
37
91
|
export interface ExportService {
|
|
38
|
-
|
|
92
|
+
/** Generate a flat list of files for a complete Hugo site. */
|
|
93
|
+
generateHugoFiles(): Promise<ExportFile[]>;
|
|
94
|
+
/** Generate a ZIP archive of the Hugo site. */
|
|
95
|
+
generateHugoSite(): Promise<Uint8Array>;
|
|
39
96
|
}
|
|
40
97
|
|
|
41
|
-
interface SiteConfig {
|
|
98
|
+
export interface SiteConfig {
|
|
42
99
|
siteName: string;
|
|
43
100
|
siteUrl: string;
|
|
44
101
|
siteDescription: string;
|
|
45
102
|
siteLanguage: string;
|
|
46
103
|
showJantBrandingOnHome: boolean;
|
|
47
104
|
homeDefaultView: string;
|
|
48
|
-
|
|
105
|
+
/** "latest" or "featured" — drives the default RSS nav link in the exported site. */
|
|
106
|
+
mainRssFeed: string;
|
|
49
107
|
siteFooter: string;
|
|
50
108
|
showHeaderAvatar: boolean;
|
|
51
109
|
siteAvatarUrl: string;
|
|
@@ -66,31 +124,58 @@ interface SiteConfig {
|
|
|
66
124
|
sitePathPrefix?: string;
|
|
67
125
|
navItems: Pick<
|
|
68
126
|
NavItem,
|
|
69
|
-
"type" | "systemKey" | "label" | "url" | "position"
|
|
127
|
+
"type" | "systemKey" | "label" | "url" | "position" | "placement"
|
|
70
128
|
>[];
|
|
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;
|
|
129
|
+
/** Items per page for Hugo pagination — kept in sync with the main site's PAGE_SIZE. */
|
|
130
|
+
pageSize: number;
|
|
131
|
+
/** Items per archive page — kept in sync with the main site's ARCHIVE_PAGE_SIZE. */
|
|
132
|
+
archivePageSize: number;
|
|
133
|
+
/** Max items per Atom feed — kept in sync with the main site's rssFeedLimit. */
|
|
134
|
+
rssFeedLimit: number;
|
|
90
135
|
}
|
|
91
136
|
|
|
92
137
|
type IconExportMode = "default" | "custom";
|
|
93
138
|
|
|
139
|
+
type ExportedCollectionDirectoryItem =
|
|
140
|
+
| {
|
|
141
|
+
type: "collection";
|
|
142
|
+
sequence: string;
|
|
143
|
+
slug: string;
|
|
144
|
+
title: string;
|
|
145
|
+
/** Rendered HTML of the collection description, or null if empty. */
|
|
146
|
+
descriptionHtml?: string | null;
|
|
147
|
+
entryCount?: number;
|
|
148
|
+
recentActivityLabel?: string | null;
|
|
149
|
+
recentActivityIso?: string | null;
|
|
150
|
+
}
|
|
151
|
+
| {
|
|
152
|
+
type: "divider";
|
|
153
|
+
label: string | null;
|
|
154
|
+
}
|
|
155
|
+
| {
|
|
156
|
+
type: "link";
|
|
157
|
+
sequence: string;
|
|
158
|
+
label: string;
|
|
159
|
+
url: string;
|
|
160
|
+
/** Rendered HTML of the link description, or null if empty. */
|
|
161
|
+
descriptionHtml?: string | null;
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
interface ExportCollectionDirectorySourceItem {
|
|
165
|
+
type: "collection" | "divider" | "link";
|
|
166
|
+
label?: string | null;
|
|
167
|
+
url?: string | null;
|
|
168
|
+
description?: string | null;
|
|
169
|
+
collection?: {
|
|
170
|
+
id: string;
|
|
171
|
+
slug: string;
|
|
172
|
+
title: string;
|
|
173
|
+
description?: string | null;
|
|
174
|
+
postCount?: number;
|
|
175
|
+
recentActivityAt?: number;
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
94
179
|
interface SiteIconAssets {
|
|
95
180
|
faviconBytes: Uint8Array;
|
|
96
181
|
faviconMode: IconExportMode;
|
|
@@ -98,6 +183,41 @@ interface SiteIconAssets {
|
|
|
98
183
|
appleTouchMode: IconExportMode;
|
|
99
184
|
}
|
|
100
185
|
|
|
186
|
+
interface ExportedCollectionMetrics {
|
|
187
|
+
postCount: number;
|
|
188
|
+
recentActivityAt: number;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* A single `collections:` front-matter entry, already resolved to its
|
|
193
|
+
* Hugo-visible slug. Assembled from
|
|
194
|
+
* `collectionService.getCollectionEntriesByPostIds` + `collectionSlugMap`.
|
|
195
|
+
*/
|
|
196
|
+
interface ExportedCollectionEntry {
|
|
197
|
+
slug: string;
|
|
198
|
+
/**
|
|
199
|
+
* Denormalized collection title. Kept alongside the slug so the exported
|
|
200
|
+
* front matter can render a tag label without templates having to resolve
|
|
201
|
+
* another page. Optional because legacy call sites may not supply it.
|
|
202
|
+
*/
|
|
203
|
+
title?: string;
|
|
204
|
+
/** Unix seconds. */
|
|
205
|
+
collectedAt: number;
|
|
206
|
+
position: number;
|
|
207
|
+
/** Unix seconds, or null when not pinned in this collection. */
|
|
208
|
+
pinnedAt: number | null;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function buildDefaultAppleTouchAsset(): Pick<
|
|
212
|
+
SiteIconAssets,
|
|
213
|
+
"appleTouchBytes" | "appleTouchMode"
|
|
214
|
+
> {
|
|
215
|
+
return {
|
|
216
|
+
appleTouchBytes: getDefaultJantAppleTouchIconBytes(),
|
|
217
|
+
appleTouchMode: "default",
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
101
221
|
export function createExportService(
|
|
102
222
|
services: {
|
|
103
223
|
posts: PostService;
|
|
@@ -109,15 +229,22 @@ export function createExportService(
|
|
|
109
229
|
deps: { storage?: StorageDriver | null } = {},
|
|
110
230
|
): ExportService {
|
|
111
231
|
return {
|
|
112
|
-
async
|
|
232
|
+
async generateHugoFiles() {
|
|
233
|
+
const collectionDirectoryDataPromise =
|
|
234
|
+
typeof services.collections.listDirectoryData === "function"
|
|
235
|
+
? services.collections.listDirectoryData()
|
|
236
|
+
: Promise.resolve(null);
|
|
237
|
+
|
|
113
238
|
// 1. Query all data
|
|
114
|
-
const [allPosts, allCollections] =
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
239
|
+
const [allPosts, allCollections, collectionDirectoryData] =
|
|
240
|
+
await Promise.all([
|
|
241
|
+
services.posts.list({
|
|
242
|
+
excludeReplies: false,
|
|
243
|
+
limit: 10000,
|
|
244
|
+
}),
|
|
245
|
+
services.collections.list(),
|
|
246
|
+
collectionDirectoryDataPromise,
|
|
247
|
+
]);
|
|
121
248
|
|
|
122
249
|
const allPostIds = allPosts.map((p) => p.id);
|
|
123
250
|
const roots = allPosts.filter((p) => p.replyToId === null);
|
|
@@ -126,23 +253,45 @@ export function createExportService(
|
|
|
126
253
|
|
|
127
254
|
const [
|
|
128
255
|
collectionsByPost,
|
|
256
|
+
collectionEntriesByPost,
|
|
129
257
|
rawMediaByPost,
|
|
130
258
|
slugMap,
|
|
131
259
|
aliasMap,
|
|
132
260
|
collectionSlugMap,
|
|
133
261
|
] = await Promise.all([
|
|
134
262
|
services.collections.getCollectionsByPostIds(allPostIds),
|
|
263
|
+
services.collections.getCollectionEntriesByPostIds(allPostIds),
|
|
135
264
|
services.media.getByPostIds(allPostIds),
|
|
136
265
|
services.paths.getPostSlugMap(allPostIds),
|
|
137
266
|
services.paths.getPostAliases(rootPostIds),
|
|
138
267
|
services.paths.getCollectionSlugMap(allCollections.map((c) => c.id)),
|
|
139
268
|
]);
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
);
|
|
269
|
+
// Denormalized title lookup so front-matter collection refs can
|
|
270
|
+
// include a title label without templates having to resolve another
|
|
271
|
+
// page. Source of truth is still `slug` on round-trip; `title` is
|
|
272
|
+
// refreshed from DB on every export.
|
|
273
|
+
const collectionTitleMap = new Map<string, string>();
|
|
274
|
+
for (const collection of allCollections) {
|
|
275
|
+
collectionTitleMap.set(collection.id, collection.title);
|
|
276
|
+
}
|
|
277
|
+
|
|
145
278
|
const iconAssets = await buildSiteIconAssets(siteConfig, deps.storage);
|
|
279
|
+
const collectionMetrics = buildExportedCollectionMetrics(
|
|
280
|
+
allCollections,
|
|
281
|
+
allPosts,
|
|
282
|
+
collectionsByPost,
|
|
283
|
+
);
|
|
284
|
+
const exportedCollectionDirectoryItems =
|
|
285
|
+
buildExportedCollectionDirectoryItems(
|
|
286
|
+
collectionDirectoryData?.items ??
|
|
287
|
+
allCollections.map((collection) => ({
|
|
288
|
+
id: collection.id,
|
|
289
|
+
type: "collection" as const,
|
|
290
|
+
collection,
|
|
291
|
+
})),
|
|
292
|
+
collectionSlugMap,
|
|
293
|
+
collectionMetrics,
|
|
294
|
+
);
|
|
146
295
|
|
|
147
296
|
// 2. Group replies by threadId
|
|
148
297
|
const repliesByThread = new Map<string, Post[]>();
|
|
@@ -156,90 +305,236 @@ export function createExportService(
|
|
|
156
305
|
list.sort((a, b) => a.createdAt - b.createdAt);
|
|
157
306
|
}
|
|
158
307
|
|
|
159
|
-
// 3. Build
|
|
160
|
-
const
|
|
161
|
-
const files: Record<string, Uint8Array> = {};
|
|
308
|
+
// 3. Build file list
|
|
309
|
+
const exportFiles: ExportFile[] = [];
|
|
162
310
|
|
|
163
|
-
// Generate
|
|
311
|
+
// Generate thread bundles (root _index.md + per-reply index.md).
|
|
164
312
|
for (const root of roots) {
|
|
165
313
|
const slug = slugMap.get(root.id) ?? root.slug;
|
|
166
314
|
const threadReplies = repliesByThread.get(root.id) ?? [];
|
|
167
|
-
const postCollections = collectionsByPost.get(root.id) ?? [];
|
|
168
315
|
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
316
|
|
|
179
|
-
const
|
|
317
|
+
const rootCollectionEntries = buildExportedCollectionEntriesForPost(
|
|
318
|
+
root.id,
|
|
319
|
+
collectionEntriesByPost,
|
|
320
|
+
collectionSlugMap,
|
|
321
|
+
collectionTitleMap,
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
const bundleFiles = await buildThreadBundle(
|
|
180
325
|
root,
|
|
181
326
|
threadReplies,
|
|
182
|
-
|
|
183
|
-
|
|
327
|
+
slug,
|
|
328
|
+
rootAliases,
|
|
329
|
+
rootCollectionEntries,
|
|
184
330
|
slugMap,
|
|
331
|
+
collectionEntriesByPost,
|
|
185
332
|
collectionSlugMap,
|
|
186
|
-
|
|
333
|
+
collectionTitleMap,
|
|
187
334
|
rawMediaByPost,
|
|
188
335
|
siteConfig,
|
|
189
|
-
|
|
336
|
+
deps.storage ?? null,
|
|
190
337
|
);
|
|
191
|
-
|
|
192
|
-
files[`content/${slug}/index.md`] = new TextEncoder().encode(markdown);
|
|
338
|
+
exportFiles.push(...bundleFiles);
|
|
193
339
|
}
|
|
194
340
|
|
|
341
|
+
// Collection landing pages (`content/{slug}/_index.md`).
|
|
195
342
|
for (const collection of allCollections) {
|
|
196
343
|
const slug = collectionSlugMap.get(collection.id) ?? collection.slug;
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
344
|
+
const entryCount = collectionMetrics.get(collection.id)?.postCount ?? 0;
|
|
345
|
+
exportFiles.push({
|
|
346
|
+
path: `content/${slug}/_index.md`,
|
|
347
|
+
content: await buildCollectionSection(collection, slug, entryCount),
|
|
348
|
+
});
|
|
201
349
|
}
|
|
202
350
|
|
|
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
|
-
);
|
|
351
|
+
// Section + home scaffolding.
|
|
352
|
+
exportFiles.push({
|
|
353
|
+
path: "hugo.toml",
|
|
354
|
+
content: buildHugoToml(siteConfig),
|
|
355
|
+
});
|
|
356
|
+
exportFiles.push({
|
|
357
|
+
path: "content/_index.md",
|
|
358
|
+
content: await buildHomeSection(siteConfig),
|
|
359
|
+
});
|
|
360
|
+
exportFiles.push({
|
|
361
|
+
path: "content/collections/_index.md",
|
|
362
|
+
content: await buildCollectionsSection(),
|
|
363
|
+
});
|
|
364
|
+
exportFiles.push({
|
|
365
|
+
path: "content/archive/_index.md",
|
|
366
|
+
content: await buildArchiveSection(),
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
const usedSlugs = new Set<string>();
|
|
370
|
+
for (const s of slugMap.values()) usedSlugs.add(s);
|
|
371
|
+
for (const s of collectionSlugMap.values()) usedSlugs.add(s);
|
|
372
|
+
if (!usedSlugs.has("featured")) {
|
|
373
|
+
exportFiles.push({
|
|
374
|
+
path: "content/featured/_index.md",
|
|
375
|
+
content: await buildFeaturedSection(),
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Single data file consumed by templates via `hugo.Data.jant`. The
|
|
380
|
+
// collection directory lives on the same object as `directory` so
|
|
381
|
+
// everything Jant owns round-trips in one place.
|
|
382
|
+
exportFiles.push({
|
|
383
|
+
path: "data/jant.toml",
|
|
384
|
+
content: buildJantDataToml(
|
|
385
|
+
siteConfig,
|
|
386
|
+
iconAssets,
|
|
387
|
+
exportedCollectionDirectoryItems,
|
|
388
|
+
),
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
// Theme scaffolding (real templates + styles land in Commit 5).
|
|
392
|
+
exportFiles.push({
|
|
393
|
+
path: "themes/jant/theme.toml",
|
|
394
|
+
content: THEME_TOML,
|
|
395
|
+
});
|
|
396
|
+
exportFiles.push({
|
|
397
|
+
path: "themes/jant/layouts/_default/baseof.html",
|
|
398
|
+
content: LAYOUT_BASEOF,
|
|
399
|
+
});
|
|
400
|
+
exportFiles.push({
|
|
401
|
+
path: "themes/jant/layouts/_default/single.html",
|
|
402
|
+
content: LAYOUT_SINGLE,
|
|
403
|
+
});
|
|
404
|
+
exportFiles.push({
|
|
405
|
+
path: "themes/jant/layouts/_default/list.html",
|
|
406
|
+
content: LAYOUT_LIST,
|
|
407
|
+
});
|
|
408
|
+
exportFiles.push({
|
|
409
|
+
path: "themes/jant/layouts/_default/alias.html",
|
|
410
|
+
content: LAYOUT_ALIAS,
|
|
411
|
+
});
|
|
412
|
+
exportFiles.push({
|
|
413
|
+
path: "themes/jant/layouts/index.html",
|
|
414
|
+
content: LAYOUT_INDEX,
|
|
415
|
+
});
|
|
416
|
+
exportFiles.push({
|
|
417
|
+
path: "themes/jant/layouts/post/list.html",
|
|
418
|
+
content: LAYOUT_POST_LIST,
|
|
419
|
+
});
|
|
420
|
+
exportFiles.push({
|
|
421
|
+
path: "themes/jant/layouts/featured/list.html",
|
|
422
|
+
content: LAYOUT_FEATURED_LIST,
|
|
423
|
+
});
|
|
424
|
+
exportFiles.push({
|
|
425
|
+
path: "themes/jant/layouts/archive/list.html",
|
|
426
|
+
content: LAYOUT_ARCHIVE_LIST,
|
|
427
|
+
});
|
|
428
|
+
exportFiles.push({
|
|
429
|
+
path: "themes/jant/layouts/collections/list.html",
|
|
430
|
+
content: LAYOUT_COLLECTIONS_LIST,
|
|
431
|
+
});
|
|
432
|
+
exportFiles.push({
|
|
433
|
+
path: "themes/jant/layouts/collection/single.html",
|
|
434
|
+
content: LAYOUT_COLLECTION_SINGLE,
|
|
435
|
+
});
|
|
436
|
+
exportFiles.push({
|
|
437
|
+
path: "themes/jant/layouts/partials/head.html",
|
|
438
|
+
content: PARTIAL_HEAD,
|
|
439
|
+
});
|
|
440
|
+
exportFiles.push({
|
|
441
|
+
path: "themes/jant/layouts/partials/header.html",
|
|
442
|
+
content: PARTIAL_HEADER,
|
|
443
|
+
});
|
|
444
|
+
exportFiles.push({
|
|
445
|
+
path: "themes/jant/layouts/partials/footer.html",
|
|
446
|
+
content: PARTIAL_FOOTER,
|
|
447
|
+
});
|
|
448
|
+
exportFiles.push({
|
|
449
|
+
path: "themes/jant/layouts/partials/pagination.html",
|
|
450
|
+
content: PARTIAL_PAGINATION,
|
|
451
|
+
});
|
|
452
|
+
exportFiles.push({
|
|
453
|
+
path: "themes/jant/layouts/partials/post-card.html",
|
|
454
|
+
content: PARTIAL_POST_CARD,
|
|
455
|
+
});
|
|
456
|
+
exportFiles.push({
|
|
457
|
+
path: "themes/jant/layouts/partials/media-gallery.html",
|
|
458
|
+
content: PARTIAL_MEDIA_GALLERY,
|
|
459
|
+
});
|
|
460
|
+
exportFiles.push({
|
|
461
|
+
path: "themes/jant/layouts/partials/reply.html",
|
|
462
|
+
content: PARTIAL_REPLY,
|
|
463
|
+
});
|
|
464
|
+
exportFiles.push({
|
|
465
|
+
path: "themes/jant/layouts/partials/thread-preview.html",
|
|
466
|
+
content: PARTIAL_THREAD_PREVIEW,
|
|
467
|
+
});
|
|
468
|
+
exportFiles.push({
|
|
469
|
+
path: "themes/jant/layouts/_default/rss.xml",
|
|
470
|
+
content: LAYOUT_RSS,
|
|
471
|
+
});
|
|
472
|
+
exportFiles.push({
|
|
473
|
+
path: "themes/jant/layouts/partials/feed-post-content.xml",
|
|
474
|
+
content: PARTIAL_FEED_POST_CONTENT,
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
// Static assets. Load order in the template's <head> is
|
|
478
|
+
// tokens → main → theme → custom (wired up by the Commit 5 partial).
|
|
479
|
+
exportFiles.push({
|
|
480
|
+
path: "themes/jant/static/tokens.css",
|
|
481
|
+
content: TOKENS_CSS,
|
|
482
|
+
});
|
|
483
|
+
exportFiles.push({
|
|
484
|
+
path: "themes/jant/static/main.css",
|
|
485
|
+
content: THEME_STYLE_MAIN_CSS,
|
|
486
|
+
});
|
|
487
|
+
exportFiles.push({
|
|
488
|
+
path: "themes/jant/static/theme.css",
|
|
489
|
+
content: siteConfig.themeCss ?? "",
|
|
490
|
+
});
|
|
491
|
+
exportFiles.push({
|
|
492
|
+
path: "themes/jant/static/custom.css",
|
|
493
|
+
content: siteConfig.customCss ?? "",
|
|
494
|
+
});
|
|
495
|
+
// Client-side interactions: media lightbox, feed video autoplay,
|
|
496
|
+
// audio waveform, gallery scroll hints. Reserved namespace keeps these
|
|
497
|
+
// from colliding with user-authored static files.
|
|
498
|
+
exportFiles.push({
|
|
499
|
+
path: "themes/jant/static/_jant/client-site.js",
|
|
500
|
+
content: CLIENT_SITE_JS,
|
|
501
|
+
});
|
|
502
|
+
exportFiles.push({
|
|
503
|
+
path: "themes/jant/static/_jant/client-site.css",
|
|
504
|
+
content: CLIENT_SITE_CSS,
|
|
505
|
+
});
|
|
506
|
+
exportFiles.push({
|
|
507
|
+
path: "themes/jant/static/favicon.ico",
|
|
508
|
+
content: iconAssets.faviconBytes,
|
|
509
|
+
});
|
|
510
|
+
exportFiles.push({
|
|
511
|
+
path: "themes/jant/static/apple-touch-icon.png",
|
|
512
|
+
content: iconAssets.appleTouchBytes,
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
exportFiles.push({
|
|
516
|
+
path: "README.md",
|
|
517
|
+
content: buildReadme(siteConfig.siteName),
|
|
518
|
+
});
|
|
519
|
+
exportFiles.push({
|
|
520
|
+
path: ".gitignore",
|
|
521
|
+
content: buildGitignore(),
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
return exportFiles;
|
|
525
|
+
},
|
|
242
526
|
|
|
527
|
+
async generateHugoSite() {
|
|
528
|
+
const exportFiles = await this.generateHugoFiles();
|
|
529
|
+
const { zipSync } = await import("fflate");
|
|
530
|
+
const encoder = new TextEncoder();
|
|
531
|
+
const files: Record<string, Uint8Array> = {};
|
|
532
|
+
for (const file of exportFiles) {
|
|
533
|
+
files[file.path] =
|
|
534
|
+
typeof file.content === "string"
|
|
535
|
+
? encoder.encode(file.content)
|
|
536
|
+
: file.content;
|
|
537
|
+
}
|
|
243
538
|
return zipSync(files);
|
|
244
539
|
},
|
|
245
540
|
};
|
|
@@ -272,25 +567,38 @@ async function buildSiteIconAssets(
|
|
|
272
567
|
return {
|
|
273
568
|
faviconBytes,
|
|
274
569
|
faviconMode,
|
|
275
|
-
|
|
276
|
-
appleTouchMode: "default",
|
|
570
|
+
...buildDefaultAppleTouchAsset(),
|
|
277
571
|
};
|
|
278
572
|
}
|
|
279
573
|
|
|
280
574
|
if (!storage) {
|
|
281
|
-
|
|
282
|
-
|
|
575
|
+
return {
|
|
576
|
+
faviconBytes,
|
|
577
|
+
faviconMode,
|
|
578
|
+
...buildDefaultAppleTouchAsset(),
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
let appleTouchBytes: Uint8Array | null;
|
|
583
|
+
try {
|
|
584
|
+
appleTouchBytes = await readStorageObjectBytes(
|
|
585
|
+
storage,
|
|
586
|
+
config.appleTouchIconStorageKey,
|
|
283
587
|
);
|
|
588
|
+
} catch {
|
|
589
|
+
return {
|
|
590
|
+
faviconBytes,
|
|
591
|
+
faviconMode,
|
|
592
|
+
...buildDefaultAppleTouchAsset(),
|
|
593
|
+
};
|
|
284
594
|
}
|
|
285
595
|
|
|
286
|
-
const appleTouchBytes = await readStorageObjectBytes(
|
|
287
|
-
storage,
|
|
288
|
-
config.appleTouchIconStorageKey,
|
|
289
|
-
);
|
|
290
596
|
if (!appleTouchBytes) {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
597
|
+
return {
|
|
598
|
+
faviconBytes,
|
|
599
|
+
faviconMode,
|
|
600
|
+
...buildDefaultAppleTouchAsset(),
|
|
601
|
+
};
|
|
294
602
|
}
|
|
295
603
|
|
|
296
604
|
return {
|
|
@@ -302,563 +610,1033 @@ async function buildSiteIconAssets(
|
|
|
302
610
|
}
|
|
303
611
|
|
|
304
612
|
// ---------------------------------------------------------------------------
|
|
305
|
-
//
|
|
613
|
+
// Thread bundle generation
|
|
306
614
|
// ---------------------------------------------------------------------------
|
|
307
615
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
616
|
+
function buildExportedCollectionEntriesForPost(
|
|
617
|
+
postId: string,
|
|
618
|
+
collectionEntriesByPost: Map<
|
|
619
|
+
string,
|
|
620
|
+
{
|
|
621
|
+
collectionId: string;
|
|
622
|
+
createdAt: number;
|
|
623
|
+
position: number;
|
|
624
|
+
pinnedAt: number | null;
|
|
625
|
+
}[]
|
|
626
|
+
>,
|
|
627
|
+
collectionSlugMap: Map<string, string>,
|
|
628
|
+
collectionTitleMap: Map<string, string>,
|
|
629
|
+
): ExportedCollectionEntry[] {
|
|
630
|
+
const entries = collectionEntriesByPost.get(postId) ?? [];
|
|
631
|
+
const resolved: ExportedCollectionEntry[] = [];
|
|
632
|
+
for (const entry of entries) {
|
|
633
|
+
const slug = collectionSlugMap.get(entry.collectionId);
|
|
634
|
+
if (!slug) continue;
|
|
635
|
+
const title = collectionTitleMap.get(entry.collectionId);
|
|
636
|
+
resolved.push({
|
|
637
|
+
slug,
|
|
638
|
+
title,
|
|
639
|
+
collectedAt: entry.createdAt,
|
|
640
|
+
position: entry.position,
|
|
641
|
+
pinnedAt: entry.pinnedAt,
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
return resolved;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
function collectionEntriesToRefs(
|
|
648
|
+
entries: readonly ExportedCollectionEntry[],
|
|
649
|
+
): HugoCollectionRef[] {
|
|
650
|
+
return entries.map((entry) => ({
|
|
651
|
+
slug: entry.slug,
|
|
652
|
+
title: entry.title,
|
|
653
|
+
collected_at: toISOString(entry.collectedAt),
|
|
654
|
+
position: entry.position,
|
|
655
|
+
pinned_at: entry.pinnedAt !== null ? toISOString(entry.pinnedAt) : null,
|
|
656
|
+
}));
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
interface MediaEmission {
|
|
660
|
+
/** Flat `media:` front-matter entry. */
|
|
661
|
+
entry: JantMedia;
|
|
662
|
+
/**
|
|
663
|
+
* Site-relative path under `static/` where the primary bytes should
|
|
664
|
+
* land, or null when the media links to a remote public URL and no
|
|
665
|
+
* bytes need to be emitted.
|
|
666
|
+
*/
|
|
667
|
+
inlinePath: string | null;
|
|
668
|
+
/**
|
|
669
|
+
* Site-relative path under `static/` where the poster bytes should
|
|
670
|
+
* land, or null when there's no poster or the poster is linked via
|
|
671
|
+
* a public URL.
|
|
672
|
+
*/
|
|
673
|
+
inlinePosterPath: string | null;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
/**
|
|
677
|
+
* Build a flat `media:` entry for a Media record plus a decision about
|
|
678
|
+
* whether the primary bytes (and poster) should be bundled into the
|
|
679
|
+
* export's `static/media/` directory.
|
|
680
|
+
*
|
|
681
|
+
* When the media's provider has a reachable public URL (R2/S3/local
|
|
682
|
+
* proxy configured with a `*_public_url`), `src` points at that absolute
|
|
683
|
+
* URL and no bytes are emitted — the exported site stays small and the
|
|
684
|
+
* media keeps being served from wherever it already lives. Without a
|
|
685
|
+
* public URL the bytes are written to `static/media/{id}.ext` and `src`
|
|
686
|
+
* is the site-relative path.
|
|
687
|
+
*/
|
|
688
|
+
function buildMediaEmission(
|
|
689
|
+
media: Media,
|
|
690
|
+
siteConfig: SiteConfig,
|
|
691
|
+
): MediaEmission {
|
|
692
|
+
const publicUrl = getPublicUrlForProvider(
|
|
693
|
+
media.provider,
|
|
694
|
+
siteConfig.r2PublicUrl,
|
|
695
|
+
siteConfig.s3PublicUrl,
|
|
696
|
+
siteConfig.localPublicUrl,
|
|
697
|
+
);
|
|
698
|
+
const hasPublic = Boolean(publicUrl);
|
|
699
|
+
|
|
700
|
+
const ext = extOfFilename(media.filename);
|
|
701
|
+
const localName = `${media.id}${ext}`;
|
|
702
|
+
const localPath = `/media/${localName}`;
|
|
703
|
+
const src = hasPublic ? getMediaUrl(media.storageKey, publicUrl) : localPath;
|
|
704
|
+
|
|
705
|
+
const entry: JantMedia = {
|
|
706
|
+
id: media.id,
|
|
707
|
+
kind: media.mediaKind,
|
|
708
|
+
src,
|
|
709
|
+
position: parsePositionForSort(media.position),
|
|
710
|
+
};
|
|
711
|
+
if (media.alt !== null && media.alt !== "") entry.alt = media.alt;
|
|
712
|
+
if (media.width !== null) entry.width = media.width;
|
|
713
|
+
if (media.height !== null) entry.height = media.height;
|
|
714
|
+
if (media.blurhash !== null && media.blurhash !== "")
|
|
715
|
+
entry.blurhash = media.blurhash;
|
|
716
|
+
if (media.originalName) entry.original_name = media.originalName;
|
|
717
|
+
if (media.mimeType) entry.mime_type = media.mimeType;
|
|
718
|
+
if (typeof media.size === "number") entry.size = media.size;
|
|
719
|
+
if (media.waveform) entry.waveform = media.waveform;
|
|
720
|
+
if (media.summary) entry.summary = media.summary;
|
|
721
|
+
if (typeof media.chars === "number") entry.chars = media.chars;
|
|
722
|
+
if (media.durationSeconds !== null && media.durationSeconds !== undefined) {
|
|
723
|
+
entry.duration_seconds = media.durationSeconds;
|
|
724
|
+
}
|
|
725
|
+
entry.provider = media.provider;
|
|
726
|
+
entry.storage_key = media.storageKey;
|
|
727
|
+
|
|
728
|
+
let inlinePosterPath: string | null = null;
|
|
729
|
+
if (media.posterKey) {
|
|
730
|
+
const posterExt = extOfStorageKey(media.posterKey);
|
|
731
|
+
const posterLocalName = `${media.id}-poster.${posterExt}`;
|
|
732
|
+
entry.poster = hasPublic
|
|
733
|
+
? getMediaUrl(media.posterKey, publicUrl)
|
|
734
|
+
: `/media/${posterLocalName}`;
|
|
735
|
+
entry.poster_key = media.posterKey;
|
|
736
|
+
if (!hasPublic) {
|
|
737
|
+
inlinePosterPath = `static/media/${posterLocalName}`;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
return {
|
|
742
|
+
entry,
|
|
743
|
+
inlinePath: hasPublic ? null : `static/media/${localName}`,
|
|
744
|
+
inlinePosterPath,
|
|
745
|
+
};
|
|
315
746
|
}
|
|
316
747
|
|
|
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")}"`;
|
|
748
|
+
function parsePositionForSort(position: string): number {
|
|
749
|
+
// Fractional indexing keys sort lexicographically, but downstream
|
|
750
|
+
// consumers (and the UI) expect a numeric fallback. Keep a stable
|
|
751
|
+
// ordering by hashing the string into an integer.
|
|
752
|
+
let hash = 0;
|
|
753
|
+
for (let i = 0; i < position.length; i++) {
|
|
754
|
+
hash = (hash * 31 + position.charCodeAt(i)) | 0;
|
|
330
755
|
}
|
|
331
|
-
return
|
|
756
|
+
return hash;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
function extOfFilename(filename: string): string {
|
|
760
|
+
const dot = filename.lastIndexOf(".");
|
|
761
|
+
return dot >= 0 ? filename.slice(dot) : "";
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
function extOfStorageKey(key: string): string {
|
|
765
|
+
const dot = key.lastIndexOf(".");
|
|
766
|
+
return dot >= 0 ? key.slice(dot + 1) : "webp";
|
|
332
767
|
}
|
|
333
768
|
|
|
334
|
-
|
|
769
|
+
/**
|
|
770
|
+
* Build a complete set of ExportFile entries for a single thread bundle:
|
|
771
|
+
* the root `_index.md`, one `index.md` per reply, and resource blobs for
|
|
772
|
+
* attached media when the storage driver can fetch them.
|
|
773
|
+
*/
|
|
774
|
+
async function buildThreadBundle(
|
|
335
775
|
root: Post,
|
|
336
776
|
threadReplies: Post[],
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
zolaAliases: string[];
|
|
341
|
-
},
|
|
777
|
+
rootSlug: string,
|
|
778
|
+
rootAliases: string[],
|
|
779
|
+
rootCollectionEntries: ExportedCollectionEntry[],
|
|
342
780
|
slugMap: Map<string, string>,
|
|
781
|
+
collectionEntriesByPost: Map<
|
|
782
|
+
string,
|
|
783
|
+
{
|
|
784
|
+
collectionId: string;
|
|
785
|
+
createdAt: number;
|
|
786
|
+
position: number;
|
|
787
|
+
pinnedAt: number | null;
|
|
788
|
+
}[]
|
|
789
|
+
>,
|
|
343
790
|
collectionSlugMap: Map<string, string>,
|
|
344
|
-
|
|
791
|
+
collectionTitleMap: Map<string, string>,
|
|
345
792
|
mediaByPost: Map<string, Media[]>,
|
|
346
793
|
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");
|
|
794
|
+
storage: StorageDriver | null,
|
|
795
|
+
): Promise<ExportFile[]> {
|
|
796
|
+
const files: ExportFile[] = [];
|
|
797
|
+
|
|
798
|
+
// Root aliases = historical root slugs + every reply slug (so
|
|
799
|
+
// /{reply-slug}/ gets a Hugo alias page that redirects/anchors to
|
|
800
|
+
// the thread root).
|
|
801
|
+
const aliases = [...rootAliases];
|
|
802
|
+
for (const reply of threadReplies) {
|
|
803
|
+
const replySlug = slugMap.get(reply.id) ?? reply.slug;
|
|
804
|
+
aliases.push(`/${replySlug}/`);
|
|
365
805
|
}
|
|
366
806
|
|
|
367
|
-
|
|
368
|
-
|
|
807
|
+
// Root front matter.
|
|
808
|
+
const rootMedia = mediaByPost.get(root.id) ?? [];
|
|
809
|
+
const rootEmissions = rootMedia.map((m) => buildMediaEmission(m, siteConfig));
|
|
810
|
+
const rootMediaList = rootEmissions.map((e) => e.entry);
|
|
811
|
+
const rootFrontMatter: HugoFrontMatter = {
|
|
812
|
+
id: root.id,
|
|
813
|
+
title: root.format !== "quote" ? (root.title ?? undefined) : undefined,
|
|
814
|
+
date:
|
|
815
|
+
root.publishedAt !== null
|
|
816
|
+
? toISOString(root.publishedAt)
|
|
817
|
+
: toISOString(root.createdAt),
|
|
818
|
+
updated:
|
|
819
|
+
root.updatedAt && root.updatedAt !== root.publishedAt
|
|
820
|
+
? toISOString(root.updatedAt)
|
|
821
|
+
: undefined,
|
|
822
|
+
// `updated` only reflects edits to the root post itself; when a reply
|
|
823
|
+
// lands, it does NOT bump. For RSS we want the thread's last activity
|
|
824
|
+
// (max of all published reply timestamps) so readers re-surface a
|
|
825
|
+
// thread when a new reply appears. Kept alongside `updated` so the
|
|
826
|
+
// importer round-trips cleanly.
|
|
827
|
+
last_activity_at:
|
|
828
|
+
root.lastActivityAt !== null && root.lastActivityAt !== root.publishedAt
|
|
829
|
+
? toISOString(root.lastActivityAt)
|
|
830
|
+
: undefined,
|
|
831
|
+
slug: rootSlug,
|
|
832
|
+
type: "post",
|
|
833
|
+
draft:
|
|
834
|
+
root.status === "draft" || root.visibility === "private"
|
|
835
|
+
? true
|
|
836
|
+
: undefined,
|
|
837
|
+
aliases: aliases.length > 0 ? aliases : undefined,
|
|
838
|
+
format: root.format,
|
|
839
|
+
status: root.status,
|
|
840
|
+
visibility: root.visibility,
|
|
841
|
+
summary_text: getArchiveSummaryText(root) ?? undefined,
|
|
842
|
+
link_url: root.format === "link" && root.url ? root.url : undefined,
|
|
843
|
+
source_name: root.format === "quote" && root.title ? root.title : undefined,
|
|
844
|
+
source_url: root.format === "quote" && root.url ? root.url : undefined,
|
|
845
|
+
quote_text: root.quoteText ?? undefined,
|
|
846
|
+
rating: root.rating ?? undefined,
|
|
847
|
+
featured_at:
|
|
848
|
+
root.featuredAt !== null ? toISOString(root.featuredAt) : undefined,
|
|
849
|
+
pinned_at: root.pinnedAt !== null ? toISOString(root.pinnedAt) : undefined,
|
|
850
|
+
root_aliases: rootAliases.length > 0 ? rootAliases : undefined,
|
|
851
|
+
collections:
|
|
852
|
+
rootCollectionEntries.length > 0
|
|
853
|
+
? collectionEntriesToRefs(rootCollectionEntries)
|
|
854
|
+
: undefined,
|
|
855
|
+
media: rootMediaList.length > 0 ? rootMediaList : undefined,
|
|
856
|
+
};
|
|
369
857
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
858
|
+
const rootBody = root.body ? tiptapJsonToMarkdown(root.body) : "";
|
|
859
|
+
files.push({
|
|
860
|
+
path: `content/${rootSlug}/_index.md`,
|
|
861
|
+
content: `${await formatFrontMatter(rootFrontMatter)}\n${rootBody}${rootBody.endsWith("\n") ? "" : "\n"}`,
|
|
862
|
+
});
|
|
863
|
+
|
|
864
|
+
// Emit media bytes under static/media/ for any media without a
|
|
865
|
+
// reachable public URL. Media whose provider has a configured public
|
|
866
|
+
// URL keeps `src` pointing at that absolute URL and skips inlining —
|
|
867
|
+
// this avoids re-downloading every attachment when the site is going
|
|
868
|
+
// to keep serving media from the existing CDN/proxy anyway.
|
|
869
|
+
for (const { emission, media } of rootEmissions.map((e, i) => ({
|
|
870
|
+
emission: e,
|
|
871
|
+
media: rootMedia[i] as Media,
|
|
872
|
+
}))) {
|
|
873
|
+
if (emission.inlinePath) {
|
|
874
|
+
const file = await readMediaResourceFile(
|
|
875
|
+
storage,
|
|
876
|
+
media.storageKey,
|
|
877
|
+
emission.inlinePath,
|
|
878
|
+
);
|
|
879
|
+
if (file) files.push(file);
|
|
374
880
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
const colSlug = collectionSlugMap.get(c.id) ?? c.slug;
|
|
383
|
-
parts.push(` - ${yamlString(colSlug)}`);
|
|
881
|
+
if (emission.inlinePosterPath && media.posterKey) {
|
|
882
|
+
const posterFile = await readMediaResourceFile(
|
|
883
|
+
storage,
|
|
884
|
+
media.posterKey,
|
|
885
|
+
emission.inlinePosterPath,
|
|
886
|
+
);
|
|
887
|
+
if (posterFile) files.push(posterFile);
|
|
384
888
|
}
|
|
385
889
|
}
|
|
386
890
|
|
|
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
|
-
}
|
|
891
|
+
// Replies as nested leaf bundles.
|
|
892
|
+
for (const reply of threadReplies) {
|
|
893
|
+
const replySlug = slugMap.get(reply.id) ?? reply.slug;
|
|
894
|
+
const replyMedia = mediaByPost.get(reply.id) ?? [];
|
|
895
|
+
const replyEmissions = replyMedia.map((m) =>
|
|
896
|
+
buildMediaEmission(m, siteConfig),
|
|
897
|
+
);
|
|
898
|
+
const replyMediaList = replyEmissions.map((e) => e.entry);
|
|
899
|
+
const replyCollectionEntries = buildExportedCollectionEntriesForPost(
|
|
900
|
+
reply.id,
|
|
901
|
+
collectionEntriesByPost,
|
|
902
|
+
collectionSlugMap,
|
|
903
|
+
collectionTitleMap,
|
|
904
|
+
);
|
|
424
905
|
|
|
425
|
-
|
|
426
|
-
|
|
906
|
+
const replyFrontMatter: HugoFrontMatter = {
|
|
907
|
+
id: reply.id,
|
|
908
|
+
title: reply.format !== "quote" ? (reply.title ?? undefined) : undefined,
|
|
909
|
+
date:
|
|
910
|
+
reply.publishedAt !== null
|
|
911
|
+
? toISOString(reply.publishedAt)
|
|
912
|
+
: toISOString(reply.createdAt),
|
|
913
|
+
updated:
|
|
914
|
+
reply.updatedAt && reply.updatedAt !== reply.publishedAt
|
|
915
|
+
? toISOString(reply.updatedAt)
|
|
916
|
+
: undefined,
|
|
917
|
+
slug: replySlug,
|
|
918
|
+
type: "post",
|
|
919
|
+
draft:
|
|
920
|
+
reply.status === "draft" || reply.visibility === "private"
|
|
921
|
+
? true
|
|
922
|
+
: undefined,
|
|
923
|
+
build: { render: "never", list: "local" },
|
|
924
|
+
format: reply.format,
|
|
925
|
+
status: reply.status,
|
|
926
|
+
visibility: reply.visibility,
|
|
927
|
+
summary_text: getArchiveSummaryText(reply) ?? undefined,
|
|
928
|
+
link_url: reply.format === "link" && reply.url ? reply.url : undefined,
|
|
929
|
+
source_name:
|
|
930
|
+
reply.format === "quote" && reply.title ? reply.title : undefined,
|
|
931
|
+
source_url: reply.format === "quote" && reply.url ? reply.url : undefined,
|
|
932
|
+
quote_text: reply.quoteText ?? undefined,
|
|
933
|
+
rating: reply.rating ?? undefined,
|
|
934
|
+
featured_at:
|
|
935
|
+
reply.featuredAt !== null ? toISOString(reply.featuredAt) : undefined,
|
|
936
|
+
pinned_at:
|
|
937
|
+
reply.pinnedAt !== null ? toISOString(reply.pinnedAt) : undefined,
|
|
938
|
+
collections:
|
|
939
|
+
replyCollectionEntries.length > 0
|
|
940
|
+
? collectionEntriesToRefs(replyCollectionEntries)
|
|
941
|
+
: undefined,
|
|
942
|
+
media: replyMediaList.length > 0 ? replyMediaList : undefined,
|
|
943
|
+
};
|
|
427
944
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
945
|
+
const replyBody = reply.body ? tiptapJsonToMarkdown(reply.body) : "";
|
|
946
|
+
files.push({
|
|
947
|
+
path: `content/${rootSlug}/${replySlug}/index.md`,
|
|
948
|
+
content: `${await formatFrontMatter(replyFrontMatter)}\n${replyBody}${replyBody.endsWith("\n") ? "" : "\n"}`,
|
|
949
|
+
});
|
|
950
|
+
|
|
951
|
+
for (const { emission, media } of replyEmissions.map((e, i) => ({
|
|
952
|
+
emission: e,
|
|
953
|
+
media: replyMedia[i] as Media,
|
|
954
|
+
}))) {
|
|
955
|
+
if (emission.inlinePath) {
|
|
956
|
+
const file = await readMediaResourceFile(
|
|
957
|
+
storage,
|
|
958
|
+
media.storageKey,
|
|
959
|
+
emission.inlinePath,
|
|
960
|
+
);
|
|
961
|
+
if (file) files.push(file);
|
|
962
|
+
}
|
|
963
|
+
if (emission.inlinePosterPath && media.posterKey) {
|
|
964
|
+
const posterFile = await readMediaResourceFile(
|
|
965
|
+
storage,
|
|
966
|
+
media.posterKey,
|
|
967
|
+
emission.inlinePosterPath,
|
|
968
|
+
);
|
|
969
|
+
if (posterFile) files.push(posterFile);
|
|
970
|
+
}
|
|
971
|
+
}
|
|
435
972
|
}
|
|
436
973
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
parts.push("");
|
|
974
|
+
return files;
|
|
975
|
+
}
|
|
440
976
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
977
|
+
/**
|
|
978
|
+
* Read a media record's bytes from storage and return an ExportFile so
|
|
979
|
+
* they can be bundled next to the post as a Hugo page resource. Returns
|
|
980
|
+
* null when storage is unavailable or the object cannot be read, in
|
|
981
|
+
* which case the front matter entry still points at the resource name
|
|
982
|
+
* and the CLI's pull-media step (or a later sync) can fill it in.
|
|
983
|
+
*/
|
|
984
|
+
async function readMediaResourceFile(
|
|
985
|
+
storage: StorageDriver | null,
|
|
986
|
+
storageKey: string,
|
|
987
|
+
bundlePath: string,
|
|
988
|
+
): Promise<ExportFile | null> {
|
|
989
|
+
if (!storage) return null;
|
|
990
|
+
try {
|
|
991
|
+
const bytes = await readStorageObjectBytes(storage, storageKey);
|
|
992
|
+
if (!bytes) return null;
|
|
993
|
+
return { path: bundlePath, content: bytes };
|
|
994
|
+
} catch {
|
|
995
|
+
return null;
|
|
996
|
+
}
|
|
997
|
+
}
|
|
445
998
|
|
|
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 += " -->";
|
|
999
|
+
// ---------------------------------------------------------------------------
|
|
1000
|
+
// Section + landing pages
|
|
1001
|
+
// ---------------------------------------------------------------------------
|
|
465
1002
|
|
|
466
|
-
|
|
467
|
-
|
|
1003
|
+
async function buildHomeSection(siteConfig: SiteConfig): Promise<string> {
|
|
1004
|
+
const frontMatter: HugoFrontMatter = {
|
|
1005
|
+
title: siteConfig.siteName,
|
|
1006
|
+
type: "home",
|
|
1007
|
+
};
|
|
1008
|
+
return `${await formatFrontMatter(frontMatter)}\n`;
|
|
1009
|
+
}
|
|
468
1010
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
].filter(Boolean);
|
|
477
|
-
if (replyBlocks.length > 0) {
|
|
478
|
-
parts.push(replyBlocks.join("\n\n"));
|
|
479
|
-
}
|
|
480
|
-
}
|
|
1011
|
+
async function buildCollectionsSection(): Promise<string> {
|
|
1012
|
+
const frontMatter: HugoFrontMatter = {
|
|
1013
|
+
title: "Collections",
|
|
1014
|
+
type: "collections",
|
|
1015
|
+
};
|
|
1016
|
+
return `${await formatFrontMatter(frontMatter)}\n`;
|
|
1017
|
+
}
|
|
481
1018
|
|
|
482
|
-
|
|
1019
|
+
async function buildArchiveSection(): Promise<string> {
|
|
1020
|
+
const frontMatter: HugoFrontMatter = {
|
|
1021
|
+
title: "Archive",
|
|
1022
|
+
type: "archive",
|
|
1023
|
+
// Opt into Atom output at /archive/index.xml.
|
|
1024
|
+
outputs: ["html", "rss"],
|
|
1025
|
+
};
|
|
1026
|
+
return `${await formatFrontMatter(frontMatter)}\n`;
|
|
483
1027
|
}
|
|
484
1028
|
|
|
485
|
-
function
|
|
486
|
-
const
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
1029
|
+
async function buildFeaturedSection(): Promise<string> {
|
|
1030
|
+
const frontMatter: HugoFrontMatter = {
|
|
1031
|
+
title: "Featured",
|
|
1032
|
+
type: "featured",
|
|
1033
|
+
// Opt into Atom output at /featured/index.xml.
|
|
1034
|
+
outputs: ["html", "rss"],
|
|
1035
|
+
};
|
|
1036
|
+
return `${await formatFrontMatter(frontMatter)}\n`;
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
async function buildCollectionSection(
|
|
1040
|
+
collection: Collection,
|
|
1041
|
+
slug: string,
|
|
1042
|
+
entryCount: number,
|
|
1043
|
+
): Promise<string> {
|
|
1044
|
+
const frontMatter: HugoFrontMatter = {
|
|
1045
|
+
title: collection.title,
|
|
1046
|
+
slug,
|
|
1047
|
+
type: "collection",
|
|
1048
|
+
summary_text: collection.description ?? undefined,
|
|
1049
|
+
sort_order: collection.sortOrder,
|
|
1050
|
+
entry_count: entryCount,
|
|
1051
|
+
// Opt into Atom output at /{slug}/index.xml.
|
|
1052
|
+
outputs: ["html", "rss"],
|
|
1053
|
+
};
|
|
1054
|
+
return `${await formatFrontMatter(frontMatter)}\n`;
|
|
498
1055
|
}
|
|
499
1056
|
|
|
1057
|
+
// ---------------------------------------------------------------------------
|
|
1058
|
+
// Summary extraction (kept from the previous exporter)
|
|
1059
|
+
// ---------------------------------------------------------------------------
|
|
1060
|
+
|
|
500
1061
|
function normalizeArchiveText(text: string | null | undefined): string {
|
|
501
1062
|
return (text ?? "").replace(/\s+/g, " ").trim();
|
|
502
1063
|
}
|
|
503
1064
|
|
|
504
1065
|
function getArchiveSummaryText(post: Post): string | null {
|
|
1066
|
+
// `summary_text` is a plain-text projection of the post's primary content,
|
|
1067
|
+
// used for `<meta name="description">`, `og:description`, and archive/card
|
|
1068
|
+
// fallbacks when the rendered body is empty. The candidate list differs
|
|
1069
|
+
// per format so the description reflects the right "primary content":
|
|
1070
|
+
//
|
|
1071
|
+
// - Quote: body (commentary) → quoteText. Quotes have no title, so we
|
|
1072
|
+
// fall back to the quote itself to guarantee a meaningful description.
|
|
1073
|
+
// - Link: body only. Links have a title + domain; the URL is already
|
|
1074
|
+
// serialized as `link_url` and rendered as a domain badge, so using
|
|
1075
|
+
// it as `summary_text` would duplicate that information.
|
|
1076
|
+
// - Note: body only. If the body is empty, there's nothing to describe.
|
|
505
1077
|
const candidates =
|
|
506
1078
|
post.format === "quote"
|
|
507
|
-
? [post.summary, post.
|
|
508
|
-
: [post.summary, post.bodyText
|
|
1079
|
+
? [post.summary, post.bodyText, post.quoteText]
|
|
1080
|
+
: [post.summary, post.bodyText];
|
|
509
1081
|
|
|
510
1082
|
for (const candidate of candidates) {
|
|
511
1083
|
const normalized = normalizeArchiveText(candidate);
|
|
512
1084
|
if (normalized) return normalized;
|
|
513
1085
|
}
|
|
514
|
-
|
|
515
1086
|
return null;
|
|
516
1087
|
}
|
|
517
1088
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
textAttachmentContents: Map<string, TextAttachmentContent>,
|
|
522
|
-
): string {
|
|
523
|
-
if (mediaList.length === 0) return "";
|
|
1089
|
+
// ---------------------------------------------------------------------------
|
|
1090
|
+
// Collection metrics + directory items (kept from the previous exporter)
|
|
1091
|
+
// ---------------------------------------------------------------------------
|
|
524
1092
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
1093
|
+
function formatCollectionActivityLabel(
|
|
1094
|
+
timestamp: number | undefined,
|
|
1095
|
+
): string | null {
|
|
1096
|
+
if (typeof timestamp !== "number") {
|
|
1097
|
+
return null;
|
|
1098
|
+
}
|
|
530
1099
|
|
|
531
|
-
return
|
|
1100
|
+
return formatRelativeAge(timestamp);
|
|
532
1101
|
}
|
|
533
1102
|
|
|
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>`;
|
|
1103
|
+
function formatCollectionActivityIso(
|
|
1104
|
+
timestamp: number | undefined,
|
|
1105
|
+
): string | null {
|
|
1106
|
+
if (typeof timestamp !== "number") {
|
|
1107
|
+
return null;
|
|
566
1108
|
}
|
|
567
1109
|
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
throw new Error(`Attachment ${media.id} is missing an export URL`);
|
|
571
|
-
}
|
|
1110
|
+
return toISOString(timestamp);
|
|
1111
|
+
}
|
|
572
1112
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
1113
|
+
/**
|
|
1114
|
+
* Group-aware sequence labels for the exported collection directory.
|
|
1115
|
+
*
|
|
1116
|
+
* Mirrors the main site's `computeSequenceLabels` in
|
|
1117
|
+
* `ui/shared/CollectionDirectory.tsx` so Hugo exports render the same numeric
|
|
1118
|
+
* indices (e.g. "00" "01" under the first divider, "10" "11" under the
|
|
1119
|
+
* second). Dividers themselves receive an empty string — their slot is
|
|
1120
|
+
* reserved so the returned array is index-aligned with the source list.
|
|
1121
|
+
*/
|
|
1122
|
+
function computeCollectionDirectorySequenceLabels(
|
|
1123
|
+
items: readonly ExportCollectionDirectorySourceItem[],
|
|
1124
|
+
): string[] {
|
|
1125
|
+
const isContentItem = (item: ExportCollectionDirectorySourceItem) =>
|
|
1126
|
+
(item.type === "collection" && item.collection) ||
|
|
1127
|
+
(item.type === "link" && item.label && item.url);
|
|
1128
|
+
|
|
1129
|
+
const groupSizes: number[] = [];
|
|
1130
|
+
let seenDivider = false;
|
|
1131
|
+
let ungroupedCount = 0;
|
|
1132
|
+
for (const item of items) {
|
|
1133
|
+
if (item.type === "divider") {
|
|
1134
|
+
seenDivider = true;
|
|
1135
|
+
groupSizes.push(0);
|
|
1136
|
+
} else if (isContentItem(item)) {
|
|
1137
|
+
if (seenDivider) {
|
|
1138
|
+
const lastGroupIndex = groupSizes.length - 1;
|
|
1139
|
+
const lastGroupSize = groupSizes[lastGroupIndex];
|
|
1140
|
+
if (lastGroupSize !== undefined) {
|
|
1141
|
+
groupSizes[lastGroupIndex] = lastGroupSize + 1;
|
|
1142
|
+
}
|
|
1143
|
+
} else {
|
|
1144
|
+
ungroupedCount += 1;
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
580
1147
|
}
|
|
581
1148
|
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
${caption}
|
|
592
|
-
</figure>`;
|
|
593
|
-
}
|
|
1149
|
+
const hasGroups = groupSizes.length > 0;
|
|
1150
|
+
const maxGroupIndex = Math.max(0, groupSizes.length - 1);
|
|
1151
|
+
const groupWidth = hasGroups
|
|
1152
|
+
? Math.max(1, maxGroupIndex.toString(36).length)
|
|
1153
|
+
: 0;
|
|
1154
|
+
const ungroupedItemWidth = Math.max(
|
|
1155
|
+
2,
|
|
1156
|
+
String(Math.max(0, ungroupedCount - 1)).length,
|
|
1157
|
+
);
|
|
594
1158
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
1159
|
+
const labels: string[] = [];
|
|
1160
|
+
let groupIndex = -1;
|
|
1161
|
+
let itemIndex = 0;
|
|
1162
|
+
|
|
1163
|
+
for (const item of items) {
|
|
1164
|
+
if (item.type === "divider") {
|
|
1165
|
+
groupIndex += 1;
|
|
1166
|
+
itemIndex = 0;
|
|
1167
|
+
labels.push("");
|
|
1168
|
+
} else if (isContentItem(item)) {
|
|
1169
|
+
if (hasGroups) {
|
|
1170
|
+
const g = Math.max(0, groupIndex)
|
|
1171
|
+
.toString(36)
|
|
1172
|
+
.padStart(groupWidth, "0");
|
|
1173
|
+
const i = itemIndex.toString(36);
|
|
1174
|
+
labels.push(g + i);
|
|
1175
|
+
} else {
|
|
1176
|
+
labels.push(String(itemIndex).padStart(ungroupedItemWidth, "0"));
|
|
1177
|
+
}
|
|
1178
|
+
itemIndex += 1;
|
|
1179
|
+
} else {
|
|
1180
|
+
labels.push("");
|
|
1181
|
+
}
|
|
601
1182
|
}
|
|
602
1183
|
|
|
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>`;
|
|
1184
|
+
return labels;
|
|
612
1185
|
}
|
|
613
1186
|
|
|
614
|
-
function
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
1187
|
+
function buildExportedCollectionDirectoryItems(
|
|
1188
|
+
items: readonly ExportCollectionDirectorySourceItem[],
|
|
1189
|
+
collectionSlugMap: Map<string, string>,
|
|
1190
|
+
collectionMetrics: Map<string, ExportedCollectionMetrics>,
|
|
1191
|
+
): ExportedCollectionDirectoryItem[] {
|
|
1192
|
+
const sequenceLabels = computeCollectionDirectorySequenceLabels(items);
|
|
1193
|
+
const exportedItems: ExportedCollectionDirectoryItem[] = [];
|
|
1194
|
+
|
|
1195
|
+
items.forEach((item, index) => {
|
|
1196
|
+
if (item.type === "divider") {
|
|
1197
|
+
exportedItems.push({
|
|
1198
|
+
type: "divider",
|
|
1199
|
+
label: item.label ?? null,
|
|
1200
|
+
});
|
|
1201
|
+
return;
|
|
1202
|
+
}
|
|
620
1203
|
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
1204
|
+
if (item.type === "link") {
|
|
1205
|
+
if (!item.label || !item.url) {
|
|
1206
|
+
return;
|
|
1207
|
+
}
|
|
624
1208
|
|
|
625
|
-
|
|
626
|
-
|
|
1209
|
+
const description = item.description?.trim();
|
|
1210
|
+
exportedItems.push({
|
|
1211
|
+
type: "link",
|
|
1212
|
+
sequence: sequenceLabels[index] ?? "",
|
|
1213
|
+
label: item.label,
|
|
1214
|
+
url: item.url,
|
|
1215
|
+
descriptionHtml: description ? renderMarkdown(description) : null,
|
|
1216
|
+
});
|
|
1217
|
+
return;
|
|
1218
|
+
}
|
|
627
1219
|
|
|
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
|
-
);
|
|
1220
|
+
const collection = item.collection;
|
|
1221
|
+
if (!collection?.id) {
|
|
1222
|
+
return;
|
|
638
1223
|
}
|
|
639
1224
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
1225
|
+
const slug = collectionSlugMap.get(collection.id) ?? collection.slug;
|
|
1226
|
+
if (!slug) {
|
|
1227
|
+
return;
|
|
1228
|
+
}
|
|
1229
|
+
const metrics = collectionMetrics.get(collection.id);
|
|
1230
|
+
const activityTimestamp =
|
|
1231
|
+
metrics?.recentActivityAt ?? collection.recentActivityAt;
|
|
1232
|
+
|
|
1233
|
+
const collectionDescription = collection.description?.trim();
|
|
1234
|
+
exportedItems.push({
|
|
1235
|
+
type: "collection",
|
|
1236
|
+
sequence: sequenceLabels[index] ?? "",
|
|
1237
|
+
slug,
|
|
1238
|
+
title: collection.title || slug,
|
|
1239
|
+
descriptionHtml: collectionDescription
|
|
1240
|
+
? renderMarkdown(collectionDescription)
|
|
1241
|
+
: null,
|
|
1242
|
+
entryCount:
|
|
1243
|
+
metrics?.postCount ??
|
|
1244
|
+
(typeof collection.postCount === "number"
|
|
1245
|
+
? collection.postCount
|
|
1246
|
+
: undefined),
|
|
1247
|
+
recentActivityLabel: formatCollectionActivityLabel(activityTimestamp),
|
|
1248
|
+
recentActivityIso: formatCollectionActivityIso(activityTimestamp),
|
|
1249
|
+
});
|
|
1250
|
+
});
|
|
1251
|
+
|
|
1252
|
+
return exportedItems;
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
function buildExportedCollectionMetrics(
|
|
1256
|
+
collections: readonly Collection[],
|
|
1257
|
+
posts: readonly Post[],
|
|
1258
|
+
collectionsByPost: ReadonlyMap<string, readonly Collection[]>,
|
|
1259
|
+
): Map<string, ExportedCollectionMetrics> {
|
|
1260
|
+
const metrics = new Map<string, ExportedCollectionMetrics>();
|
|
1261
|
+
|
|
1262
|
+
for (const collection of collections) {
|
|
1263
|
+
metrics.set(collection.id, {
|
|
1264
|
+
postCount: 0,
|
|
1265
|
+
recentActivityAt: collection.updatedAt,
|
|
1266
|
+
});
|
|
656
1267
|
}
|
|
657
1268
|
|
|
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");
|
|
1269
|
+
for (const post of posts) {
|
|
1270
|
+
if (post.deletedAt !== null) {
|
|
1271
|
+
continue;
|
|
1272
|
+
}
|
|
1273
|
+
// Drafts and private posts are excluded — they won't reach Hugo.
|
|
1274
|
+
if (post.status === "draft" || post.visibility === "private") {
|
|
1275
|
+
continue;
|
|
1276
|
+
}
|
|
1277
|
+
// Replies roll up into their thread root for directory metrics.
|
|
1278
|
+
if (post.replyToId !== null) {
|
|
1279
|
+
continue;
|
|
1280
|
+
}
|
|
693
1281
|
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
1282
|
+
const activityAt =
|
|
1283
|
+
post.lastActivityAt ??
|
|
1284
|
+
post.publishedAt ??
|
|
1285
|
+
post.updatedAt ??
|
|
1286
|
+
post.createdAt;
|
|
1287
|
+
const postCollections = collectionsByPost.get(post.id) ?? [];
|
|
1288
|
+
|
|
1289
|
+
for (const collection of postCollections) {
|
|
1290
|
+
const current = metrics.get(collection.id);
|
|
1291
|
+
if (!current) {
|
|
1292
|
+
continue;
|
|
1293
|
+
}
|
|
697
1294
|
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
if (!content) {
|
|
705
|
-
throw new Error(
|
|
706
|
-
`Text attachment ${media.id} content is unavailable for export`,
|
|
1295
|
+
if (current.postCount === 0) {
|
|
1296
|
+
current.recentActivityAt = activityAt;
|
|
1297
|
+
} else {
|
|
1298
|
+
current.recentActivityAt = Math.max(
|
|
1299
|
+
current.recentActivityAt,
|
|
1300
|
+
activityAt,
|
|
707
1301
|
);
|
|
708
1302
|
}
|
|
709
|
-
|
|
710
|
-
}
|
|
711
|
-
|
|
1303
|
+
current.postCount += 1;
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
712
1306
|
|
|
713
|
-
return
|
|
1307
|
+
return metrics;
|
|
714
1308
|
}
|
|
715
1309
|
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
.replace(/"/g, '\\"')
|
|
720
|
-
.replace(/\n/g, "\\n");
|
|
721
|
-
}
|
|
1310
|
+
// ---------------------------------------------------------------------------
|
|
1311
|
+
// Nav item resolution
|
|
1312
|
+
// ---------------------------------------------------------------------------
|
|
722
1313
|
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
1314
|
+
/**
|
|
1315
|
+
* System nav items on the main site store an empty `label` in the DB and
|
|
1316
|
+
* resolve their display text at render time through i18n
|
|
1317
|
+
* (`getNavItemDisplayLabel`). The Hugo export has no i18n runtime, so fall
|
|
1318
|
+
* back to these English defaults when serializing to `data/jant.toml`.
|
|
1319
|
+
* Users can still override by setting a custom label on the nav item.
|
|
1320
|
+
*/
|
|
1321
|
+
const SYSTEM_NAV_FALLBACK_LABELS: Record<string, string> = {
|
|
1322
|
+
latest: "Latest",
|
|
1323
|
+
featured: "Featured",
|
|
1324
|
+
collections: "Collections",
|
|
1325
|
+
archive: "Archive",
|
|
1326
|
+
rss: "RSS",
|
|
1327
|
+
settings: "Settings",
|
|
1328
|
+
};
|
|
1329
|
+
|
|
1330
|
+
function resolveNavItemLabel(item: SiteConfig["navItems"][number]): string {
|
|
1331
|
+
if (item.label) return item.label;
|
|
1332
|
+
if (item.systemKey) {
|
|
1333
|
+
const fallback = SYSTEM_NAV_FALLBACK_LABELS[item.systemKey];
|
|
1334
|
+
if (fallback) return fallback;
|
|
1335
|
+
}
|
|
1336
|
+
return item.label;
|
|
728
1337
|
}
|
|
729
1338
|
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
1339
|
+
/**
|
|
1340
|
+
* Resolve a nav item's final href for the Hugo export.
|
|
1341
|
+
*
|
|
1342
|
+
* Mirrors the runtime logic in `lib/view.ts:toNavItemView`. System URLs
|
|
1343
|
+
* stored in the DB ("/latest", "/featured") are not real routes — they get
|
|
1344
|
+
* rewritten to "/" when they match `homeDefaultView`, otherwise they
|
|
1345
|
+
* resolve to the dedicated path.
|
|
1346
|
+
*
|
|
1347
|
+
* The "rss" system nav item points at whichever Atom feed the site has
|
|
1348
|
+
* configured as its main feed: `mainRssFeed === "featured"` → the featured
|
|
1349
|
+
* section feed at `/featured/index.xml`, otherwise the home feed at
|
|
1350
|
+
* `/index.xml` (which mirrors the homepage's "latest" timeline).
|
|
1351
|
+
*/
|
|
1352
|
+
function resolveNavItemUrl(
|
|
1353
|
+
item: SiteConfig["navItems"][number],
|
|
1354
|
+
homeDefaultView: string,
|
|
1355
|
+
mainRssFeed: string,
|
|
733
1356
|
): string {
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
1357
|
+
if (item.systemKey === "latest") {
|
|
1358
|
+
return homeDefaultView === "latest" ? "/" : "/latest/";
|
|
1359
|
+
}
|
|
1360
|
+
if (item.systemKey === "featured") {
|
|
1361
|
+
return homeDefaultView === "featured" ? "/" : "/featured/";
|
|
1362
|
+
}
|
|
1363
|
+
if (item.systemKey === "collections") return "/collections/";
|
|
1364
|
+
if (item.systemKey === "archive") return "/archive/";
|
|
1365
|
+
if (item.systemKey === "rss") {
|
|
1366
|
+
return mainRssFeed === "featured" ? "/featured/index.xml" : "/index.xml";
|
|
1367
|
+
}
|
|
1368
|
+
return item.url;
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
// ---------------------------------------------------------------------------
|
|
1372
|
+
// hugo.toml + data TOMLs
|
|
1373
|
+
// ---------------------------------------------------------------------------
|
|
1374
|
+
|
|
1375
|
+
/** Escape a string for use inside a TOML double-quoted value. */
|
|
1376
|
+
function escapeTomlString(value: string): string {
|
|
1377
|
+
return value
|
|
1378
|
+
.replace(/\\/g, "\\\\")
|
|
1379
|
+
.replace(/"/g, '\\"')
|
|
1380
|
+
.replace(/\r/g, "\\r")
|
|
1381
|
+
.replace(/\n/g, "\\n");
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
function buildHugoToml(config: SiteConfig): string {
|
|
1385
|
+
const baseUrl = (config.siteUrl || "https://example.com").replace(/\/+$/, "");
|
|
1386
|
+
// Hugo requires language codes to be all lowercase (it rejects the BCP-47
|
|
1387
|
+
// casing `zh-Hant` / `zh-Hans` with "must be all lower case and no spaces").
|
|
1388
|
+
const language = config.siteLanguage.toLowerCase();
|
|
1389
|
+
const parts: string[] = [
|
|
1390
|
+
`baseURL = "${escapeTomlString(baseUrl)}/"`,
|
|
1391
|
+
`title = "${escapeTomlString(config.siteName)}"`,
|
|
1392
|
+
`languageCode = "${escapeTomlString(language)}"`,
|
|
1393
|
+
`defaultContentLanguage = "${escapeTomlString(language)}"`,
|
|
1394
|
+
'theme = "jant"',
|
|
1395
|
+
`paginate = ${config.pageSize}`,
|
|
1396
|
+
"enableRobotsTXT = true",
|
|
1397
|
+
// Disable Hugo's built-in taxonomies — jant has no tags or categories
|
|
1398
|
+
// and the default empty /tags/ and /categories/ pages are noise. This
|
|
1399
|
+
// must stay at the root (before any `[table]` header) so TOML doesn't
|
|
1400
|
+
// nest it under the previous table.
|
|
1401
|
+
"disableKinds = ['taxonomy', 'term']",
|
|
1402
|
+
"",
|
|
1403
|
+
"[permalinks]",
|
|
1404
|
+
' post = "/:slug/"',
|
|
1405
|
+
"",
|
|
1406
|
+
"[markup]",
|
|
1407
|
+
" [markup.goldmark]",
|
|
1408
|
+
" [markup.goldmark.renderer]",
|
|
1409
|
+
" unsafe = true",
|
|
742
1410
|
"",
|
|
743
|
-
|
|
1411
|
+
// Emit an Atom 2005 feed at the site root (/index.xml). Per-section
|
|
1412
|
+
// feeds (featured, archive, each collection) are opted in via each
|
|
1413
|
+
// section's front matter `outputs: ["html", "rss"]` — enabling RSS on
|
|
1414
|
+
// `section` globally here would also create a feed at every root post's
|
|
1415
|
+
// URL, since root posts are themselves branch bundles / sections.
|
|
1416
|
+
// Override the built-in RSS output format to emit Atom instead of
|
|
1417
|
+
// RSS 2.0 so the wire format mirrors the main site's `lib/feed.ts`.
|
|
1418
|
+
"[outputs]",
|
|
1419
|
+
' home = ["html", "rss"]',
|
|
1420
|
+
// Hugo's default for sections is ["html", "rss"] — without overriding
|
|
1421
|
+
// it here every root post (which is a section) would get its own
|
|
1422
|
+
// /{slug}/index.xml. Turn sections off by default and re-enable RSS
|
|
1423
|
+
// on just featured, archive, and each collection via per-section
|
|
1424
|
+
// front matter `outputs: ["html", "rss"]`.
|
|
1425
|
+
' section = ["html"]',
|
|
744
1426
|
"",
|
|
745
|
-
"[
|
|
1427
|
+
"[outputFormats]",
|
|
1428
|
+
" [outputFormats.RSS]",
|
|
1429
|
+
' mediaType = "application/atom+xml"',
|
|
1430
|
+
' baseName = "index"',
|
|
1431
|
+
// Use text/template (not html/template) so Hugo doesn't HTML-escape
|
|
1432
|
+
// the XML prologue, CDATA markers, or tag literals. Every dynamic
|
|
1433
|
+
// value inside the template passes through `transform.XMLEscape`.
|
|
1434
|
+
" isPlainText = true",
|
|
1435
|
+
' rel = "alternate"',
|
|
1436
|
+
"",
|
|
1437
|
+
"[mediaTypes]",
|
|
1438
|
+
' [mediaTypes."application/atom+xml"]',
|
|
1439
|
+
' suffixes = ["xml"]',
|
|
1440
|
+
"",
|
|
1441
|
+
"[params]",
|
|
1442
|
+
` description = "${escapeTomlString(config.siteDescription)}"`,
|
|
1443
|
+
` home_default_view = "${escapeTomlString(config.homeDefaultView)}"`,
|
|
1444
|
+
` main_rss_feed = "${escapeTomlString(config.mainRssFeed)}"`,
|
|
1445
|
+
` show_jant_branding_on_home = ${config.showJantBrandingOnHome}`,
|
|
1446
|
+
` show_header_avatar = ${config.showHeaderAvatar}`,
|
|
1447
|
+
` noindex = ${config.noindex}`,
|
|
1448
|
+
` theme_id = "${escapeTomlString(config.themeId)}"`,
|
|
1449
|
+
` default_theme_id = "${escapeTomlString(config.defaultThemeId)}"`,
|
|
1450
|
+
` font_theme_id = "${escapeTomlString(config.fontThemeId)}"`,
|
|
1451
|
+
` theme_mode = "${escapeTomlString(config.themeMode)}"`,
|
|
1452
|
+
` page_size = ${config.pageSize}`,
|
|
1453
|
+
` archive_page_size = ${config.archivePageSize}`,
|
|
1454
|
+
` rss_feed_limit = ${config.rssFeedLimit}`,
|
|
1455
|
+
];
|
|
1456
|
+
if (config.siteAvatarUrl) {
|
|
1457
|
+
parts.push(
|
|
1458
|
+
` site_avatar_url = "${escapeTomlString(config.siteAvatarUrl)}"`,
|
|
1459
|
+
);
|
|
1460
|
+
}
|
|
1461
|
+
if (config.faviconVersion) {
|
|
1462
|
+
parts.push(
|
|
1463
|
+
` favicon_version = "${escapeTomlString(config.faviconVersion)}"`,
|
|
1464
|
+
);
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
return `${parts.join("\n")}\n`;
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
function buildJantDataToml(
|
|
1471
|
+
config: SiteConfig,
|
|
1472
|
+
iconAssets: SiteIconAssets,
|
|
1473
|
+
directoryItems: readonly ExportedCollectionDirectoryItem[],
|
|
1474
|
+
): string {
|
|
1475
|
+
const footerHtml = config.siteFooter ? renderMarkdown(config.siteFooter) : "";
|
|
1476
|
+
const parts: string[] = [
|
|
746
1477
|
'format = "jant-site"',
|
|
747
1478
|
"version = 1",
|
|
748
|
-
`generated_at = "${
|
|
749
|
-
""
|
|
750
|
-
"
|
|
751
|
-
`
|
|
752
|
-
`
|
|
1479
|
+
`generated_at = "${escapeTomlString(toISOString(Math.floor(Date.now() / 1000)))}"`,
|
|
1480
|
+
`site_name = "${escapeTomlString(config.siteName)}"`,
|
|
1481
|
+
`site_description = "${escapeTomlString(config.siteDescription)}"`,
|
|
1482
|
+
`site_language = "${escapeTomlString(config.siteLanguage)}"`,
|
|
1483
|
+
`home_default_view = "${escapeTomlString(config.homeDefaultView)}"`,
|
|
1484
|
+
`main_rss_feed = "${escapeTomlString(config.mainRssFeed)}"`,
|
|
753
1485
|
`show_jant_branding_on_home = ${config.showJantBrandingOnHome}`,
|
|
754
1486
|
`show_header_avatar = ${config.showHeaderAvatar}`,
|
|
755
1487
|
`noindex = ${config.noindex}`,
|
|
756
1488
|
`site_avatar_mode = "${config.siteAvatarUrl ? "custom" : "none"}"`,
|
|
757
1489
|
`favicon_mode = "${iconAssets.faviconMode}"`,
|
|
758
1490
|
`apple_touch_mode = "${iconAssets.appleTouchMode}"`,
|
|
759
|
-
|
|
760
|
-
`
|
|
761
|
-
`
|
|
762
|
-
`
|
|
763
|
-
`
|
|
1491
|
+
`theme_id = "${escapeTomlString(config.themeId)}"`,
|
|
1492
|
+
`default_theme_id = "${escapeTomlString(config.defaultThemeId)}"`,
|
|
1493
|
+
`font_theme_id = "${escapeTomlString(config.fontThemeId)}"`,
|
|
1494
|
+
`theme_mode = "${escapeTomlString(config.themeMode)}"`,
|
|
1495
|
+
`page_size = ${config.pageSize}`,
|
|
1496
|
+
`archive_page_size = ${config.archivePageSize}`,
|
|
1497
|
+
`rss_feed_limit = ${config.rssFeedLimit}`,
|
|
1498
|
+
'favicon_path = "/favicon.ico"',
|
|
1499
|
+
'apple_touch_icon_path = "/apple-touch-icon.png"',
|
|
764
1500
|
];
|
|
765
|
-
|
|
766
1501
|
if (config.siteAvatarUrl) {
|
|
767
|
-
parts.push(`site_avatar_url = "${
|
|
1502
|
+
parts.push(`site_avatar_url = "${escapeTomlString(config.siteAvatarUrl)}"`);
|
|
768
1503
|
}
|
|
769
1504
|
if (config.faviconVersion) {
|
|
770
|
-
parts.push(
|
|
1505
|
+
parts.push(
|
|
1506
|
+
`favicon_version = "${escapeTomlString(config.faviconVersion)}"`,
|
|
1507
|
+
);
|
|
771
1508
|
}
|
|
772
1509
|
if (footerHtml) {
|
|
773
|
-
parts.push(`site_footer_html = "${
|
|
1510
|
+
parts.push(`site_footer_html = "${escapeTomlString(footerHtml)}"`);
|
|
774
1511
|
}
|
|
775
1512
|
if (config.siteFooter) {
|
|
776
|
-
parts.push(
|
|
1513
|
+
parts.push(
|
|
1514
|
+
`site_footer_markdown = "${escapeTomlString(config.siteFooter)}"`,
|
|
1515
|
+
);
|
|
777
1516
|
}
|
|
778
1517
|
|
|
779
1518
|
for (const item of config.navItems) {
|
|
1519
|
+
// `settings` is authenticated-only and has no corresponding page in the
|
|
1520
|
+
// static Hugo site — drop it at export time so it never shows up in nav.
|
|
1521
|
+
if (item.systemKey === "settings") continue;
|
|
780
1522
|
parts.push("");
|
|
781
|
-
parts.push("[[
|
|
782
|
-
parts.push(`type = "${
|
|
783
|
-
parts.push(`label = "${
|
|
784
|
-
parts.push(
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
}
|
|
1523
|
+
parts.push("[[nav]]");
|
|
1524
|
+
parts.push(`type = "${escapeTomlString(item.type)}"`);
|
|
1525
|
+
parts.push(`label = "${escapeTomlString(resolveNavItemLabel(item))}"`);
|
|
1526
|
+
parts.push(
|
|
1527
|
+
`url = "${escapeTomlString(resolveNavItemUrl(item, config.homeDefaultView, config.mainRssFeed))}"`,
|
|
1528
|
+
);
|
|
1529
|
+
parts.push(`system_key = "${escapeTomlString(item.systemKey ?? "")}"`);
|
|
1530
|
+
parts.push(`placement = "${escapeTomlString(item.placement ?? "header")}"`);
|
|
788
1531
|
}
|
|
789
1532
|
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
1533
|
+
for (const item of directoryItems) {
|
|
1534
|
+
parts.push("");
|
|
1535
|
+
parts.push("[[directory]]");
|
|
1536
|
+
parts.push(`type = "${escapeTomlString(item.type)}"`);
|
|
1537
|
+
if (item.type === "collection") {
|
|
1538
|
+
parts.push(`sequence = "${escapeTomlString(item.sequence)}"`);
|
|
1539
|
+
parts.push(`slug = "${escapeTomlString(item.slug)}"`);
|
|
1540
|
+
parts.push(`title = "${escapeTomlString(item.title)}"`);
|
|
1541
|
+
if (item.descriptionHtml) {
|
|
1542
|
+
parts.push(
|
|
1543
|
+
`description_html = "${escapeTomlString(item.descriptionHtml)}"`,
|
|
1544
|
+
);
|
|
1545
|
+
}
|
|
1546
|
+
if (typeof item.entryCount === "number") {
|
|
1547
|
+
parts.push(`entry_count = ${item.entryCount}`);
|
|
1548
|
+
}
|
|
1549
|
+
if (item.recentActivityLabel) {
|
|
1550
|
+
parts.push(
|
|
1551
|
+
`recent_activity_label = "${escapeTomlString(item.recentActivityLabel)}"`,
|
|
1552
|
+
);
|
|
1553
|
+
}
|
|
1554
|
+
if (item.recentActivityIso) {
|
|
1555
|
+
parts.push(
|
|
1556
|
+
`recent_activity_iso = "${escapeTomlString(item.recentActivityIso)}"`,
|
|
1557
|
+
);
|
|
1558
|
+
}
|
|
1559
|
+
} else if (item.type === "divider") {
|
|
1560
|
+
if (item.label !== null) {
|
|
1561
|
+
parts.push(`label = "${escapeTomlString(item.label)}"`);
|
|
1562
|
+
}
|
|
1563
|
+
} else {
|
|
1564
|
+
parts.push(`sequence = "${escapeTomlString(item.sequence)}"`);
|
|
1565
|
+
parts.push(`label = "${escapeTomlString(item.label)}"`);
|
|
1566
|
+
parts.push(`url = "${escapeTomlString(item.url)}"`);
|
|
1567
|
+
if (item.descriptionHtml) {
|
|
1568
|
+
parts.push(
|
|
1569
|
+
`description_html = "${escapeTomlString(item.descriptionHtml)}"`,
|
|
1570
|
+
);
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
798
1574
|
|
|
799
|
-
return `${parts.join("\n")}
|
|
800
|
-
`;
|
|
1575
|
+
return `${parts.join("\n")}\n`;
|
|
801
1576
|
}
|
|
802
1577
|
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
1578
|
+
// ---------------------------------------------------------------------------
|
|
1579
|
+
// README + .gitignore
|
|
1580
|
+
// ---------------------------------------------------------------------------
|
|
1581
|
+
|
|
1582
|
+
function buildGitignore(): string {
|
|
1583
|
+
return `# Hugo build output
|
|
1584
|
+
public/
|
|
1585
|
+
resources/
|
|
1586
|
+
.hugo_build.lock
|
|
1587
|
+
|
|
1588
|
+
# OS
|
|
1589
|
+
.DS_Store
|
|
1590
|
+
Thumbs.db
|
|
810
1591
|
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
+++
|
|
1592
|
+
# Editors
|
|
1593
|
+
.vscode/
|
|
1594
|
+
.idea/
|
|
1595
|
+
*.swp
|
|
816
1596
|
`;
|
|
817
1597
|
}
|
|
818
1598
|
|
|
819
1599
|
function buildReadme(siteName: string): string {
|
|
820
|
-
return `# ${siteName} —
|
|
1600
|
+
return `# ${siteName} — Hugo Export
|
|
821
1601
|
|
|
822
|
-
This is a static site exported from [Jant](https://github.com/jant-me/jant), ready to build with [
|
|
1602
|
+
This is a static site exported from [Jant](https://github.com/jant-me/jant), ready to build with [Hugo](https://gohugo.io/).
|
|
823
1603
|
|
|
824
|
-
## Install
|
|
1604
|
+
## Install Hugo
|
|
1605
|
+
|
|
1606
|
+
This export targets Hugo **extended 0.160.1+**.
|
|
825
1607
|
|
|
826
1608
|
**macOS (Homebrew):**
|
|
827
1609
|
|
|
828
1610
|
\`\`\`sh
|
|
829
|
-
brew install
|
|
1611
|
+
brew install hugo
|
|
830
1612
|
\`\`\`
|
|
831
1613
|
|
|
832
1614
|
**Windows (Scoop):**
|
|
833
1615
|
|
|
834
1616
|
\`\`\`sh
|
|
835
|
-
scoop install
|
|
1617
|
+
scoop install hugo-extended
|
|
836
1618
|
\`\`\`
|
|
837
1619
|
|
|
838
|
-
**Linux
|
|
839
|
-
|
|
840
|
-
\`\`\`sh
|
|
841
|
-
snap install zola --edge
|
|
842
|
-
\`\`\`
|
|
1620
|
+
**Linux:**
|
|
843
1621
|
|
|
844
|
-
|
|
1622
|
+
Download the extended build from <https://github.com/gohugoio/hugo/releases>.
|
|
845
1623
|
|
|
846
|
-
See the [
|
|
1624
|
+
See the [Hugo installation docs](https://gohugo.io/installation/) for more options.
|
|
847
1625
|
|
|
848
1626
|
## Quick start
|
|
849
1627
|
|
|
850
1628
|
Preview locally:
|
|
851
1629
|
|
|
852
1630
|
\`\`\`sh
|
|
853
|
-
|
|
1631
|
+
hugo serve
|
|
854
1632
|
\`\`\`
|
|
855
1633
|
|
|
856
|
-
Then open <http://
|
|
1634
|
+
Then open <http://localhost:1313> in your browser.
|
|
857
1635
|
|
|
858
1636
|
Build the site for deployment:
|
|
859
1637
|
|
|
860
1638
|
\`\`\`sh
|
|
861
|
-
|
|
1639
|
+
hugo --minify
|
|
862
1640
|
\`\`\`
|
|
863
1641
|
|
|
864
1642
|
The output goes to the \`public/\` directory. Upload it to any static host (Netlify, Vercel, Cloudflare Pages, GitHub Pages, etc.).
|
|
@@ -866,1886 +1644,61 @@ The output goes to the \`public/\` directory. Upload it to any static host (Netl
|
|
|
866
1644
|
## Project structure
|
|
867
1645
|
|
|
868
1646
|
\`\`\`
|
|
869
|
-
|
|
1647
|
+
hugo.toml — Site configuration (baseURL, title, theme, params)
|
|
870
1648
|
content/
|
|
871
|
-
_index.md
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
1649
|
+
_index.md — Home section
|
|
1650
|
+
archive/_index.md — Archive section
|
|
1651
|
+
collections/_index.md — Collections directory section
|
|
1652
|
+
featured/_index.md — Featured section
|
|
1653
|
+
{slug}/
|
|
1654
|
+
_index.md — Thread root (branch bundle)
|
|
1655
|
+
{reply-slug}/
|
|
1656
|
+
index.md — Reply (leaf bundle, not rendered as its own URL)
|
|
1657
|
+
data/
|
|
1658
|
+
jant.toml — Nav items, branding, display preferences, ordered collections directory
|
|
1659
|
+
themes/jant/ — Bundled Hugo theme (overrideable via layouts/ at the site root)
|
|
1660
|
+
static/ — Copy files here to add them to the published site
|
|
881
1661
|
\`\`\`
|
|
882
1662
|
|
|
883
1663
|
## Customizing
|
|
884
1664
|
|
|
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 %}
|
|
1665
|
+
- **Site settings** — edit \`hugo.toml\` to change the baseURL, title, or pagination.
|
|
1666
|
+
- **Jant metadata** — \`data/jant.toml\` drives nav and the collections directory, and is preserved across round-trip import.
|
|
1667
|
+
- **Styles** — edit \`themes/jant/static/main.css\`, or drop a \`static/main.css\` at the site root to override.
|
|
1668
|
+
- **Templates** — add files under \`layouts/\` at the site root to override the bundled theme.
|
|
1669
|
+
- **Debugging** — from a Jant site project, run \`npx jant site export --directory ./my-site\`, then \`cd my-site && hugo serve\`.
|
|
1056
1670
|
|
|
1057
|
-
|
|
1671
|
+
## Fetching media locally
|
|
1058
1672
|
|
|
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
|
-
`;
|
|
1673
|
+
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
1674
|
|
|
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
|
-
`;
|
|
1675
|
+
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
1676
|
|
|
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
|
-
`;
|
|
1677
|
+
\`\`\`sh
|
|
1678
|
+
npx @jant/core site pull-media --path .
|
|
1679
|
+
\`\`\`
|
|
1219
1680
|
|
|
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
|
-
`;
|
|
1681
|
+
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
1682
|
|
|
1262
|
-
|
|
1263
|
-
// Shared macro — single post card used by all list/detail templates
|
|
1264
|
-
// ---------------------------------------------------------------------------
|
|
1683
|
+
## Notes
|
|
1265
1684
|
|
|
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 %}
|
|
1685
|
+
- 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.
|
|
1686
|
+
- \`/{reply-slug}/\` URLs are preserved via \`aliases:\` on the root post, so old links still land on the right thread anchor.
|
|
1687
|
+
- 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.
|
|
1688
|
+
- Posts with \`draft: true\` in front matter are only built when you pass \`--buildDrafts\` to \`hugo\` / \`hugo serve\`.
|
|
1492
1689
|
`;
|
|
1690
|
+
}
|
|
1493
1691
|
|
|
1494
1692
|
// ---------------------------------------------------------------------------
|
|
1495
|
-
//
|
|
1693
|
+
// Re-exports for consumers (kept so existing entry points compile)
|
|
1496
1694
|
// ---------------------------------------------------------------------------
|
|
1497
1695
|
|
|
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
|
-
`;
|
|
1696
|
+
export {
|
|
1697
|
+
buildSiteIconAssets,
|
|
1698
|
+
buildExportedCollectionMetrics,
|
|
1699
|
+
buildExportedCollectionDirectoryItems,
|
|
1700
|
+
readStorageObjectBytes,
|
|
1701
|
+
getArchiveSummaryText,
|
|
1702
|
+
getMediaUrl,
|
|
1703
|
+
getPublicUrlForProvider,
|
|
1704
|
+
};
|