@jant/core 0.3.40 → 0.3.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -34
- package/bin/commands/assets/upload.js +247 -0
- package/bin/commands/collections.js +268 -0
- package/bin/commands/import-site.js +1169 -343
- package/bin/commands/media.js +302 -0
- package/bin/commands/migrate-text-attachments.js +183 -0
- package/bin/commands/migrate.js +263 -2
- package/bin/commands/posts.js +262 -0
- package/bin/commands/search-reindex.js +175 -0
- package/bin/commands/search.js +53 -0
- package/bin/commands/settings.js +93 -0
- package/bin/commands/site/export.js +34 -33
- package/bin/commands/site/{localize-media.js → pull-media.js} +17 -17
- package/bin/lib/d1-query.js +15 -3
- package/bin/lib/http-api.js +223 -0
- package/bin/lib/hugo-markdown.js +102 -0
- package/bin/lib/media-upload.js +206 -0
- package/bin/lib/migration-runner.js +64 -0
- package/bin/lib/runtime-target.js +7 -2
- package/bin/lib/site-media-parser.js +71 -16
- package/bin/lib/site-pull-media.js +695 -0
- package/bin/lib/site-snapshot.js +0 -1
- package/dist/app-Ctl0T0zO.js +5 -0
- package/dist/{app-CAtsuLLh.js → app-GbfwoeDJ.js} +24357 -23139
- package/dist/client/.vite/manifest.json +3485 -0
- package/dist/client/_assets/chunks/02611a045a7fe83a12014e3debc9f731-BqEDtwXC.woff2 +0 -0
- package/dist/client/_assets/chunks/033466ef683afe931f7f520cfb42d928-BluLHEh9.woff2 +0 -0
- package/dist/client/_assets/chunks/04b8bde59cff68eeee74fb1914eb09c7-D5j2CLTt.woff2 +0 -0
- package/dist/client/_assets/chunks/057a6a98bda7fe57105ddaa99ec82015-DY_AoijT.woff2 +0 -0
- package/dist/client/_assets/chunks/05ac821472e235943ed1435e4bb8ecad-CUm8GiH6.woff2 +0 -0
- package/dist/client/_assets/chunks/05da12edb9d52210581dc6ec4541031f-BXAOx2XP.woff2 +0 -0
- package/dist/client/_assets/chunks/074c1f8483d5a4d8c45c8c5f3e4cbb32-CDqdMQm8.woff2 +0 -0
- package/dist/client/_assets/chunks/0968e4861204b51f62a2f8e9f15dd5e0-D5tLgF_8.woff2 +0 -0
- package/dist/client/_assets/chunks/0acd1fe2b2ea1ad1bfee7ae1fa139d27-BKK1m412.woff2 +0 -0
- package/dist/client/_assets/chunks/0c2ab6b295e55f356f8020d4e7747522-CkCQjQw3.woff2 +0 -0
- package/dist/client/_assets/chunks/0c5f9492af03a4fa42c784de94649de1-CQlJqksa.woff2 +0 -0
- package/dist/client/_assets/chunks/0d5dec931dc885f07fe5cd5af8bed675-C74x-U_U.woff2 +0 -0
- package/dist/client/_assets/chunks/0d9a936885a4c39077438effd3779cbd-Bw0fYF4S.woff2 +0 -0
- package/dist/client/_assets/chunks/0e645da524f7cfc0e8c3c03fb2b08428-BVeI_5Th.woff2 +0 -0
- package/dist/client/_assets/chunks/0f5e1a18987dbc84ca05188c129e1936-B6_lZ6fE.woff2 +0 -0
- package/dist/client/_assets/chunks/1076f0f6f66d28d7a2f16427faad4413-ubiXnD1P.woff2 +0 -0
- package/dist/client/_assets/chunks/112743a4ab5fdd1498dfdf2b11336380-B5FzQxTK.woff2 +0 -0
- package/dist/client/_assets/chunks/1211f03d3ff5759a702631445793f60e-DDcSiyCt.woff2 +0 -0
- package/dist/client/_assets/chunks/12ef1ba76bd20b004b865266a1aa70b3-BwmaD87S.woff2 +0 -0
- package/dist/client/_assets/chunks/135e83b403475c5dc9e49b43501a5b84-C5mE_KZS.woff2 +0 -0
- package/dist/client/_assets/chunks/1380210a722ac9aeef54005ad7b015c9-CjA-MP82.woff2 +0 -0
- package/dist/client/_assets/chunks/13b2c53b30e6a3e4de7132dbc18dbdfc-B9WOB7kv.woff2 +0 -0
- package/dist/client/_assets/chunks/147b24c2f08d03bbed30887d4cb3311c-IeeFCJ13.woff2 +0 -0
- package/dist/client/_assets/chunks/152395634a207579552f8cb54db88599-C2y2JyNZ.woff2 +0 -0
- package/dist/client/_assets/chunks/15d95680dc31cc6ce20e0a5464106dc4-CpD8dqRm.woff2 +0 -0
- package/dist/client/_assets/chunks/16663e567f81f4725a1522f37e18f71f-BNoxS9pQ.woff2 +0 -0
- package/dist/client/_assets/chunks/1733f27476507ca68b68a803bc533cc2-DXL-14Ug.woff2 +0 -0
- package/dist/client/_assets/chunks/17c7f5d0a45e92ede0e5dec3a2c77efa-CNj3jQMl.woff2 +0 -0
- package/dist/client/_assets/chunks/1824321da801e6257b902f3d1d09054c-D4dgdyWB.woff2 +0 -0
- package/dist/client/_assets/chunks/188c2db794f3dd7a45889ddbc81da9bb-C9NbUBBO.woff2 +0 -0
- package/dist/client/_assets/chunks/1a08931435f885e707edb85978ea3bb6-BOiP2u7G.woff2 +0 -0
- package/dist/client/_assets/chunks/1a3a92c7c060a71a6c35819b9ebcdbb9-DFfonQfz.woff2 +0 -0
- package/dist/client/_assets/chunks/1be602ad456b0d75902d352116cb35fd-Bc-XQtQh.woff2 +0 -0
- package/dist/client/_assets/chunks/1cf737900dd49c2e88f1b3221a82a602-CfTmnns3.woff2 +0 -0
- package/dist/client/_assets/chunks/1fba7ec0e412e911bf31841de5a8a4e4-18ELMbFF.woff2 +0 -0
- package/dist/client/_assets/chunks/233d3a685ee18276b319363474599d47-BaFdJ0-G.woff2 +0 -0
- package/dist/client/_assets/chunks/2612c60f9dc5459ac42763591240a1d6-CTD69cq1.woff2 +0 -0
- package/dist/client/_assets/chunks/284b53bbefb06924cf236d24c8ed5641-CPDiuOv6.woff2 +0 -0
- package/dist/client/_assets/chunks/28a97af9ab9d38983d20f39ff09840e1-mFqcT3Jw.woff2 +0 -0
- package/dist/client/_assets/chunks/2a56eaf19d1d38a6b57e2a388f733676-CU52qBlE.woff2 +0 -0
- package/dist/client/_assets/chunks/2ab8cf1f23a5ac7939a7876054e711a7-B4T3dK7A.woff2 +0 -0
- package/dist/client/_assets/chunks/2abfbab82b6a7c04426afc054d2464da-D5PIIHde.woff2 +0 -0
- package/dist/client/_assets/chunks/2bfaadaf3479c72286248e6de0be0ec9-DEpU6O74.woff2 +0 -0
- package/dist/client/_assets/chunks/2c8c55e4cec85ce77e95cac9d330f5cf-BCTA96EZ.woff2 +0 -0
- package/dist/client/_assets/chunks/2e6f4bb71ef6b38765d51acc5a79f638-Mbs6PECD.woff2 +0 -0
- package/dist/client/_assets/chunks/2ed9981d2e8983365bd051159b976b6d-D8gg6NWM.woff2 +0 -0
- package/dist/client/_assets/chunks/2f4c633e923ba30c6ba376367379fc91-CIJCFztv.woff2 +0 -0
- package/dist/client/_assets/chunks/31194d303a67561926a544ed0e072aee-w3-eGYAi.woff2 +0 -0
- package/dist/client/_assets/chunks/31a197213ae61d7043c81013f52d10d6-DXZrrkJP.woff2 +0 -0
- package/dist/client/_assets/chunks/349965eee0a9b6c984a319ab96a4ece9-DByAVmXw.woff2 +0 -0
- package/dist/client/_assets/chunks/34e6750e00c3a911ef87dc66b336594d-DNbWesOu.woff2 +0 -0
- package/dist/client/_assets/chunks/3623d96698132c61e861f984ce11cc23-jqV9ErzX.woff2 +0 -0
- package/dist/client/_assets/chunks/36cda3eae13370b934bba4429fab9078-BPDcUlw9.woff2 +0 -0
- package/dist/client/_assets/chunks/36f3df4730cfca4fc77fe52b52e6a126-BwK10NC0.woff2 +0 -0
- package/dist/client/_assets/chunks/37887deb7a9c466cf2af6ee7ff372ea6-BrAerJB1.woff2 +0 -0
- package/dist/client/_assets/chunks/37dbd564820cce2f7b3331e442da0df3-Cql2kzIi.woff2 +0 -0
- package/dist/client/_assets/chunks/37dc8505b20b37a2477a9121b16d44a7-CoXw6XWV.woff2 +0 -0
- package/dist/client/_assets/chunks/39211b02f24c69cbd1b54f6d41b01933-4Xu7vvnq.woff2 +0 -0
- package/dist/client/_assets/chunks/3aa13f1843d7c8fa16109b6395a9a29d-BQFZUc3B.woff2 +0 -0
- package/dist/client/_assets/chunks/3aba89c4d4281553078de4622b498cbb-8QS1zDYk.woff2 +0 -0
- package/dist/client/_assets/chunks/3ac0a07878cacdc98bb889f40f5970d3-C-BaM6-y.woff2 +0 -0
- package/dist/client/_assets/chunks/3d88558097559d775231194d43745ba7-CR2aqGC7.woff2 +0 -0
- package/dist/client/_assets/chunks/3dc3e8c35524cad2d1236f3393104ccf-9sqBsF7B.woff2 +0 -0
- package/dist/client/_assets/chunks/3e24063b19abd2b051af986a6870046d-TlweM3SW.woff2 +0 -0
- package/dist/client/_assets/chunks/3e2c06bdd9dcab29aeaeb0cfb7fa608a-BaqU-dIt.woff2 +0 -0
- package/dist/client/_assets/chunks/424123f9371ba357087363263eeafcda-C8SVfEJi.woff2 +0 -0
- package/dist/client/_assets/chunks/43b3bbf43055c3e65db9cdf2c5b18c1c-CbHx6-Ki.woff2 +0 -0
- package/dist/client/_assets/chunks/4410f876d27c9821839474b6b1059fd7-ClYz91aY.woff2 +0 -0
- package/dist/client/_assets/chunks/44704471b60c18b3ecec5300a88f1102-DDmmy6-m.woff2 +0 -0
- package/dist/client/_assets/chunks/45140742dfa8686b58db2899ce89f89a-D5IMMzDM.woff2 +0 -0
- package/dist/client/_assets/chunks/46604090efbb99c2db1800928af0a239-JX2fpYX_.woff2 +0 -0
- package/dist/client/_assets/chunks/46998df85d31e629c0142f0556f5e7c5-BIkNG9Wh.woff2 +0 -0
- package/dist/client/_assets/chunks/469d2754e315e77270c12fa0ab28026c-D-y-vsfA.woff2 +0 -0
- package/dist/client/_assets/chunks/479329a39a3eb6c0047e6b0981919855-vZKCRx42.woff2 +0 -0
- package/dist/client/_assets/chunks/491bbba374093fff045f55803a22bfef-DSQXiDCd.woff2 +0 -0
- package/dist/client/_assets/chunks/49902f1c41b281f8eb3771abdd4dccb8-DxVxNg2W.woff2 +0 -0
- package/dist/client/_assets/chunks/4c0760284569b87d8f2290c324953973-Bqd-TKHH.woff2 +0 -0
- package/dist/client/_assets/chunks/4cba92c942694bdc479bc54101bb1aa4-BrfsrgrC.woff2 +0 -0
- package/dist/client/_assets/chunks/4d38e56738156625329d93bd5c8cc74a-DONkMP0y.woff2 +0 -0
- package/dist/client/_assets/chunks/4db4f31a16965baa42cb7dad667a8f04-Dt1Cy4gr.woff2 +0 -0
- package/dist/client/_assets/chunks/4e7d263c30bb48604069a3b4404d4eeb-DNPCkvKD.woff2 +0 -0
- package/dist/client/_assets/chunks/5279c7e465d9871edf51fd02e691eee7-CKhyhvVf.woff2 +0 -0
- package/dist/client/_assets/chunks/527ff336279465617aafbafe1a830632-CC3lv4pm.woff2 +0 -0
- package/dist/client/_assets/chunks/54bfdb6f21f9d6191e5100580239e14f-PGlqYvgl.woff2 +0 -0
- package/dist/client/_assets/chunks/56b40518ea0608e62826bd44ee27f5f9-BD9F9Oss.woff2 +0 -0
- package/dist/client/_assets/chunks/56be5e32fd01668fe5ba814d94905839-CSyFA-9l.woff2 +0 -0
- package/dist/client/_assets/chunks/572b9e28f1e6d89b765a16d809db84a1-BIYMjzEa.woff2 +0 -0
- package/dist/client/_assets/chunks/57e9a86651366c1ba299e47aa7e358c2-BHuZXB4W.woff2 +0 -0
- package/dist/client/_assets/chunks/57f89adeae55aa7fe2add3fc1801ce03-Cy3PNrfx.woff2 +0 -0
- package/dist/client/_assets/chunks/5921cef36750ff6092d94b83a802823b-hF-uymTh.woff2 +0 -0
- package/dist/client/_assets/chunks/59e95d8b5332dcdae894a19946ab767d-ClRfsjWZ.woff2 +0 -0
- package/dist/client/_assets/chunks/5acc7f736217259db79a42bf44241c48-FYQUuFOM.woff2 +0 -0
- package/dist/client/_assets/chunks/5c05f8b75c9d7f25b3c04ca711cf43b3-D-YWPkSq.woff2 +0 -0
- package/dist/client/_assets/chunks/5df9ce98c75f8c50fb4fff7f01a23925-DgoCENyW.woff2 +0 -0
- package/dist/client/_assets/chunks/5e0124c7265f1b4cf0fc797b94efbebc-BTBNHsSB.woff2 +0 -0
- package/dist/client/_assets/chunks/5ec349ff62b9ee5fe0c6dae867704dc4-B4dezhrz.woff2 +0 -0
- package/dist/client/_assets/chunks/60020a830d809c26970cf8e48ba1eeda-B6FDTSik.woff2 +0 -0
- package/dist/client/_assets/chunks/601f812e8b1eafecd5ba585a6a6d7962-gIVBlUU9.woff2 +0 -0
- package/dist/client/_assets/chunks/6025ddaf99d86cc8bfd781006b19fbd6-CS-ze337.woff2 +0 -0
- package/dist/client/_assets/chunks/60bcfa3ea7446eb42394aab02d28be40-C-PQfCu3.woff2 +0 -0
- package/dist/client/_assets/chunks/60f6e28b3f0400b0e8fe32c3c7116102-Dag9L2qU.woff2 +0 -0
- package/dist/client/_assets/chunks/615a7e0d63f257109d599b2a1977de68-D6Y93yoG.woff2 +0 -0
- package/dist/client/_assets/chunks/615b38e0b5bb3ea68426f44f47706e6f-B5yzIIer.woff2 +0 -0
- package/dist/client/_assets/chunks/61a2c80d0c924d5a6187c02e8d1d1642-BY3MmHbZ.woff2 +0 -0
- package/dist/client/_assets/chunks/62fc94f8d4c5a750b7f25e7387539910-3ql2_WDK.woff2 +0 -0
- package/dist/client/_assets/chunks/639c7c6b0b0c27c738702741cfa4b8c0-BCD3OGri.woff2 +0 -0
- package/dist/client/_assets/chunks/63fafcb069520613d0ea35ad3e6b1e42-DP0Rrj4o.woff2 +0 -0
- package/dist/client/_assets/chunks/64a404d675f1d726f0891b7a80794595-DU3L0I9s.woff2 +0 -0
- package/dist/client/_assets/chunks/65a60d87c64228d258a123cbe85f5f31-B4kbf-79.woff2 +0 -0
- package/dist/client/_assets/chunks/65e9c4585d71bf48a5c62f367010da16-CvzgzReN.woff2 +0 -0
- package/dist/client/_assets/chunks/68a3a4bf337f8a0722be76676e20b850-Dnaw8EPD.woff2 +0 -0
- package/dist/client/_assets/chunks/6968e889e18891d912809fe484c2e745-Bj1m5e0s.woff2 +0 -0
- package/dist/client/_assets/chunks/6abdd163d2c4b85698db8aa1ce149361-B5FVGJuo.woff2 +0 -0
- package/dist/client/_assets/chunks/6b75213eb0be40ce84241eb2bb438a2e-CqHmF-NR.woff2 +0 -0
- package/dist/client/_assets/chunks/70714891ad3fbfc3d5f10a8669dacc5a-BWk5nLFN.woff2 +0 -0
- package/dist/client/_assets/chunks/7158022889c6c177062ac85036e7af10-B3D4K22n.woff2 +0 -0
- package/dist/client/_assets/chunks/73b794d885b88f7befb7ea8ab1780a15-D0ZehnEI.woff2 +0 -0
- package/dist/client/_assets/chunks/743f290baf027d4626a86e22f3d44600-DDTDQdRC.woff2 +0 -0
- package/dist/client/_assets/chunks/7520ca6ca8c60eb1e62d50e71c8d30f1-CVRaGu2G.woff2 +0 -0
- package/dist/client/_assets/chunks/757019c01c5e2c2b0f54293cea3b5636-BTovUnoH.woff2 +0 -0
- package/dist/client/_assets/chunks/759a647b77791b1e98f99bc0ab5317a7-CrK6FIFz.woff2 +0 -0
- package/dist/client/_assets/chunks/761d84afca8d7f34eebefe538adba827-DRbiVuwX.woff2 +0 -0
- package/dist/client/_assets/chunks/76e07530323418ec723c5d7634c9bcca-DGtVJTTH.woff2 +0 -0
- package/dist/client/_assets/chunks/76e51630143b95b6322ef93ad330da7a-D-kV82C_.woff2 +0 -0
- package/dist/client/_assets/chunks/76f117a3baacda304a71965a17266a13-DFfvsFUm.woff2 +0 -0
- package/dist/client/_assets/chunks/773819b7b9b8fd404a929867c0fd677e-Bu1xCMZb.woff2 +0 -0
- package/dist/client/_assets/chunks/77747f17625a259fb405b7bbdd84ac4b-o_vNfh-6.woff2 +0 -0
- package/dist/client/_assets/chunks/7817dd16805145d8538ad57590f69f5a-DguSUP3O.woff2 +0 -0
- package/dist/client/_assets/chunks/788498548ddfb710d6ef3c7bff79fa97-3ffoQfRd.woff2 +0 -0
- package/dist/client/_assets/chunks/78ea6c40923ce95367e3517dd1e5a849-DvLDRhX9.woff2 +0 -0
- package/dist/client/_assets/chunks/79aa7c8c842c4a27cf57c0a3a1ffca5a-sDoCeoKI.woff2 +0 -0
- package/dist/client/_assets/chunks/7b59f0ec7792b18458dc5a361e37884c-DWvxkKmT.woff2 +0 -0
- package/dist/client/_assets/chunks/7c3945788a689a69356c1a622d69d48b-1JjIpss2.woff2 +0 -0
- package/dist/client/_assets/chunks/7cc72fd2c9105560422b6a67c6162945-Co3AF5_G.woff2 +0 -0
- package/dist/client/_assets/chunks/7d0ea0690e432462f4d05a23d720ec19-Dx7DK4C3.woff2 +0 -0
- package/dist/client/_assets/chunks/7dffa5c1bec57e0903fd62357401ff1a-DuQHmiiN.woff2 +0 -0
- package/dist/client/_assets/chunks/7e38ca789a9e76ac91d9256374e154f0-BtETgGRe.woff2 +0 -0
- package/dist/client/_assets/chunks/7f9a6c9286b68de9c72b0024f7beeb40-JpXdo4FC.woff2 +0 -0
- package/dist/client/_assets/chunks/815a0b797465190f60d8b1a04656c42e-Spzo94nU.woff2 +0 -0
- package/dist/client/_assets/chunks/823d1bb11097331238d103c7f72138dc-CGN6m2VF.woff2 +0 -0
- package/dist/client/_assets/chunks/82f6ccc063960eed1cdfd1d61ada8862-CsmAqx2a.woff2 +0 -0
- package/dist/client/_assets/chunks/835b6505bb9eea9678925a1fa885353d-C6mSBwUc.woff2 +0 -0
- package/dist/client/_assets/chunks/845b4b67564d62cf5cad242f7ac08ea5-DnvzrloJ.woff2 +0 -0
- package/dist/client/_assets/chunks/84c8e7bc0931008ed91f44ed12dfea94-C2Hvhh-b.woff2 +0 -0
- package/dist/client/_assets/chunks/8726558cf297cbda831cc19514897205-gSFV1vW0.woff2 +0 -0
- package/dist/client/_assets/chunks/8855472f3c02f6c7ebb3216617ebe4af-C1JsWVqq.woff2 +0 -0
- package/dist/client/_assets/chunks/887a11290ba78b1e66c6d2f67043005e-L_A1f5Fz.woff2 +0 -0
- package/dist/client/_assets/chunks/888ba53510213d5d1f6a7deb60e569b6-OP8E-ELN.woff2 +0 -0
- package/dist/client/_assets/chunks/88db1d042074fb6e66821ffc10941930-kZ6K3e62.woff2 +0 -0
- package/dist/client/_assets/chunks/892aa49b2529c89bb4076d4aa51fe30f-Bw0_Km2A.woff2 +0 -0
- package/dist/client/_assets/chunks/8b5c9ef81159f31d2ab35f45ca4373e0-Djw95sKC.woff2 +0 -0
- package/dist/client/_assets/chunks/8b91c7c2ed390f1278b9befa3aa87233-B2C3iFbQ.woff2 +0 -0
- package/dist/client/_assets/chunks/8dc18c0cebe6aa4bf4c45dbb831048ab-bUlANLit.woff2 +0 -0
- package/dist/client/_assets/chunks/8e231d707f0c4f8e3cde90a6b52a79aa-CvwbWlul.woff2 +0 -0
- package/dist/client/_assets/chunks/8fb45117a62d92dce44a80f0b729ead5-Djr4-o5-.woff2 +0 -0
- package/dist/client/_assets/chunks/9064c12fd72c7aba235d8c3881755b91-BnguxT-p.woff2 +0 -0
- package/dist/client/_assets/chunks/90b7848e9b1623b77bdcf155e90b839c-CFvPRnoM.woff2 +0 -0
- package/dist/client/_assets/chunks/911b9e53e9814de2998c60bf3115f560-DmPmj635.woff2 +0 -0
- package/dist/client/_assets/chunks/919a879bd2d580d8491a31a449390689-BY2w-8_Y.woff2 +0 -0
- package/dist/client/_assets/chunks/91da6cb174bebfb96976e2f1316be2fe-DztAJb6W.woff2 +0 -0
- package/dist/client/_assets/chunks/92fdc376bce277874e75db666f175b44-DJUnHEIN.woff2 +0 -0
- package/dist/client/_assets/chunks/930a23430f0eb64480d7fe5f82834e21-2VrDPT7E.woff2 +0 -0
- package/dist/client/_assets/chunks/934dfdbed2bb2c4b6129199d699a34fa-CtpHsjRD.woff2 +0 -0
- package/dist/client/_assets/chunks/93bed0e5f2dd5a21cf73304fcfbc149d-DJiaETX7.woff2 +0 -0
- package/dist/client/_assets/chunks/93f3ea6918533d96d9c252378b9a4af0-D-FJaGi-.woff2 +0 -0
- package/dist/client/_assets/chunks/95a0951d2a2722ff773a1a45e8c474d6-DuZg-BpP.woff2 +0 -0
- package/dist/client/_assets/chunks/95ff41191c76ef893ac70a1043e95e44-C08QDbJO.woff2 +0 -0
- package/dist/client/_assets/chunks/96c75862c8cec51a7c05ff025fea86cd-DBnRwCuW.woff2 +0 -0
- package/dist/client/_assets/chunks/97d7a17234f2b5030ae9697ae00aded2-CkUSaB-p.woff2 +0 -0
- package/dist/client/_assets/chunks/982cd1765ca6bbb3e6547e47834d5148-DkIybx1r.woff2 +0 -0
- package/dist/client/_assets/chunks/985d4d41afa0934a4eb2de35473fb9a2-CF7rgJMw.woff2 +0 -0
- package/dist/client/_assets/chunks/995c4fda62d25c3b79dd5987737df6ae-CY5ri8HW.woff2 +0 -0
- package/dist/client/_assets/chunks/9b0c3caac458c7bc9c9133e40405ddea-DhPrYOcz.woff2 +0 -0
- package/dist/client/_assets/chunks/9b21e8244f5930c48ad5073f83777b6c-CuE9-r-r.woff2 +0 -0
- package/dist/client/_assets/chunks/9bb38fd05201de666b88b82d56a386f7-nMl6lB8X.woff2 +0 -0
- package/dist/client/_assets/chunks/9d0afa53dc2f92ba42024f55f11f6779-Bozpx8bL.woff2 +0 -0
- package/dist/client/_assets/chunks/9e13a47242926f1cd7d68c08d9ef8889-DwKQjc8M.woff2 +0 -0
- package/dist/client/_assets/chunks/9eff2ab2a9d2ced47ab761bf6fdb11ec-D86-Zm1H.woff2 +0 -0
- package/dist/client/_assets/chunks/9f1963fe8d4d6878d717d872a8f3fdb5-DxjITUFq.woff2 +0 -0
- package/dist/client/_assets/chunks/9f3bdab4025dc7c5ba1a5871e62e8918-BqxDEFLJ.woff2 +0 -0
- package/dist/client/_assets/chunks/9fce02d1a09f464c36c9fcfb14a354c5-F2tMAB_j.woff2 +0 -0
- package/dist/client/_assets/chunks/a00e94d4f04df6aa659db9c4954c7efe-CRBKCj_3.woff2 +0 -0
- package/dist/client/_assets/chunks/a0a6755c7e3f9e4cf03387027dd9f16c-BAyRqF2w.woff2 +0 -0
- package/dist/client/_assets/chunks/a0d00fe4816c95a8c7dffd445ef00b03-CxcTiRLJ.woff2 +0 -0
- package/dist/client/_assets/chunks/a0f199077fa1a33bc2a1e01c64de7fe6-IdgVJmZD.woff2 +0 -0
- package/dist/client/_assets/chunks/a2b0597837e382aca19919c4b74e58c8-7iVOBjzI.woff2 +0 -0
- package/dist/client/_assets/chunks/a373e85c96aa0dc303092429fb837e51-Dvmw8mbX.woff2 +0 -0
- package/dist/client/_assets/chunks/a379ada89abd750c587ded29f77e731c-CgPBsEsv.woff2 +0 -0
- package/dist/client/_assets/chunks/a4475c442c6b259354c9eda62e64e7df-TZ0c8vK-.woff2 +0 -0
- package/dist/client/_assets/chunks/a48be56ce5d3a99dc8f8331398004412-BR_ixH1W.woff2 +0 -0
- package/dist/client/_assets/chunks/a4a39b5f048671aa37c2b2a9581ab08a-EDfDYsUd.woff2 +0 -0
- package/dist/client/_assets/chunks/a534449d67561ac9b06489f64e57ad98-C355TCrD.woff2 +0 -0
- package/dist/client/_assets/chunks/a6e044424780a57610833cc856c15bf5-x1b8FE4H.woff2 +0 -0
- package/dist/client/_assets/chunks/a7c35e42a659347e490c3cb7983b2e7f-BWpA0-oz.woff2 +0 -0
- package/dist/client/_assets/chunks/a7c58070d68dc1724e9d4e781afb78db-ql27pT-6.woff2 +0 -0
- package/dist/client/_assets/chunks/a806759e72987951d6d08b7cbdf36a1b-TvLDwB_u.woff2 +0 -0
- package/dist/client/_assets/chunks/a88481ec8bc7be11cb66e46781a79bb9-Ck-r_sGx.woff2 +0 -0
- package/dist/client/_assets/chunks/a919a8c6fec6d7e8bb21177965f2e9d7-CC5uWzwd.woff2 +0 -0
- package/dist/client/_assets/chunks/a9a016d4a93409f65278327e8a8bb38d-dylrNwF8.woff2 +0 -0
- package/dist/client/_assets/chunks/aa662092cf249707123ca4d575e2764b-CSxNsTUL.woff2 +0 -0
- package/dist/client/_assets/chunks/aae1eda193285ab817a2d1b408440326-CjyjBoRm.woff2 +0 -0
- package/dist/client/_assets/chunks/ab92d41062a7644faa45f50bf384454a-Cq0X8a9H.woff2 +0 -0
- package/dist/client/_assets/chunks/accc43762ad05edfbff66ba1380d192a-B5jIykaZ.woff2 +0 -0
- package/dist/client/_assets/chunks/ad47bcd6d4663026d648c132d969318d-BYdIbDge.woff2 +0 -0
- package/dist/client/_assets/chunks/ad67113f88b59582991cb0c5d33ea19f-DOH1vcIZ.woff2 +0 -0
- package/dist/client/_assets/chunks/adb6e613fb99c6dd660b727101f554a8-DJDw_ZZK.woff2 +0 -0
- package/dist/client/_assets/chunks/adc4a98a89870ef984ee8f4b57c8a3a5-D30vw26R.woff2 +0 -0
- package/dist/client/_assets/chunks/af57a2e8c72f9ba0efb1af771d32c124-D5Zwq9pS.woff2 +0 -0
- package/dist/client/_assets/chunks/b0ae5f374e43dac992a4a5d334cebc0b-Ccoi4dPt.woff2 +0 -0
- package/dist/client/_assets/chunks/b0cb664cb2e1371efda8943c0b7dcd1c-YzGFnelF.woff2 +0 -0
- package/dist/client/_assets/chunks/b0d1cdced482352cf0d3ae58638aacb9-IunWfc6R.woff2 +0 -0
- package/dist/client/_assets/chunks/b12379ab782468d725519cd07a7d15bc-IjbydKs2.woff2 +0 -0
- package/dist/client/_assets/chunks/b320f5d185b2cff933ac549c184031c5-IYnPrIz4.woff2 +0 -0
- package/dist/client/_assets/chunks/b326a8a9c33b554db570da94f60bc380-B6dzaCQg.woff2 +0 -0
- package/dist/client/_assets/chunks/b55486f8a459c838fe329d4e79a8c211-DW8q7oGV.woff2 +0 -0
- package/dist/client/_assets/chunks/b6117ff2993b11bb1fdc7ea3588a010c-JoMYL5W_.woff2 +0 -0
- package/dist/client/_assets/chunks/b61982951bd51b724143c30dfaaa9fe9-D3KhBKl6.woff2 +0 -0
- package/dist/client/_assets/chunks/b91234c10fd8b8c8abc88e03afe66a1f-BABNh2la.woff2 +0 -0
- package/dist/client/_assets/chunks/b9317198f118a1dfd8ddf2b82ec028f3-CiMecxeH.woff2 +0 -0
- package/dist/client/_assets/chunks/ba825ae79b1a6f7e0cce5215fcb5c96f-B6EWU_D5.woff2 +0 -0
- package/dist/client/_assets/chunks/ba9c59d7dfa4494db1bb764ada81467d-D0yPcC1m.woff2 +0 -0
- package/dist/client/_assets/chunks/bab547459d514f46206e340c4bb2dc88-YnBqlGhw.woff2 +0 -0
- package/dist/client/_assets/chunks/bb0b15492b8cdbbec57c0bcfa0aa9241-BjIFbEsN.woff2 +0 -0
- package/dist/client/_assets/chunks/bc6183ac08f0fac78c46f80c10cf7c92-B81nbRb3.woff2 +0 -0
- package/dist/client/_assets/chunks/bc8ccea5abf6598cf3cfa97eb59804bb-B_7IbluG.woff2 +0 -0
- package/dist/client/_assets/chunks/bc95a792cbca6639214c9b0da13392ff-CHQe0E-D.woff2 +0 -0
- package/dist/client/_assets/chunks/bcc39eda837bb7a7a3d37c8c60fffb81-CGd1Zk6S.woff2 +0 -0
- package/dist/client/_assets/chunks/bcfa62f35731856246c146d3a6932bf3-DMDq6w4v.woff2 +0 -0
- package/dist/client/_assets/chunks/bd73264d7f98776708d5d6f3c9b78fcc-d9QWWMym.woff2 +0 -0
- package/dist/client/_assets/chunks/bed610b217d500f5975cfc9fe6157570-B5t7s1bw.woff2 +0 -0
- package/dist/client/_assets/chunks/bedc74b423b7293b6ad0bdecc61c42cc-4Nnh7peP.woff2 +0 -0
- package/dist/client/_assets/chunks/bf8901f8f11d4f433ea17dabc9370ea6-B8dkfvGn.woff2 +0 -0
- package/dist/client/_assets/chunks/bfb00d4a4c48661bd0be99f300a0faae-OxsWZ7l-.woff2 +0 -0
- package/dist/client/_assets/chunks/c0098958e20db68cab90097b5e62516f-VpjQo3vV.woff2 +0 -0
- package/dist/client/_assets/chunks/c063897793f593eb26d6ff7b7baaba18-BWeJJ1sH.woff2 +0 -0
- package/dist/client/_assets/chunks/c1b0df29ae41d764904df84e9ac83d1e-D1N934vs.woff2 +0 -0
- package/dist/client/_assets/chunks/c4143bb9f2fe77b6ccf20088a8904650-nHfYM24e.woff2 +0 -0
- package/dist/client/_assets/chunks/c42c67070cfe99cf823df92d81c7fa6e-B_a405Ez.woff2 +0 -0
- package/dist/client/_assets/chunks/c4bfaa5e50798246e3770718b7a7c84a-B-r6Y_o0.woff2 +0 -0
- package/dist/client/_assets/chunks/c4d749e45ecd5a5aed5a0bb3ebfd355d-CkqE1fRi.woff2 +0 -0
- package/dist/client/_assets/chunks/c6c2971ad1f3221f6cf84028aa0f477e-D7BZGIcY.woff2 +0 -0
- package/dist/client/_assets/chunks/c7ff3f6bbdcd5f604b7343602ab904df-Bq7UjVrZ.woff2 +0 -0
- package/dist/client/_assets/chunks/c84598999133455503042e06f4ab79cb-DIXndJX3.woff2 +0 -0
- package/dist/client/_assets/chunks/c945c62368357d05a53206620460fb30-82gFEf6n.woff2 +0 -0
- package/dist/client/_assets/chunks/c96d83978add28b356c22c4c84916733-BL4VaPIE.woff2 +0 -0
- package/dist/client/_assets/chunks/c97f41eef722121d86f55d553c056a39-CjejgQO2.woff2 +0 -0
- package/dist/client/_assets/chunks/ca62704509932d3232d62918de97af3f-CKWF-3mX.woff2 +0 -0
- package/dist/client/_assets/chunks/ca9a533988d7019597a60d4e17127e0c-ClZ6ygZM.woff2 +0 -0
- package/dist/client/_assets/chunks/cb7ccd6494256f7a2977b7c2b0225592-Cxp8XObm.woff2 +0 -0
- package/dist/client/_assets/chunks/ce022e18a1377ac509443c3c3790b431-DpH0t6Tn.woff2 +0 -0
- package/dist/client/_assets/chunks/ce41d70ce6a069a498525c9e15c45cf2-DGtgv-_b.woff2 +0 -0
- package/dist/client/_assets/chunks/cf2b28f90f47276f7e2688a65e88a101-LVbNkXHj.woff2 +0 -0
- package/dist/client/_assets/chunks/cf9cffe56636322f62b40d61130fbc5e-DNCBMqAo.woff2 +0 -0
- package/dist/client/_assets/chunks/d1f064825fa5784b5c930652bd831cce-BU6qwjp8.woff2 +0 -0
- package/dist/client/_assets/chunks/d20d8944bc0b85f5b2aae4b24f343516-oY9l-NmC.woff2 +0 -0
- package/dist/client/_assets/chunks/d2faabcedd19f016e7b21bce073c0ec8-Dv6eFCf5.woff2 +0 -0
- package/dist/client/_assets/chunks/d320171e57480510f87dbbc7d5264b0a-CcCfjaFX.woff2 +0 -0
- package/dist/client/_assets/chunks/d36644d6502527e1fff205d0c7eca434-kVBj8zy_.woff2 +0 -0
- package/dist/client/_assets/chunks/d3a72a99d365dddfbca8d017a8011368-Bgdmhl3M.woff2 +0 -0
- package/dist/client/_assets/chunks/d40ab99c7a38026f411c8f112f742b48-D7Nm7fg6.woff2 +0 -0
- package/dist/client/_assets/chunks/d42aafa0f246ad3b4c16fe96d3a3a432-CRxTqZXU.woff2 +0 -0
- package/dist/client/_assets/chunks/d4aaf23c13ae808a4bed617afd13aa07-d_T1SKe9.woff2 +0 -0
- package/dist/client/_assets/chunks/d509ee5c8241bc7c3de2039d75564fa5-BpNFD9JE.woff2 +0 -0
- package/dist/client/_assets/chunks/d675d717cd329bfd4c0524f76ae1579c-Bd5_ctoQ.woff2 +0 -0
- package/dist/client/_assets/chunks/d689b1861d7e4377dd72ad3013482612-jPsVOU3F.woff2 +0 -0
- package/dist/client/_assets/chunks/d6bb686bddfbe8f38a36a68e609a8667-C_QduS-X.woff2 +0 -0
- package/dist/client/_assets/chunks/d9020ff69a83b2a6ee1f42ae480f7db0-DLBUPMCe.woff2 +0 -0
- package/dist/client/_assets/chunks/d9047070d72a816b3dba9d40c2d85e69-LTQ42vEp.woff2 +0 -0
- package/dist/client/_assets/chunks/da04549f3f4ed28076b01b8cd710d313-CDXOeduw.woff2 +0 -0
- package/dist/client/_assets/chunks/da7af303f8c645f9a9dbae0e6e32dd35-DCkS2jqi.woff2 +0 -0
- package/dist/client/_assets/chunks/da90a9012ab2d98f759e3fa0820ef502-D0D9d1zg.woff2 +0 -0
- package/dist/client/_assets/chunks/dc6e234ded795e91f76d6647f628fbf0-IkooXcRh.woff2 +0 -0
- package/dist/client/_assets/chunks/dc8f6256445e68199540be9ade33529f-umb5tWbZ.woff2 +0 -0
- package/dist/client/_assets/chunks/dd0cfdfdac0866e66d587e2b5a9e9961-bl4ozE0c.woff2 +0 -0
- package/dist/client/_assets/chunks/de304c8a02e45ded4f8dcc479d167198-ClRu89-4.woff2 +0 -0
- package/dist/client/_assets/chunks/df10d94bea357a43313e20da5d84bd30-CmAgh2oA.woff2 +0 -0
- package/dist/client/_assets/chunks/df9568257eb29b156449fdd4bec5ec76-BMz6aLRr.woff2 +0 -0
- package/dist/client/_assets/chunks/e067cd0ed76c90cd0a93c9339253f20b-6RxCQ4jy.woff2 +0 -0
- package/dist/client/_assets/chunks/e08b07772e7bed3cec2832d43f7fd339-CQCkeKsr.woff2 +0 -0
- package/dist/client/_assets/chunks/e12150d5a39b30be8f567968c7a527b0-DjdoWzeQ.woff2 +0 -0
- package/dist/client/_assets/chunks/e34b2b141e472dc776c86fdf8eea23b0-Cjk7H-Ev.woff2 +0 -0
- package/dist/client/_assets/chunks/e444fd88f1390636e603d0d681538ac8-Bx7CPOZh.woff2 +0 -0
- package/dist/client/_assets/chunks/e53fcb2381eee345db4f6f973dd95a3e-DmkkCUWz.woff2 +0 -0
- package/dist/client/_assets/chunks/e5bd313ef81f687d398aacb11cec3069-CwBEbcji.woff2 +0 -0
- package/dist/client/_assets/chunks/e79898628283edc27180fc39d0d769c1-By2pplQz.woff2 +0 -0
- package/dist/client/_assets/chunks/e8c15be62faf978d208925e79ea6a10d-Ct5M9lTP.woff2 +0 -0
- package/dist/client/_assets/chunks/e8c364c16daa04835bf32d293d2598db-3v4Tut_d.woff2 +0 -0
- package/dist/client/_assets/chunks/e91c4d941ed9f5c2f3e27f205a3a225e-BwOJ2Lst.woff2 +0 -0
- package/dist/client/_assets/chunks/e9c566be7a5d38a9085225f7372bc82b-BCtnCVML.woff2 +0 -0
- package/dist/client/_assets/chunks/e9ebc567a711eeb29019ddae3e0ce7fe-CnNhV49O.woff2 +0 -0
- package/dist/client/_assets/chunks/eaf332445a40942928e5f5750c1c3116-qZ5LSRrj.woff2 +0 -0
- package/dist/client/_assets/chunks/eb5afb3d952b8593782caec6026514b6-BXugZF2T.woff2 +0 -0
- package/dist/client/_assets/chunks/ec86e23683052da5cfdc3b77641bd15a-BeLcbcRc.woff2 +0 -0
- package/dist/client/_assets/chunks/ecb8875a56c7b038b35432fda41ae128-BVqgNf1o.woff2 +0 -0
- package/dist/client/_assets/chunks/eda1b0cb6d1719dd9bedcf3216a9e8de-BJTfLL5z.woff2 +0 -0
- package/dist/client/_assets/chunks/edd6a4f608d04fc0351d7688cfc321e4-d3V4U40c.woff2 +0 -0
- package/dist/client/_assets/chunks/ee6fce20b420a480714607c66d7f97e5-DEtC-G5G.woff2 +0 -0
- package/dist/client/_assets/chunks/eee3836d6ac17ebb2c450bbcbc9db121-C5a2TZjQ.woff2 +0 -0
- package/dist/client/_assets/chunks/et-book-bold-line-figures-BFJr2_zv.woff +0 -0
- package/dist/client/_assets/chunks/et-book-display-italic-old-style-figures-CoeRJAe8.woff +0 -0
- package/dist/client/_assets/chunks/et-book-roman-line-figures-CaA40oOf.woff +0 -0
- package/dist/client/_assets/chunks/f1661731474b78bdf81114daf10b254f-KnMKX_U3.woff2 +0 -0
- package/dist/client/_assets/chunks/f2c58dee206ba9355046fc23d05491f7-Cm4tCWav.woff2 +0 -0
- package/dist/client/_assets/chunks/f50ac27ea4358d67fdda403c2bb52467-WCfYHde1.woff2 +0 -0
- package/dist/client/_assets/chunks/f55334112f8e8be82d65db29887a663f-vDq1zH8k.woff2 +0 -0
- package/dist/client/_assets/chunks/f5738255e92d8dd34a46d1bcdf4c4074-tpghTzL1.woff2 +0 -0
- package/dist/client/_assets/chunks/f740e93f3c0277ecc616594103bca683-C4BgW_ko.woff2 +0 -0
- package/dist/client/_assets/chunks/f7633b5af033d76ff2fb3c3c266d77c5-BCkrZxwO.woff2 +0 -0
- package/dist/client/_assets/chunks/f7d8468cba2335a83ee414ea68291bab-CVnotkZU.woff2 +0 -0
- package/dist/client/_assets/chunks/f91cd722855f4269256eae1187df64ec-DJlAx1A_.woff2 +0 -0
- package/dist/client/_assets/chunks/f9695c6c4df2bf6bc03045ff79d4f01f-DDaYGYHT.woff2 +0 -0
- package/dist/client/_assets/chunks/fa6e58ce4b52695e7ae19bbea6336ec8-B30jEpze.woff2 +0 -0
- package/dist/client/_assets/chunks/fada6eaa68ff8816afe43d2a36c5423e-DlGAWNPb.woff2 +0 -0
- package/dist/client/_assets/chunks/fbcc4bf5367218951172bdee6f77d7a6-Dax2VSCd.woff2 +0 -0
- package/dist/client/_assets/chunks/fc895f5ce66b656f4a933097bf2a8775-DjjysNdf.woff2 +0 -0
- package/dist/client/_assets/chunks/fc9ae5b600fb711f2d67e93ce768cba4-D1lx7LBz.woff2 +0 -0
- package/dist/client/_assets/chunks/fca6720fd14c467d29a90f18ef3859b9-B3btrwq3.woff2 +0 -0
- package/dist/client/_assets/chunks/fd1bb507bcbf04856eeb7f7fd47ea579-NR1nGJTt.woff2 +0 -0
- package/dist/client/_assets/chunks/fd1bceb55d3e0183ac2454b8532fec7d-D3t-v6nn.woff2 +0 -0
- package/dist/client/_assets/chunks/fd2069e6e8588c70b4f11364093b81f2-BoXhYVgw.woff2 +0 -0
- package/dist/client/_assets/chunks/{url-CG0eolsk.js → url-pLre2DM_.js} +1 -1
- package/dist/client/_assets/client-C_kImWZj.css +2 -0
- package/dist/client/_assets/client-D95FNDg5.js +272 -0
- package/dist/client/_assets/client-auth-CXILhW1b.js +4547 -0
- package/dist/client/_assets/client-cjk-jp-DZwrTzQC.css +1 -0
- package/dist/client/_assets/client-cjk-kr-_3ZNI2ZP.css +1 -0
- package/dist/env-CgaH9Mut.js +252 -0
- package/dist/github-api-BkRWnqMx.js +176 -0
- package/dist/github-app-WeadXMb8.js +275 -0
- package/dist/github-sync-7y_nTXx1.js +4723 -0
- package/dist/index.js +88 -2
- package/dist/node.js +71 -30
- package/dist/url-umUptr5z.js +334 -0
- package/package.json +16 -12
- package/src/__tests__/bin/content-cli.test.ts +179 -0
- package/src/__tests__/bin/media-cli.test.ts +192 -0
- package/src/__tests__/export-hugo-build.test.ts +178 -0
- package/src/__tests__/export-import-roundtrip.test.ts +265 -0
- package/src/__tests__/export-service.test.ts +763 -510
- package/src/__tests__/helpers/app.ts +15 -4
- package/src/__tests__/helpers/export-fixtures.ts +108 -0
- package/src/__tests__/import-site-command.test.ts +358 -350
- package/src/__tests__/site-pull-media.test.ts +256 -0
- package/src/app.tsx +68 -13
- package/src/client/__tests__/collection-form-bridge.test.ts +87 -7
- package/src/client/__tests__/collection-page-actions.test.ts +4 -4
- package/src/client/__tests__/collection-picker-order.test.ts +53 -0
- package/src/client/__tests__/collection-sort-menu.test.ts +2 -2
- package/src/client/__tests__/compose-bridge.test.ts +305 -13
- package/src/client/__tests__/compose-launch.test.ts +120 -0
- package/src/client/__tests__/compose-shortcuts.test.ts +141 -0
- package/src/client/__tests__/feed-video-player.test.ts +44 -0
- package/src/client/__tests__/site-header-nav.test.ts +250 -0
- package/src/client/__tests__/sortable-list.test.ts +6 -2
- package/src/client/__tests__/thread-context.test.ts +117 -0
- package/src/client/collection-form-bridge.ts +109 -4
- package/src/client/collection-page-actions.ts +3 -1
- package/src/client/collection-picker-order.ts +104 -0
- package/src/client/components/__tests__/{jant-collection-sidebar.test.ts → jant-collection-directory.test.ts} +91 -10
- package/src/client/components/__tests__/jant-collection-form.test.ts +40 -18
- package/src/client/components/__tests__/jant-compose-dialog.test.ts +1285 -66
- package/src/client/components/__tests__/jant-compose-editor.test.ts +254 -19
- package/src/client/components/__tests__/jant-compose-fullscreen.test.ts +127 -1
- package/src/client/components/__tests__/jant-media-lightbox.test.ts +237 -0
- package/src/client/components/__tests__/jant-nav-manager.test.ts +326 -0
- package/src/client/components/__tests__/jant-post-menu.test.ts +372 -2
- package/src/client/components/__tests__/jant-settings-avatar.test.ts +61 -5
- package/src/client/components/__tests__/jant-settings-general.test.ts +29 -28
- package/src/client/components/__tests__/jant-text-preview.test.ts +206 -0
- package/src/client/components/collection-manager-types.ts +18 -1
- package/src/client/components/compose-types.ts +48 -0
- package/src/client/components/jant-collection-directory.ts +1448 -0
- package/src/client/components/jant-collection-form.ts +65 -17
- package/src/client/components/jant-command-palette.ts +544 -0
- package/src/client/components/jant-compose-dialog.ts +2490 -375
- package/src/client/components/jant-compose-editor.ts +420 -75
- package/src/client/components/jant-compose-fullscreen.ts +59 -8
- package/src/client/components/jant-media-lightbox.ts +373 -18
- package/src/client/components/jant-nav-manager.ts +624 -286
- package/src/client/components/jant-post-menu.ts +451 -126
- package/src/client/components/jant-repo-picker-types.ts +39 -0
- package/src/client/components/jant-repo-picker.ts +799 -0
- package/src/client/components/jant-settings-avatar.ts +16 -2
- package/src/client/components/jant-settings-general.ts +137 -35
- package/src/client/components/jant-text-preview.ts +239 -55
- package/src/client/components/nav-manager-types.ts +25 -9
- package/src/client/components/settings-types.ts +13 -2
- package/src/client/compose-bridge.ts +428 -68
- package/src/client/compose-launch.ts +41 -4
- package/src/client/compose-shortcuts.ts +77 -5
- package/src/client/feed-video-player.ts +374 -0
- package/src/client/image-processor.ts +53 -127
- package/src/client/media-lightbox-events.ts +1 -0
- package/src/client/media-scroll-hint.ts +66 -0
- package/src/client/multipart-upload.ts +4 -1
- package/src/client/palette-shortcuts.ts +35 -0
- package/src/client/search-rank.ts +74 -0
- package/src/client/settings-bridge.ts +7 -0
- package/src/client/site-header-nav.d.ts +1 -0
- package/src/client/site-header-nav.js +139 -51
- package/src/client/sortable-list.ts +2 -0
- package/src/client/thread-context.ts +54 -11
- package/src/client/tiptap/__tests__/block-insertion.test.ts +65 -0
- package/src/client/tiptap/__tests__/footnotes.test.ts +692 -0
- package/src/client/tiptap/__tests__/insert-paragraph-around.test.ts +228 -0
- package/src/client/tiptap/__tests__/link-toolbar.test.ts +120 -4
- package/src/client/tiptap/__tests__/markdown-clipboard.test.ts +48 -0
- package/src/client/tiptap/block-insertion.ts +36 -0
- package/src/client/tiptap/bubble-menu.ts +34 -4
- package/src/client/tiptap/create-editor.ts +49 -30
- package/src/client/tiptap/embed-dialog.ts +301 -0
- package/src/client/tiptap/embed-node.ts +373 -0
- package/src/client/tiptap/embed-paste.ts +91 -0
- package/src/client/tiptap/extensions.ts +80 -28
- package/src/client/tiptap/footnotes.ts +718 -0
- package/src/client/tiptap/html-block-node.ts +222 -0
- package/src/client/tiptap/image-node.ts +49 -6
- package/src/client/tiptap/inline-image-upload.ts +200 -40
- package/src/client/tiptap/insert-paragraph-around.ts +79 -0
- package/src/client/tiptap/link-input-rules.ts +50 -16
- package/src/client/tiptap/link-toolbar.ts +156 -133
- package/src/client/tiptap/markdown-clipboard.ts +120 -0
- package/src/client/tiptap/more-break.ts +36 -3
- package/src/client/tiptap/paste-media.ts +6 -0
- package/src/client/tiptap/slash-commands.ts +167 -14
- package/src/client/tiptap/tab-indent.ts +184 -0
- package/src/client/tiptap/toolbar-mode.ts +15 -2
- package/src/client/tiptap/wrapping-input-rules.ts +102 -0
- package/src/client/types/sortablejs.d.ts +9 -1
- package/src/client/upload-session.ts +2 -1
- package/src/client/video-processor.ts +177 -20
- package/src/client-auth.ts +9 -1
- package/src/client-site.ts +15 -0
- package/src/client.ts +2 -0
- package/src/db/__tests__/demo-canonical-snapshot.test.ts +1 -1
- package/src/db/__tests__/migration-rehearsal.test.ts +5 -1
- package/src/db/__tests__/migrations.test.ts +25 -2
- package/src/db/backfills/0001_strip_collection_path_c_prefix.sql +11 -0
- package/src/db/backfills/0002_strip_collection_path_c_prefix_fix.sql +12 -0
- package/src/db/backfills/0003_clear_system_nav_default_labels.sql +15 -0
- package/src/db/dialect.ts +40 -0
- package/src/db/migrations/0005_busy_the_phantom.sql +39 -0
- package/src/db/migrations/0006_adorable_magdalene.sql +3 -0
- package/src/db/migrations/0007_unusual_warstar.sql +1 -0
- package/src/db/migrations/0008_nasty_lockheed.sql +1 -0
- package/src/db/migrations/0009_clear_fixer.sql +31 -0
- package/src/db/migrations/0010_futuristic_preak.sql +31 -0
- package/src/db/migrations/0011_bizarre_smasher.sql +30 -0
- package/src/db/migrations/0012_furry_thena.sql +52 -0
- package/src/db/migrations/0013_mixed_lightspeed.sql +39 -0
- package/src/db/migrations/0014_high_the_santerians.sql +41 -0
- package/src/db/migrations/0015_skinny_shinobi_shaw.sql +1 -0
- package/src/db/migrations/0016_remarkable_nicolaos.sql +17 -0
- package/src/db/migrations/0017_powerful_moonstone.sql +14 -0
- package/src/db/migrations/0018_yummy_franklin_richards.sql +6 -0
- package/src/db/migrations/meta/0005_snapshot.json +1904 -0
- package/src/db/migrations/meta/0006_snapshot.json +1925 -0
- package/src/db/migrations/meta/0007_snapshot.json +1933 -0
- package/src/db/migrations/meta/0008_snapshot.json +1940 -0
- package/src/db/migrations/meta/0009_snapshot.json +1952 -0
- package/src/db/migrations/meta/0010_snapshot.json +1952 -0
- package/src/db/migrations/meta/0011_snapshot.json +1948 -0
- package/src/db/migrations/meta/0012_snapshot.json +1955 -0
- package/src/db/migrations/meta/0013_snapshot.json +1977 -0
- package/src/db/migrations/meta/0014_snapshot.json +1988 -0
- package/src/db/migrations/meta/0015_snapshot.json +1995 -0
- package/src/db/migrations/meta/0016_snapshot.json +2104 -0
- package/src/db/migrations/meta/0017_snapshot.json +2188 -0
- package/src/db/migrations/meta/0018_snapshot.json +2225 -0
- package/src/db/migrations/meta/_journal.json +98 -0
- package/src/db/migrations/pg/0003_motionless_norrin_radd.sql +21 -0
- package/src/db/migrations/pg/0004_nervous_captain_midlands.sql +3 -0
- package/src/db/migrations/pg/0005_romantic_mesmero.sql +1 -0
- package/src/db/migrations/pg/0006_perpetual_bruce_banner.sql +1 -0
- package/src/db/migrations/pg/0007_nav_item_placement.sql +1 -0
- package/src/db/migrations/pg/0008_yielding_frightful_four.sql +3 -0
- package/src/db/migrations/pg/0009_outstanding_ogun.sql +1 -0
- package/src/db/migrations/pg/0010_overjoyed_gertrude_yorkes.sql +28 -0
- package/src/db/migrations/pg/0011_fixed_hulk.sql +19 -0
- package/src/db/migrations/pg/0012_cute_shockwave.sql +2 -0
- package/src/db/migrations/pg/0013_bizarre_obadiah_stane.sql +1 -0
- package/src/db/migrations/pg/0014_tearful_grim_reaper.sql +16 -0
- package/src/db/migrations/pg/0015_daffy_mikhail_rasputin.sql +14 -0
- package/src/db/migrations/pg/0016_familiar_lionheart.sql +6 -0
- package/src/db/migrations/pg/meta/0003_snapshot.json +2482 -0
- package/src/db/migrations/pg/meta/0004_snapshot.json +2500 -0
- package/src/db/migrations/pg/meta/0005_snapshot.json +2507 -0
- package/src/db/migrations/pg/meta/0006_snapshot.json +2513 -0
- package/src/db/migrations/pg/meta/0008_snapshot.json +2524 -0
- package/src/db/migrations/pg/meta/0009_snapshot.json +2520 -0
- package/src/db/migrations/pg/meta/0010_snapshot.json +2526 -0
- package/src/db/migrations/pg/meta/0011_snapshot.json +2563 -0
- package/src/db/migrations/pg/meta/0012_snapshot.json +2573 -0
- package/src/db/migrations/pg/meta/0013_snapshot.json +2579 -0
- package/src/db/migrations/pg/meta/0014_snapshot.json +2702 -0
- package/src/db/migrations/pg/meta/0015_snapshot.json +2803 -0
- package/src/db/migrations/pg/meta/0016_snapshot.json +2840 -0
- package/src/db/migrations/pg/meta/_journal.json +98 -0
- package/src/db/pg/__tests__/node.test.ts +38 -0
- package/src/db/pg/node.ts +149 -0
- package/src/db/pg/schema.ts +148 -12
- package/src/db/rehearsal-fixtures/demo-current.sql +47 -3
- package/src/db/schema.ts +162 -12
- package/src/i18n/Trans.tsx +3 -3
- package/src/i18n/__tests__/context.test.tsx +9 -6
- package/src/i18n/__tests__/detect.test.ts +65 -48
- package/src/i18n/__tests__/fallback.test.ts +40 -0
- package/src/i18n/__tests__/message-placeholders.test.ts +43 -0
- package/src/i18n/__tests__/middleware.test.ts +83 -0
- package/src/i18n/context.tsx +9 -44
- package/src/i18n/detect.ts +95 -28
- package/src/i18n/i18n.ts +41 -17
- package/src/i18n/index.ts +7 -5
- package/src/i18n/locales/{en.po → public/en.po} +284 -1151
- package/src/i18n/locales/public/en.ts +1 -0
- package/src/i18n/locales/public/zh-Hans.po +2327 -0
- package/src/i18n/locales/public/zh-Hans.ts +1 -0
- package/src/i18n/locales/public/zh-Hant.po +2327 -0
- package/src/i18n/locales/public/zh-Hant.ts +1 -0
- package/src/i18n/locales/settings/en.po +1622 -0
- package/src/i18n/locales/settings/en.ts +1 -0
- package/src/i18n/locales/settings/zh-Hans.po +1622 -0
- package/src/i18n/locales/settings/zh-Hans.ts +1 -0
- package/src/i18n/locales/settings/zh-Hant.po +1622 -0
- package/src/i18n/locales/settings/zh-Hant.ts +1 -0
- package/src/i18n/middleware.ts +37 -24
- package/src/index.ts +4 -6
- package/src/lib/__tests__/collection-groups.test.ts +17 -0
- package/src/lib/__tests__/constants.test.ts +2 -24
- package/src/lib/__tests__/csp-builder.test.ts +68 -0
- package/src/lib/__tests__/display-text.test.ts +27 -0
- package/src/lib/__tests__/embed-providers.test.ts +104 -0
- package/src/lib/__tests__/embed-render.test.ts +60 -0
- package/src/lib/__tests__/feed.test.ts +125 -63
- package/src/lib/__tests__/hosted-signin.test.ts +41 -2
- package/src/lib/__tests__/markdown-roundtrip-embed.test.ts +88 -0
- package/src/lib/__tests__/markdown-to-tiptap.test.ts +59 -0
- package/src/lib/__tests__/markdown.test.ts +47 -5
- package/src/lib/__tests__/navigation.test.ts +132 -0
- package/src/lib/__tests__/post-display.test.ts +3 -2
- package/src/lib/__tests__/post-meta.test.ts +3 -0
- package/src/lib/__tests__/rate-limit-d1.test.ts +82 -0
- package/src/lib/__tests__/rate-limit-memory.test.ts +69 -0
- package/src/lib/__tests__/resolve-config.test.ts +30 -25
- package/src/lib/__tests__/schemas.test.ts +20 -3
- package/src/lib/__tests__/summary.test.ts +540 -1
- package/src/lib/__tests__/theme.test.ts +41 -2
- package/src/lib/__tests__/timeline.test.ts +33 -27
- package/src/lib/__tests__/tiptap-render.test.ts +224 -0
- package/src/lib/__tests__/tiptap-to-markdown.test.ts +51 -0
- package/src/lib/__tests__/video-playback.test.ts +48 -0
- package/src/lib/__tests__/view.test.ts +181 -8
- package/src/lib/__tests__/worker-response-cache.test.ts +311 -0
- package/src/lib/__tests__/youtube.test.ts +99 -0
- package/src/lib/api-media.ts +55 -0
- package/src/lib/api-posts.ts +99 -0
- package/src/lib/api-search.ts +47 -0
- package/src/lib/api-settings.ts +122 -0
- package/src/lib/asset-path.ts +20 -2
- package/src/lib/collection-groups.ts +2 -1
- package/src/lib/collection-paths.ts +40 -0
- package/src/lib/constants.ts +3 -24
- package/src/lib/csp-builder.ts +99 -0
- package/src/lib/decorative-quote-mark.ts +11 -0
- package/src/lib/display-text.ts +56 -0
- package/src/lib/embed-providers.ts +289 -0
- package/src/lib/embed-render.ts +151 -0
- package/src/lib/env.ts +70 -1
- package/src/lib/excerpt.ts +11 -2
- package/src/lib/feed.ts +154 -95
- package/src/lib/footnotes.ts +146 -0
- package/src/lib/github-api.ts +423 -0
- package/src/lib/github-app-state.ts +135 -0
- package/src/lib/github-app.ts +487 -0
- package/src/lib/github-sync-queue-handler.ts +69 -0
- package/src/lib/github-sync-site-config.ts +57 -0
- package/src/lib/github-sync-status.tsx +87 -0
- package/src/lib/github-sync-trigger.ts +199 -0
- package/src/lib/github-sync-worker.ts +72 -0
- package/src/lib/hosted-signin.ts +9 -3
- package/src/lib/hugo-markdown.ts +255 -0
- package/src/lib/job-queue-cf.ts +18 -0
- package/src/lib/job-queue-db.ts +149 -0
- package/src/lib/job-queue.ts +35 -0
- package/src/lib/markdown-manager.ts +864 -0
- package/src/lib/markdown-to-tiptap.ts +4 -323
- package/src/lib/markdown.ts +16 -24
- package/src/lib/media-helpers.ts +1 -0
- package/src/lib/navigation.ts +50 -19
- package/src/lib/post-display.ts +10 -22
- package/src/lib/post-meta.ts +20 -2
- package/src/lib/public-storage.ts +6 -1
- package/src/lib/rate-limit-d1.ts +99 -0
- package/src/lib/rate-limit-memory.ts +105 -0
- package/src/lib/rate-limit.ts +63 -0
- package/src/lib/render.tsx +14 -2
- package/src/lib/resolve-config.ts +32 -11
- package/src/lib/rich-image.ts +89 -0
- package/src/lib/schemas.ts +180 -13
- package/src/lib/summary.ts +162 -20
- package/src/lib/theme.ts +119 -0
- package/src/lib/timeline.ts +63 -42
- package/src/lib/tiptap-render.ts +265 -126
- package/src/lib/tiptap-to-markdown.ts +6 -329
- package/src/lib/upload.ts +97 -6
- package/src/lib/url.ts +34 -0
- package/src/lib/version.ts +18 -0
- package/src/lib/video-playback.ts +73 -0
- package/src/lib/view.ts +168 -94
- package/src/lib/webhook-signature.ts +65 -0
- package/src/lib/worker-response-cache.ts +220 -0
- package/src/lib/youtube.ts +119 -0
- package/src/middleware/__tests__/auth.test.ts +44 -4
- package/src/middleware/__tests__/onboarding.test.ts +3 -3
- package/src/middleware/__tests__/rate-limit.test.ts +113 -0
- package/src/middleware/__tests__/secure-headers.test.ts +28 -1
- package/src/middleware/__tests__/session.test.ts +85 -0
- package/src/middleware/auth.ts +62 -25
- package/src/middleware/config.ts +7 -10
- package/src/middleware/cors.ts +49 -0
- package/src/middleware/rate-limit.ts +54 -0
- package/src/middleware/secure-headers.ts +45 -55
- package/src/middleware/session.ts +36 -0
- package/src/node/__tests__/cli-migrate.test.ts +111 -0
- package/src/node/__tests__/cli-runtime-target.test.ts +1 -1
- package/src/node/__tests__/cli-site-token-env.test.ts +2 -2
- package/src/preset.css +316 -12
- package/src/routes/__tests__/compose.test.ts +4 -2
- package/src/routes/api/__tests__/collections.test.ts +81 -27
- package/src/routes/api/__tests__/mcp.test.ts +389 -0
- package/src/routes/api/__tests__/nav-items.test.ts +163 -2
- package/src/routes/api/__tests__/posts.test.ts +190 -0
- package/src/routes/api/__tests__/search.test.ts +48 -0
- package/src/routes/api/__tests__/settings.test.ts +99 -3
- package/src/routes/api/__tests__/upload-multipart.test.ts +21 -6
- package/src/routes/api/__tests__/upload.test.ts +132 -0
- package/src/routes/api/__tests__/uploads.test.ts +5 -3
- package/src/routes/api/collections.ts +67 -26
- package/src/routes/api/custom-urls.ts +1 -0
- package/src/routes/api/export.ts +8 -4
- package/src/routes/api/github-sync.tsx +380 -0
- package/src/routes/api/internal/__tests__/sites.test.ts +45 -2
- package/src/routes/api/internal/search-reindex.ts +40 -0
- package/src/routes/api/internal/sites.ts +2 -0
- package/src/routes/api/internal/text-attachments.ts +60 -0
- package/src/routes/api/internal/uploads.ts +5 -1
- package/src/routes/api/mcp.ts +30 -0
- package/src/routes/api/nav-items.ts +30 -11
- package/src/routes/api/palette.ts +17 -0
- package/src/routes/api/posts.ts +37 -106
- package/src/routes/api/public/__tests__/posts.test.ts +372 -0
- package/src/routes/api/public/posts.ts +302 -0
- package/src/routes/api/search.ts +13 -46
- package/src/routes/api/settings.ts +44 -58
- package/src/routes/api/upload-multipart.ts +15 -5
- package/src/routes/api/upload.ts +62 -49
- package/src/routes/api/uploads.ts +1 -0
- package/src/routes/auth/__tests__/hosted-sso.test.ts +33 -0
- package/src/routes/auth/__tests__/setup.test.ts +29 -4
- package/src/routes/auth/dev.ts +1 -1
- package/src/routes/auth/hosted-sso-expired-page.tsx +89 -0
- package/src/routes/auth/hosted-sso.ts +42 -1
- package/src/routes/auth/reset.tsx +46 -32
- package/src/routes/auth/setup.tsx +53 -43
- package/src/routes/auth/signin.tsx +57 -29
- package/src/routes/compose.tsx +114 -1
- package/src/routes/dash/__tests__/font-theme.test.ts +6 -27
- package/src/routes/dash/__tests__/settings-avatar.test.ts +2 -3
- package/src/routes/dash/custom-urls.tsx +250 -112
- package/src/routes/dash/settings.tsx +1182 -75
- package/src/routes/feed/__tests__/{rss.test.ts → feed.test.ts} +62 -123
- package/src/routes/feed/__tests__/sitemap.test.ts +320 -4
- package/src/routes/feed/feed.ts +386 -0
- package/src/routes/feed/manifest.ts +67 -0
- package/src/routes/feed/sitemap.ts +211 -27
- package/src/routes/pages/__tests__/collection-routing.test.ts +176 -0
- package/src/routes/pages/__tests__/collections.test.ts +2 -3
- package/src/routes/pages/__tests__/featured.test.ts +2 -3
- package/src/routes/pages/__tests__/page-canonical.test.ts +101 -0
- package/src/routes/pages/archive.tsx +523 -127
- package/src/routes/pages/collection.tsx +197 -43
- package/src/routes/pages/collections.tsx +5 -1
- package/src/routes/pages/home.tsx +26 -17
- package/src/routes/pages/latest.tsx +3 -3
- package/src/routes/pages/page.tsx +255 -3
- package/src/routes/pages/partials.tsx +8 -14
- package/src/routes/pages/search.tsx +17 -1
- package/src/routes/pages/theme-sample.tsx +1 -2
- package/src/runtime/__tests__/node.test.ts +1 -1
- package/src/runtime/cloudflare.ts +4 -0
- package/src/runtime/node.ts +16 -0
- package/src/runtime/readiness.ts +1 -1
- package/src/runtime/site.ts +9 -1
- package/src/services/__tests__/collection.test.ts +137 -54
- package/src/services/__tests__/github-app-installations.test.ts +181 -0
- package/src/services/__tests__/github-sync-classify.test.ts +189 -0
- package/src/services/__tests__/github-sync-push.test.ts +159 -0
- package/src/services/__tests__/media.test.ts +444 -7
- package/src/services/__tests__/navigation.test.ts +271 -14
- package/src/services/__tests__/post-timeline.test.ts +31 -21
- package/src/services/__tests__/post.test.ts +288 -9
- package/src/services/__tests__/search.test.ts +44 -0
- package/src/services/__tests__/settings.test.ts +43 -21
- package/src/services/auth.ts +2 -2
- package/src/services/bootstrap.ts +10 -7
- package/src/services/collection.ts +591 -193
- package/src/services/custom-url.ts +34 -7
- package/src/services/export-theme/assets/client-site.css +2 -0
- package/src/services/export-theme/assets/client-site.js +145 -0
- package/src/services/export-theme/layouts/_default/alias.html +49 -0
- package/src/services/export-theme/layouts/_default/baseof.html +21 -0
- package/src/services/export-theme/layouts/_default/list.html +104 -0
- package/src/services/export-theme/layouts/_default/rss.xml +160 -0
- package/src/services/export-theme/layouts/_default/single.html +15 -0
- package/src/services/export-theme/layouts/archive/list.html +31 -0
- package/src/services/export-theme/layouts/collection/single.html +19 -0
- package/src/services/export-theme/layouts/collections/list.html +111 -0
- package/src/services/export-theme/layouts/featured/list.html +28 -0
- package/src/services/export-theme/layouts/index.html +41 -0
- package/src/services/export-theme/layouts/partials/feed-post-content.xml +107 -0
- package/src/services/export-theme/layouts/partials/footer.html +32 -0
- package/src/services/export-theme/layouts/partials/head.html +52 -0
- package/src/services/export-theme/layouts/partials/header.html +115 -0
- package/src/services/export-theme/layouts/partials/media-gallery.html +246 -0
- package/src/services/export-theme/layouts/partials/pagination.html +55 -0
- package/src/services/export-theme/layouts/partials/post-card.html +127 -0
- package/src/services/export-theme/layouts/partials/reply.html +91 -0
- package/src/services/export-theme/layouts/partials/thread-preview.html +82 -0
- package/src/services/export-theme/layouts/post/list.html +129 -0
- package/src/services/export-theme/styles/main.css +1981 -0
- package/src/services/export-theme/theme.toml +12 -0
- package/src/services/export.ts +1372 -2412
- package/src/services/github-app-installations.ts +302 -0
- package/src/services/github-sync.ts +769 -0
- package/src/services/index.ts +15 -0
- package/src/services/mcp.ts +1324 -0
- package/src/services/media.ts +336 -25
- package/src/services/navigation.ts +115 -23
- package/src/services/path.ts +129 -28
- package/src/services/post.ts +858 -197
- package/src/services/search.ts +3 -0
- package/src/services/settings.ts +29 -15
- package/src/services/site-admin.ts +26 -6
- package/src/services/upload-session.ts +69 -34
- package/src/style-cjk-jp.css +3 -0
- package/src/style-cjk-kr.css +3 -0
- package/src/styles/components.css +160 -59
- package/src/styles/fonts/et-book/et-book-bold-line-figures.woff +0 -0
- package/src/styles/fonts/et-book/et-book-display-italic-old-style-figures.woff +0 -0
- package/src/styles/fonts/et-book/et-book-roman-line-figures.woff +0 -0
- package/src/styles/fonts/et-book.css +31 -0
- package/src/styles/fonts/latin.css +1 -0
- package/src/styles/fonts/noto-serif-jp/400/057a6a98bda7fe57105ddaa99ec82015.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/0acd1fe2b2ea1ad1bfee7ae1fa139d27.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/0c2ab6b295e55f356f8020d4e7747522.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/0c5f9492af03a4fa42c784de94649de1.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/0d9a936885a4c39077438effd3779cbd.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/0e645da524f7cfc0e8c3c03fb2b08428.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/135e83b403475c5dc9e49b43501a5b84.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/13b2c53b30e6a3e4de7132dbc18dbdfc.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/15d95680dc31cc6ce20e0a5464106dc4.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/1733f27476507ca68b68a803bc533cc2.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/188c2db794f3dd7a45889ddbc81da9bb.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/1cf737900dd49c2e88f1b3221a82a602.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/233d3a685ee18276b319363474599d47.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/284b53bbefb06924cf236d24c8ed5641.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/2a56eaf19d1d38a6b57e2a388f733676.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/2abfbab82b6a7c04426afc054d2464da.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/2e6f4bb71ef6b38765d51acc5a79f638.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/31194d303a67561926a544ed0e072aee.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/3623d96698132c61e861f984ce11cc23.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/39211b02f24c69cbd1b54f6d41b01933.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/3e24063b19abd2b051af986a6870046d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/4410f876d27c9821839474b6b1059fd7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/479329a39a3eb6c0047e6b0981919855.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/527ff336279465617aafbafe1a830632.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/56b40518ea0608e62826bd44ee27f5f9.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/5acc7f736217259db79a42bf44241c48.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/5c05f8b75c9d7f25b3c04ca711cf43b3.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/5e0124c7265f1b4cf0fc797b94efbebc.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/615a7e0d63f257109d599b2a1977de68.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/61a2c80d0c924d5a6187c02e8d1d1642.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/62fc94f8d4c5a750b7f25e7387539910.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/639c7c6b0b0c27c738702741cfa4b8c0.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/68a3a4bf337f8a0722be76676e20b850.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/6abdd163d2c4b85698db8aa1ce149361.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/73b794d885b88f7befb7ea8ab1780a15.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/743f290baf027d4626a86e22f3d44600.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/757019c01c5e2c2b0f54293cea3b5636.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/76f117a3baacda304a71965a17266a13.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/77747f17625a259fb405b7bbdd84ac4b.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/788498548ddfb710d6ef3c7bff79fa97.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/78ea6c40923ce95367e3517dd1e5a849.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/7f9a6c9286b68de9c72b0024f7beeb40.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/823d1bb11097331238d103c7f72138dc.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/888ba53510213d5d1f6a7deb60e569b6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/892aa49b2529c89bb4076d4aa51fe30f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/95ff41191c76ef893ac70a1043e95e44.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/96c75862c8cec51a7c05ff025fea86cd.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/9b21e8244f5930c48ad5073f83777b6c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/9fce02d1a09f464c36c9fcfb14a354c5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/a0a6755c7e3f9e4cf03387027dd9f16c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/a4475c442c6b259354c9eda62e64e7df.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/a4a39b5f048671aa37c2b2a9581ab08a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/a534449d67561ac9b06489f64e57ad98.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/a919a8c6fec6d7e8bb21177965f2e9d7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/ad67113f88b59582991cb0c5d33ea19f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/adb6e613fb99c6dd660b727101f554a8.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/adc4a98a89870ef984ee8f4b57c8a3a5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/b12379ab782468d725519cd07a7d15bc.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/b55486f8a459c838fe329d4e79a8c211.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/bb0b15492b8cdbbec57c0bcfa0aa9241.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/bedc74b423b7293b6ad0bdecc61c42cc.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/bfb00d4a4c48661bd0be99f300a0faae.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/c1b0df29ae41d764904df84e9ac83d1e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/c42c67070cfe99cf823df92d81c7fa6e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/c4d749e45ecd5a5aed5a0bb3ebfd355d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/c96d83978add28b356c22c4c84916733.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/cb7ccd6494256f7a2977b7c2b0225592.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/ce41d70ce6a069a498525c9e15c45cf2.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/d509ee5c8241bc7c3de2039d75564fa5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/d675d717cd329bfd4c0524f76ae1579c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/dc8f6256445e68199540be9ade33529f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/e8c15be62faf978d208925e79ea6a10d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/e91c4d941ed9f5c2f3e27f205a3a225e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/e9c566be7a5d38a9085225f7372bc82b.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/e9ebc567a711eeb29019ddae3e0ce7fe.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/ecb8875a56c7b038b35432fda41ae128.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/f1661731474b78bdf81114daf10b254f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/f2c58dee206ba9355046fc23d05491f7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/f50ac27ea4358d67fdda403c2bb52467.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/f55334112f8e8be82d65db29887a663f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/f740e93f3c0277ecc616594103bca683.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/f7633b5af033d76ff2fb3c3c266d77c5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/f7d8468cba2335a83ee414ea68291bab.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/fc9ae5b600fb711f2d67e93ce768cba4.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/fd1bceb55d3e0183ac2454b8532fec7d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/02611a045a7fe83a12014e3debc9f731.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/0f5e1a18987dbc84ca05188c129e1936.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/112743a4ab5fdd1498dfdf2b11336380.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/1211f03d3ff5759a702631445793f60e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/147b24c2f08d03bbed30887d4cb3311c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/16663e567f81f4725a1522f37e18f71f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/1824321da801e6257b902f3d1d09054c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/1a3a92c7c060a71a6c35819b9ebcdbb9.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/1fba7ec0e412e911bf31841de5a8a4e4.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/28a97af9ab9d38983d20f39ff09840e1.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/2bfaadaf3479c72286248e6de0be0ec9.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/2c8c55e4cec85ce77e95cac9d330f5cf.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/2ed9981d2e8983365bd051159b976b6d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/2f4c633e923ba30c6ba376367379fc91.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/349965eee0a9b6c984a319ab96a4ece9.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/36cda3eae13370b934bba4429fab9078.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/3aa13f1843d7c8fa16109b6395a9a29d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/3e2c06bdd9dcab29aeaeb0cfb7fa608a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/424123f9371ba357087363263eeafcda.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/43b3bbf43055c3e65db9cdf2c5b18c1c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/44704471b60c18b3ecec5300a88f1102.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/46604090efbb99c2db1800928af0a239.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/46998df85d31e629c0142f0556f5e7c5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/49902f1c41b281f8eb3771abdd4dccb8.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/4db4f31a16965baa42cb7dad667a8f04.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/572b9e28f1e6d89b765a16d809db84a1.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/5921cef36750ff6092d94b83a802823b.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/59e95d8b5332dcdae894a19946ab767d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/5ec349ff62b9ee5fe0c6dae867704dc4.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/601f812e8b1eafecd5ba585a6a6d7962.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/6025ddaf99d86cc8bfd781006b19fbd6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/60f6e28b3f0400b0e8fe32c3c7116102.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/64a404d675f1d726f0891b7a80794595.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/7158022889c6c177062ac85036e7af10.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/7e38ca789a9e76ac91d9256374e154f0.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/815a0b797465190f60d8b1a04656c42e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/8726558cf297cbda831cc19514897205.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/8855472f3c02f6c7ebb3216617ebe4af.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/8e231d707f0c4f8e3cde90a6b52a79aa.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/9064c12fd72c7aba235d8c3881755b91.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/911b9e53e9814de2998c60bf3115f560.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/92fdc376bce277874e75db666f175b44.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/930a23430f0eb64480d7fe5f82834e21.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/93bed0e5f2dd5a21cf73304fcfbc149d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/93f3ea6918533d96d9c252378b9a4af0.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/95a0951d2a2722ff773a1a45e8c474d6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/982cd1765ca6bbb3e6547e47834d5148.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/9b0c3caac458c7bc9c9133e40405ddea.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/9f3bdab4025dc7c5ba1a5871e62e8918.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/a00e94d4f04df6aa659db9c4954c7efe.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/a2b0597837e382aca19919c4b74e58c8.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/a373e85c96aa0dc303092429fb837e51.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/a6e044424780a57610833cc856c15bf5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/a7c58070d68dc1724e9d4e781afb78db.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/accc43762ad05edfbff66ba1380d192a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/ad47bcd6d4663026d648c132d969318d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/af57a2e8c72f9ba0efb1af771d32c124.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/b0ae5f374e43dac992a4a5d334cebc0b.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/b91234c10fd8b8c8abc88e03afe66a1f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/b9317198f118a1dfd8ddf2b82ec028f3.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/ba825ae79b1a6f7e0cce5215fcb5c96f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/bc6183ac08f0fac78c46f80c10cf7c92.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/bc8ccea5abf6598cf3cfa97eb59804bb.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/bc95a792cbca6639214c9b0da13392ff.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/bcfa62f35731856246c146d3a6932bf3.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/c063897793f593eb26d6ff7b7baaba18.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/c6c2971ad1f3221f6cf84028aa0f477e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/c97f41eef722121d86f55d553c056a39.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/ca62704509932d3232d62918de97af3f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/d1f064825fa5784b5c930652bd831cce.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/d2faabcedd19f016e7b21bce073c0ec8.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/d320171e57480510f87dbbc7d5264b0a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/d3a72a99d365dddfbca8d017a8011368.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/d42aafa0f246ad3b4c16fe96d3a3a432.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/d4aaf23c13ae808a4bed617afd13aa07.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/d6bb686bddfbe8f38a36a68e609a8667.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/d9020ff69a83b2a6ee1f42ae480f7db0.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/da90a9012ab2d98f759e3fa0820ef502.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/df10d94bea357a43313e20da5d84bd30.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/e34b2b141e472dc776c86fdf8eea23b0.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/e444fd88f1390636e603d0d681538ac8.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/e79898628283edc27180fc39d0d769c1.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/eaf332445a40942928e5f5750c1c3116.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/f91cd722855f4269256eae1187df64ec.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/fc895f5ce66b656f4a933097bf2a8775.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/fca6720fd14c467d29a90f18ef3859b9.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/fd1bb507bcbf04856eeb7f7fd47ea579.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/noto-serif-jp.css +3349 -0
- package/src/styles/fonts/noto-serif-kr/400/033466ef683afe931f7f520cfb42d928.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/05da12edb9d52210581dc6ec4541031f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/0968e4861204b51f62a2f8e9f15dd5e0.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/0d5dec931dc885f07fe5cd5af8bed675.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/1076f0f6f66d28d7a2f16427faad4413.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/12ef1ba76bd20b004b865266a1aa70b3.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/152395634a207579552f8cb54db88599.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/1a08931435f885e707edb85978ea3bb6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/31a197213ae61d7043c81013f52d10d6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/37dbd564820cce2f7b3331e442da0df3.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/37dc8505b20b37a2477a9121b16d44a7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/3ac0a07878cacdc98bb889f40f5970d3.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/491bbba374093fff045f55803a22bfef.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/4d38e56738156625329d93bd5c8cc74a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/57e9a86651366c1ba299e47aa7e358c2.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/57f89adeae55aa7fe2add3fc1801ce03.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/5df9ce98c75f8c50fb4fff7f01a23925.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/60020a830d809c26970cf8e48ba1eeda.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/60bcfa3ea7446eb42394aab02d28be40.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/63fafcb069520613d0ea35ad3e6b1e42.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/65a60d87c64228d258a123cbe85f5f31.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/6968e889e18891d912809fe484c2e745.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/70714891ad3fbfc3d5f10a8669dacc5a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/761d84afca8d7f34eebefe538adba827.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/76e07530323418ec723c5d7634c9bcca.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/773819b7b9b8fd404a929867c0fd677e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/7817dd16805145d8538ad57590f69f5a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/7b59f0ec7792b18458dc5a361e37884c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/7c3945788a689a69356c1a622d69d48b.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/835b6505bb9eea9678925a1fa885353d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/8b5c9ef81159f31d2ab35f45ca4373e0.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/8b91c7c2ed390f1278b9befa3aa87233.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/8fb45117a62d92dce44a80f0b729ead5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/90b7848e9b1623b77bdcf155e90b839c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/985d4d41afa0934a4eb2de35473fb9a2.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/9bb38fd05201de666b88b82d56a386f7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/9eff2ab2a9d2ced47ab761bf6fdb11ec.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/9f1963fe8d4d6878d717d872a8f3fdb5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/a0d00fe4816c95a8c7dffd445ef00b03.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/a0f199077fa1a33bc2a1e01c64de7fe6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/a7c35e42a659347e490c3cb7983b2e7f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/aa662092cf249707123ca4d575e2764b.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/aae1eda193285ab817a2d1b408440326.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/b0cb664cb2e1371efda8943c0b7dcd1c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/b0d1cdced482352cf0d3ae58638aacb9.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/b320f5d185b2cff933ac549c184031c5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/ba9c59d7dfa4494db1bb764ada81467d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/bab547459d514f46206e340c4bb2dc88.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/bcc39eda837bb7a7a3d37c8c60fffb81.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/bd73264d7f98776708d5d6f3c9b78fcc.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/bed610b217d500f5975cfc9fe6157570.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/bf8901f8f11d4f433ea17dabc9370ea6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/c4143bb9f2fe77b6ccf20088a8904650.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/c7ff3f6bbdcd5f604b7343602ab904df.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/c945c62368357d05a53206620460fb30.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/ce022e18a1377ac509443c3c3790b431.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/cf9cffe56636322f62b40d61130fbc5e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/d20d8944bc0b85f5b2aae4b24f343516.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/d36644d6502527e1fff205d0c7eca434.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/d40ab99c7a38026f411c8f112f742b48.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/dc6e234ded795e91f76d6647f628fbf0.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/dd0cfdfdac0866e66d587e2b5a9e9961.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/de304c8a02e45ded4f8dcc479d167198.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/e8c364c16daa04835bf32d293d2598db.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/ec86e23683052da5cfdc3b77641bd15a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/eda1b0cb6d1719dd9bedcf3216a9e8de.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/ee6fce20b420a480714607c66d7f97e5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/eee3836d6ac17ebb2c450bbcbc9db121.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/f5738255e92d8dd34a46d1bcdf4c4074.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/fa6e58ce4b52695e7ae19bbea6336ec8.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/fd2069e6e8588c70b4f11364093b81f2.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/04b8bde59cff68eeee74fb1914eb09c7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/05ac821472e235943ed1435e4bb8ecad.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/074c1f8483d5a4d8c45c8c5f3e4cbb32.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/1380210a722ac9aeef54005ad7b015c9.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/17c7f5d0a45e92ede0e5dec3a2c77efa.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/1be602ad456b0d75902d352116cb35fd.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/2612c60f9dc5459ac42763591240a1d6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/2ab8cf1f23a5ac7939a7876054e711a7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/34e6750e00c3a911ef87dc66b336594d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/36f3df4730cfca4fc77fe52b52e6a126.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/37887deb7a9c466cf2af6ee7ff372ea6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/3aba89c4d4281553078de4622b498cbb.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/3d88558097559d775231194d43745ba7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/3dc3e8c35524cad2d1236f3393104ccf.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/45140742dfa8686b58db2899ce89f89a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/469d2754e315e77270c12fa0ab28026c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/4c0760284569b87d8f2290c324953973.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/4cba92c942694bdc479bc54101bb1aa4.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/4e7d263c30bb48604069a3b4404d4eeb.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/5279c7e465d9871edf51fd02e691eee7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/54bfdb6f21f9d6191e5100580239e14f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/56be5e32fd01668fe5ba814d94905839.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/615b38e0b5bb3ea68426f44f47706e6f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/65e9c4585d71bf48a5c62f367010da16.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/6b75213eb0be40ce84241eb2bb438a2e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/7520ca6ca8c60eb1e62d50e71c8d30f1.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/759a647b77791b1e98f99bc0ab5317a7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/76e51630143b95b6322ef93ad330da7a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/79aa7c8c842c4a27cf57c0a3a1ffca5a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/7cc72fd2c9105560422b6a67c6162945.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/7d0ea0690e432462f4d05a23d720ec19.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/7dffa5c1bec57e0903fd62357401ff1a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/82f6ccc063960eed1cdfd1d61ada8862.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/845b4b67564d62cf5cad242f7ac08ea5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/84c8e7bc0931008ed91f44ed12dfea94.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/887a11290ba78b1e66c6d2f67043005e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/88db1d042074fb6e66821ffc10941930.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/8dc18c0cebe6aa4bf4c45dbb831048ab.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/919a879bd2d580d8491a31a449390689.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/91da6cb174bebfb96976e2f1316be2fe.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/934dfdbed2bb2c4b6129199d699a34fa.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/97d7a17234f2b5030ae9697ae00aded2.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/995c4fda62d25c3b79dd5987737df6ae.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/9d0afa53dc2f92ba42024f55f11f6779.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/9e13a47242926f1cd7d68c08d9ef8889.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/a379ada89abd750c587ded29f77e731c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/a48be56ce5d3a99dc8f8331398004412.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/a806759e72987951d6d08b7cbdf36a1b.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/a88481ec8bc7be11cb66e46781a79bb9.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/a9a016d4a93409f65278327e8a8bb38d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/ab92d41062a7644faa45f50bf384454a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/b326a8a9c33b554db570da94f60bc380.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/b6117ff2993b11bb1fdc7ea3588a010c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/b61982951bd51b724143c30dfaaa9fe9.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/c0098958e20db68cab90097b5e62516f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/c4bfaa5e50798246e3770718b7a7c84a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/c84598999133455503042e06f4ab79cb.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/ca9a533988d7019597a60d4e17127e0c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/cf2b28f90f47276f7e2688a65e88a101.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/d689b1861d7e4377dd72ad3013482612.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/d9047070d72a816b3dba9d40c2d85e69.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/da04549f3f4ed28076b01b8cd710d313.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/da7af303f8c645f9a9dbae0e6e32dd35.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/df9568257eb29b156449fdd4bec5ec76.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/e067cd0ed76c90cd0a93c9339253f20b.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/e08b07772e7bed3cec2832d43f7fd339.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/e12150d5a39b30be8f567968c7a527b0.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/e53fcb2381eee345db4f6f973dd95a3e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/e5bd313ef81f687d398aacb11cec3069.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/eb5afb3d952b8593782caec6026514b6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/edd6a4f608d04fc0351d7688cfc321e4.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/f9695c6c4df2bf6bc03045ff79d4f01f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/fada6eaa68ff8816afe43d2a36c5423e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/fbcc4bf5367218951172bdee6f77d7a6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/noto-serif-kr.css +2601 -0
- package/src/styles/site-media.css +823 -0
- package/src/styles/tokens.css +292 -21
- package/src/styles/ui.css +4188 -1857
- package/src/types/app-context.ts +20 -0
- package/src/types/bindings.ts +3 -1
- package/src/types/config.ts +147 -9
- package/src/types/constants.ts +46 -11
- package/src/types/entities.ts +24 -8
- package/src/types/operations.ts +80 -1
- package/src/types/props.ts +10 -9
- package/src/types/raw-assets.d.ts +39 -0
- package/src/types/views.ts +28 -7
- package/src/ui/__tests__/color-themes.test.ts +7 -40
- package/src/ui/__tests__/font-themes.test.ts +30 -11
- package/src/ui/color-themes.ts +311 -270
- package/src/ui/compose/ComposeDialog.tsx +712 -394
- package/src/ui/compose/ComposePrompt.tsx +15 -10
- package/src/ui/dash/ActionButtons.tsx +27 -18
- package/src/ui/dash/DangerZone.tsx +15 -10
- package/src/ui/dash/FormatBadge.tsx +21 -8
- package/src/ui/dash/StatusBadge.tsx +33 -22
- package/src/ui/dash/appearance/AdvancedContent.tsx +44 -30
- package/src/ui/dash/appearance/CodeInjectionContent.tsx +164 -0
- package/src/ui/dash/appearance/ColorThemeContent.tsx +127 -160
- package/src/ui/dash/appearance/FontThemeContent.tsx +20 -15
- package/src/ui/dash/appearance/NavigationContent.tsx +368 -225
- package/src/ui/dash/appearance/__tests__/NavigationContent.test.tsx +54 -0
- package/src/ui/dash/settings/AccountContent.tsx +34 -23
- package/src/ui/dash/settings/AccountMenuContent.tsx +226 -137
- package/src/ui/dash/settings/ApiTokensContent.tsx +122 -79
- package/src/ui/dash/settings/AvatarContent.tsx +73 -45
- package/src/ui/dash/settings/DeleteAccountContent.tsx +106 -66
- package/src/ui/dash/settings/GeneralContent.tsx +274 -157
- package/src/ui/dash/settings/GitHubSyncContent.tsx +558 -0
- package/src/ui/dash/settings/SessionsContent.tsx +79 -38
- package/src/ui/dash/settings/SettingsRootContent.tsx +209 -109
- package/src/ui/dash/settings/__tests__/AccountMenuContent.test.tsx +67 -0
- package/src/ui/dash/settings/__tests__/GeneralContent.test.tsx +75 -0
- package/src/ui/feed/CuratedThreadPreview.tsx +24 -14
- package/src/ui/feed/LinkCard.tsx +116 -84
- package/src/ui/feed/LinkPreview.tsx +75 -0
- package/src/ui/feed/NoteCard.tsx +60 -16
- package/src/ui/feed/PostStatusBadges.tsx +15 -0
- package/src/ui/feed/QuoteCard.tsx +10 -3
- package/src/ui/feed/ThreadPreview.tsx +62 -36
- package/src/ui/feed/TimelineFeed.tsx +2 -1
- package/src/ui/feed/__tests__/thread-preview.test.ts +220 -11
- package/src/ui/feed/__tests__/timeline-cards.test.ts +186 -5
- package/src/ui/feed/thread-preview-state.ts +23 -10
- package/src/ui/font-themes.ts +57 -97
- package/src/ui/layouts/BaseLayout.tsx +230 -136
- package/src/ui/layouts/SiteLayout.tsx +431 -143
- package/src/ui/layouts/__tests__/BaseLayout.test.tsx +76 -6
- package/src/ui/layouts/__tests__/SiteLayout.test.tsx +76 -0
- package/src/ui/pages/ArchivePage.tsx +320 -207
- package/src/ui/pages/BrandPage.tsx +499 -341
- package/src/ui/pages/CollectionEditorPage.tsx +50 -30
- package/src/ui/pages/CollectionPage.tsx +151 -78
- package/src/ui/pages/CollectionsPage.tsx +27 -18
- package/src/ui/pages/ComposePage.tsx +15 -10
- package/src/ui/pages/FeaturedPage.tsx +16 -11
- package/src/ui/pages/HomePage.tsx +15 -10
- package/src/ui/pages/PostPage.tsx +5 -1
- package/src/ui/pages/SearchPage.tsx +93 -43
- package/src/ui/pages/ThemeSamplePage.tsx +758 -520
- package/src/ui/pages/__tests__/ArchivePage.test.tsx +72 -0
- package/src/ui/shared/CollectionDirectory.tsx +215 -34
- package/src/ui/shared/CollectionsManager.tsx +155 -75
- package/src/ui/shared/DecorativeQuoteMark.tsx +12 -9
- package/src/ui/shared/MediaGallery.tsx +350 -256
- package/src/ui/shared/Pagination.tsx +36 -25
- package/src/ui/shared/PostFooter.tsx +139 -96
- package/src/ui/shared/__tests__/media-gallery.test.ts +27 -0
- package/src/ui/shared/__tests__/navigation-labels.test.ts +79 -28
- package/src/ui/shared/__tests__/post-footer.test.ts +42 -17
- package/src/ui/shared/collection-management-labels.ts +109 -33
- package/src/ui/shared/navigation-labels.ts +78 -8
- package/bin/lib/site-localize-media.js +0 -427
- package/dist/client/_assets/client-auth.js +0 -3260
- package/dist/client/_assets/client.css +0 -2
- package/dist/client/_assets/client.js +0 -380
- package/src/__tests__/site-localize-media.test.ts +0 -150
- package/src/client/components/jant-collection-sidebar.ts +0 -816
- package/src/i18n/locales/en.ts +0 -1
- package/src/i18n/locales/zh-Hans.po +0 -3197
- package/src/i18n/locales/zh-Hans.ts +0 -1
- package/src/i18n/locales/zh-Hant.po +0 -3197
- package/src/i18n/locales/zh-Hant.ts +0 -1
- package/src/routes/feed/rss.ts +0 -216
- package/src/types/lingui-react-macro.d.ts +0 -34
- /package/dist/client/_assets/chunks/{heic-to-XcUDQvtx.js → heic-to-DUUaO23q.js} +0 -0
- /package/dist/client/_assets/chunks/{module-ChVQstFd.js → module-DcsAZQZ_.js} +0 -0
- /package/dist/client/_assets/chunks/{native-CR5HLOyf.js → native-DpcrFAPh.js} +0 -0
- /package/dist/client/_assets/{client-cjk.css → client-cjk-B7Z0snDu.css} +0 -0
- /package/dist/client/_assets/{client-cjk-tc.css → client-cjk-tc-BesJYrb2.css} +0 -0
|
@@ -29,25 +29,62 @@ import type {
|
|
|
29
29
|
import type { CollectionSubmitDetail } from "./collection-types.js";
|
|
30
30
|
import { showToast } from "../toast.js";
|
|
31
31
|
import { publicPath } from "../runtime-paths.js";
|
|
32
|
+
import { parseMarkdownDocument } from "../../lib/markdown-manager.js";
|
|
33
|
+
import {
|
|
34
|
+
applyItemOrder,
|
|
35
|
+
filterCollectionsBySearch,
|
|
36
|
+
getSelectedFirstOrder,
|
|
37
|
+
} from "../collection-picker-order.js";
|
|
32
38
|
import type { JantComposeEditor } from "./jant-compose-editor.js";
|
|
33
39
|
import { getMediaCategory } from "../../lib/upload.js";
|
|
34
40
|
import { getSlugValidationIssue } from "../../lib/slug-format.js";
|
|
35
41
|
import { createTiptapEditor } from "../tiptap/create-editor.js";
|
|
42
|
+
import { MAX_THREAD_POSTS } from "../../types.js";
|
|
43
|
+
|
|
44
|
+
interface ReplyToMedia {
|
|
45
|
+
url: string;
|
|
46
|
+
previewUrl: string;
|
|
47
|
+
alt?: string;
|
|
48
|
+
mimeType: string;
|
|
49
|
+
width?: number;
|
|
50
|
+
height?: number;
|
|
51
|
+
}
|
|
36
52
|
|
|
37
53
|
interface ReplyToData {
|
|
38
54
|
contentHtml: string;
|
|
39
55
|
dateText: string;
|
|
56
|
+
media?: ReplyToMedia[];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
interface ThreadItem {
|
|
60
|
+
id: string;
|
|
61
|
+
format: ComposeFormat;
|
|
40
62
|
}
|
|
41
63
|
|
|
42
|
-
interface
|
|
64
|
+
interface ApiMediaAttachment {
|
|
65
|
+
type: "media";
|
|
43
66
|
id: string;
|
|
44
67
|
previewUrl: string;
|
|
45
68
|
alt?: string;
|
|
46
69
|
mimeType: string;
|
|
47
70
|
url?: string;
|
|
71
|
+
width?: number;
|
|
72
|
+
height?: number;
|
|
73
|
+
summary?: string;
|
|
74
|
+
chars?: number;
|
|
75
|
+
originalName?: string;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
interface ApiTextAttachment {
|
|
79
|
+
type: "text";
|
|
80
|
+
id: string;
|
|
81
|
+
contentUrl: string;
|
|
48
82
|
summary?: string;
|
|
83
|
+
chars?: number;
|
|
49
84
|
}
|
|
50
85
|
|
|
86
|
+
type ApiAttachment = ApiMediaAttachment | ApiTextAttachment;
|
|
87
|
+
|
|
51
88
|
interface ComposePostResponse {
|
|
52
89
|
id: string;
|
|
53
90
|
threadId?: string;
|
|
@@ -56,7 +93,7 @@ interface ComposePostResponse {
|
|
|
56
93
|
visibility?: ComposeVisibility | null;
|
|
57
94
|
replyToId?: string | null;
|
|
58
95
|
collectionIds?: string[];
|
|
59
|
-
|
|
96
|
+
attachments?: ApiAttachment[];
|
|
60
97
|
title?: string | null;
|
|
61
98
|
body?: string | null;
|
|
62
99
|
url?: string | null;
|
|
@@ -75,12 +112,20 @@ interface DraftsResponse {
|
|
|
75
112
|
interface ComposeOpenOptions {
|
|
76
113
|
collectionId?: string;
|
|
77
114
|
restoreDraft?: boolean;
|
|
115
|
+
initialFormat?: ComposeFormat;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
interface ComposeReplyOpenOptions {
|
|
119
|
+
restoreDraft?: boolean;
|
|
120
|
+
initialFormat?: ComposeFormat;
|
|
78
121
|
}
|
|
79
122
|
|
|
80
123
|
interface ComposeStateSnapshot {
|
|
81
124
|
format: ComposeFormat;
|
|
82
125
|
collectionIds: string[];
|
|
83
126
|
slug: string;
|
|
127
|
+
publishedAtInput: string;
|
|
128
|
+
publishedAtTimeMinutes: number | null;
|
|
84
129
|
visibility: ComposeVisibility;
|
|
85
130
|
title: string;
|
|
86
131
|
bodyJson: JSONContent | null;
|
|
@@ -116,6 +161,16 @@ interface ComposeFilePickerCloseDetail {
|
|
|
116
161
|
cancelled?: boolean;
|
|
117
162
|
}
|
|
118
163
|
|
|
164
|
+
interface ComposePublishSummaryChip {
|
|
165
|
+
kind: "publishedAt" | "slug";
|
|
166
|
+
text: string;
|
|
167
|
+
actionLabel: string;
|
|
168
|
+
value: string;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const COMPOSE_PUBLISH_PANEL_FULLSCREEN_QUERY =
|
|
172
|
+
"(max-width: 700px), (max-height: 760px), (hover: none) and (pointer: coarse)";
|
|
173
|
+
|
|
119
174
|
const COMPOSE_DIALOG_HEADER_ICONS = {
|
|
120
175
|
drafts: `
|
|
121
176
|
<rect x="3.85" y="3.45" width="7.85" height="8.35" rx="2.35" />
|
|
@@ -274,9 +329,10 @@ function toComposeCollections(value: unknown): ComposeCollection[] {
|
|
|
274
329
|
|
|
275
330
|
const id = Reflect.get(item, "id");
|
|
276
331
|
const title = Reflect.get(item, "title");
|
|
332
|
+
const slug = Reflect.get(item, "slug");
|
|
277
333
|
if (typeof id !== "string" || typeof title !== "string") return [];
|
|
278
334
|
|
|
279
|
-
return [{ id, title }];
|
|
335
|
+
return [{ id, title, slug: typeof slug === "string" ? slug : "" }];
|
|
280
336
|
});
|
|
281
337
|
}
|
|
282
338
|
|
|
@@ -293,9 +349,181 @@ function normalizeComposeDoc(json: JSONContent | null): JSONContent | null {
|
|
|
293
349
|
return isEmptyComposeDoc(json) ? null : json;
|
|
294
350
|
}
|
|
295
351
|
|
|
352
|
+
function padDateTimePart(value: number): string {
|
|
353
|
+
return String(value).padStart(2, "0");
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function toLocalDateInputValue(timestamp: number): string {
|
|
357
|
+
const date = new Date(timestamp * 1000);
|
|
358
|
+
return `${date.getFullYear()}-${padDateTimePart(
|
|
359
|
+
date.getMonth() + 1,
|
|
360
|
+
)}-${padDateTimePart(date.getDate())}`;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function parseLocalDateInputValue(
|
|
364
|
+
value: string,
|
|
365
|
+
): { year: number; monthIndex: number; day: number } | null {
|
|
366
|
+
const trimmed = value.trim();
|
|
367
|
+
if (!trimmed) return null;
|
|
368
|
+
|
|
369
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(trimmed);
|
|
370
|
+
if (!match) return null;
|
|
371
|
+
|
|
372
|
+
const year = Number(match[1]);
|
|
373
|
+
const month = Number(match[2]);
|
|
374
|
+
const day = Number(match[3]);
|
|
375
|
+
const parsed = new Date(year, month - 1, day);
|
|
376
|
+
if (
|
|
377
|
+
Number.isNaN(parsed.getTime()) ||
|
|
378
|
+
parsed.getFullYear() !== year ||
|
|
379
|
+
parsed.getMonth() !== month - 1 ||
|
|
380
|
+
parsed.getDate() !== day
|
|
381
|
+
) {
|
|
382
|
+
return null;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
return { year, monthIndex: month - 1, day };
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function getTimestampTimeMinutes(timestamp: number): number {
|
|
389
|
+
const date = new Date(timestamp * 1000);
|
|
390
|
+
return date.getHours() * 60 + date.getMinutes();
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function buildTimestampFromLocalDate(
|
|
394
|
+
value: string,
|
|
395
|
+
timeMinutes: number,
|
|
396
|
+
): number | null {
|
|
397
|
+
const parsed = parseLocalDateInputValue(value);
|
|
398
|
+
if (!parsed) return null;
|
|
399
|
+
|
|
400
|
+
const clampedMinutes = Math.min(Math.max(timeMinutes, 0), 23 * 60 + 59);
|
|
401
|
+
const hours = Math.floor(clampedMinutes / 60);
|
|
402
|
+
const minutes = clampedMinutes % 60;
|
|
403
|
+
return Math.floor(
|
|
404
|
+
new Date(
|
|
405
|
+
parsed.year,
|
|
406
|
+
parsed.monthIndex,
|
|
407
|
+
parsed.day,
|
|
408
|
+
hours,
|
|
409
|
+
minutes,
|
|
410
|
+
0,
|
|
411
|
+
0,
|
|
412
|
+
).getTime() / 1000,
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Split API attachments into media items and resolved text attachments
|
|
418
|
+
* for use with `editor.populate()`.
|
|
419
|
+
*/
|
|
420
|
+
async function resolveApiAttachments(allAttachments: ApiAttachment[]) {
|
|
421
|
+
const mediaItems = allAttachments.filter(
|
|
422
|
+
(a): a is ApiMediaAttachment => a.type === "media",
|
|
423
|
+
);
|
|
424
|
+
const textItems = allAttachments.filter(
|
|
425
|
+
(a): a is ApiTextAttachment => a.type === "text",
|
|
426
|
+
);
|
|
427
|
+
|
|
428
|
+
const media = mediaItems.map((m) => ({
|
|
429
|
+
id: m.id,
|
|
430
|
+
previewUrl: m.previewUrl,
|
|
431
|
+
alt: m.alt,
|
|
432
|
+
mimeType: m.mimeType,
|
|
433
|
+
originalName: m.originalName,
|
|
434
|
+
summary: m.summary,
|
|
435
|
+
chars: m.chars,
|
|
436
|
+
}));
|
|
437
|
+
|
|
438
|
+
const textAttachments = await Promise.all(
|
|
439
|
+
textItems.map(async (m) => {
|
|
440
|
+
// Editing a post hydrates existing text attachments back into the
|
|
441
|
+
// editor. Fetch the markdown source from the auth'd attachments API
|
|
442
|
+
// (compose is always admin-authenticated) and parse it into a
|
|
443
|
+
// Tiptap document on the client. The `.html` sibling is not used
|
|
444
|
+
// here — the editor owns HTML rendering from the JSON source.
|
|
445
|
+
try {
|
|
446
|
+
const res = await fetch(m.contentUrl);
|
|
447
|
+
if (res.ok) {
|
|
448
|
+
const payload = (await res.json()) as {
|
|
449
|
+
content?: string;
|
|
450
|
+
contentFormat?: string;
|
|
451
|
+
};
|
|
452
|
+
const markdown =
|
|
453
|
+
payload.contentFormat === "markdown" && payload.content
|
|
454
|
+
? payload.content
|
|
455
|
+
: "";
|
|
456
|
+
const doc = markdown ? parseMarkdownDocument(markdown) : null;
|
|
457
|
+
if (doc) {
|
|
458
|
+
return {
|
|
459
|
+
bodyJson: JSON.stringify(doc),
|
|
460
|
+
bodyHtml: "",
|
|
461
|
+
summary: m.summary ?? "",
|
|
462
|
+
mediaId: m.id,
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
} catch {
|
|
467
|
+
// Fetch or parse failed — fall through to the empty-shell return
|
|
468
|
+
// below. The attachment stays visible in the composer so the user
|
|
469
|
+
// can decide whether to keep, edit, or remove it explicitly; we
|
|
470
|
+
// do not silently drop it.
|
|
471
|
+
}
|
|
472
|
+
return {
|
|
473
|
+
bodyJson: JSON.stringify({
|
|
474
|
+
type: "doc",
|
|
475
|
+
content: [{ type: "paragraph" }],
|
|
476
|
+
}),
|
|
477
|
+
bodyHtml: "",
|
|
478
|
+
summary: m.summary ?? "",
|
|
479
|
+
mediaId: m.id,
|
|
480
|
+
};
|
|
481
|
+
}),
|
|
482
|
+
);
|
|
483
|
+
|
|
484
|
+
const attachmentOrder = allAttachments.map((a) => a.id);
|
|
485
|
+
|
|
486
|
+
return { media, textAttachments, attachmentOrder };
|
|
487
|
+
}
|
|
488
|
+
|
|
296
489
|
export class JantComposeDialog extends LitElement {
|
|
297
490
|
private static _lastNewPostVisibility: ComposeVisibility = "public";
|
|
298
491
|
|
|
492
|
+
/** The collection ID that triggered this compose session (for per-collection visibility memory). */
|
|
493
|
+
private _sourceCollectionId: string | null = null;
|
|
494
|
+
|
|
495
|
+
private static _collectionVisibilityKey(collectionId: string): string {
|
|
496
|
+
return `jant:collection-visibility:${collectionId}`;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
private static _getCollectionVisibility(
|
|
500
|
+
collectionId: string,
|
|
501
|
+
): ComposeVisibility | null {
|
|
502
|
+
try {
|
|
503
|
+
const v = globalThis.localStorage.getItem(
|
|
504
|
+
JantComposeDialog._collectionVisibilityKey(collectionId),
|
|
505
|
+
);
|
|
506
|
+
if (v === "public" || v === "latest_hidden" || v === "private") return v;
|
|
507
|
+
} catch {
|
|
508
|
+
// localStorage unavailable
|
|
509
|
+
}
|
|
510
|
+
return null;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
private static _setCollectionVisibility(
|
|
514
|
+
collectionId: string,
|
|
515
|
+
visibility: ComposeVisibility,
|
|
516
|
+
) {
|
|
517
|
+
try {
|
|
518
|
+
globalThis.localStorage.setItem(
|
|
519
|
+
JantComposeDialog._collectionVisibilityKey(collectionId),
|
|
520
|
+
visibility,
|
|
521
|
+
);
|
|
522
|
+
} catch {
|
|
523
|
+
// localStorage unavailable
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
299
527
|
static properties = {
|
|
300
528
|
collections: { type: Array },
|
|
301
529
|
labels: { type: Object },
|
|
@@ -306,6 +534,7 @@ export class JantComposeDialog extends LitElement {
|
|
|
306
534
|
_format: { state: true },
|
|
307
535
|
_status: { state: true },
|
|
308
536
|
_loading: { state: true },
|
|
537
|
+
_openingEdit: { state: true },
|
|
309
538
|
_collectionIds: { state: true },
|
|
310
539
|
_showCollection: { state: true },
|
|
311
540
|
_collectionSearch: { state: true },
|
|
@@ -325,14 +554,19 @@ export class JantComposeDialog extends LitElement {
|
|
|
325
554
|
_replyToId: { state: true },
|
|
326
555
|
_replyToData: { state: true },
|
|
327
556
|
_replyExpanded: { state: true },
|
|
557
|
+
_threadItems: { state: true },
|
|
558
|
+
_focusedThreadIndex: { state: true },
|
|
328
559
|
_slug: { state: true },
|
|
560
|
+
_publishedAtInput: { state: true },
|
|
329
561
|
_visibility: { state: true },
|
|
330
562
|
_showPublishPanel: { state: true },
|
|
563
|
+
_publishPanelFullscreen: { state: true },
|
|
331
564
|
_suggestedSlug: { state: true },
|
|
332
565
|
_suggestedSlugLoading: { state: true },
|
|
333
566
|
_slugCheckLoading: { state: true },
|
|
334
567
|
_slugTaken: { state: true },
|
|
335
568
|
_visibilityLocked: { state: true },
|
|
569
|
+
_quietReply: { state: true },
|
|
336
570
|
};
|
|
337
571
|
|
|
338
572
|
declare collections: ComposeCollection[];
|
|
@@ -344,6 +578,7 @@ export class JantComposeDialog extends LitElement {
|
|
|
344
578
|
declare _format: ComposeFormat;
|
|
345
579
|
declare _status: "published" | "draft";
|
|
346
580
|
declare _loading: boolean;
|
|
581
|
+
declare _openingEdit: boolean;
|
|
347
582
|
declare _collectionIds: string[];
|
|
348
583
|
declare _showCollection: boolean;
|
|
349
584
|
declare _collectionSearch: string;
|
|
@@ -363,14 +598,19 @@ export class JantComposeDialog extends LitElement {
|
|
|
363
598
|
declare _replyToId: string | null;
|
|
364
599
|
declare _replyToData: ReplyToData | null;
|
|
365
600
|
declare _replyExpanded: boolean;
|
|
601
|
+
declare _threadItems: ThreadItem[];
|
|
602
|
+
declare _focusedThreadIndex: number;
|
|
366
603
|
declare _slug: string;
|
|
604
|
+
declare _publishedAtInput: string;
|
|
367
605
|
declare _visibility: ComposeVisibility;
|
|
368
606
|
declare _showPublishPanel: boolean;
|
|
607
|
+
declare _publishPanelFullscreen: boolean;
|
|
369
608
|
declare _suggestedSlug: string;
|
|
370
609
|
declare _suggestedSlugLoading: boolean;
|
|
371
610
|
declare _slugCheckLoading: boolean;
|
|
372
611
|
declare _slugTaken: boolean;
|
|
373
612
|
declare _visibilityLocked: boolean;
|
|
613
|
+
declare _quietReply: boolean;
|
|
374
614
|
|
|
375
615
|
private _attachedEditor: Editor | null = null;
|
|
376
616
|
private _attachedTextSnapshot: JSONContent | null = null;
|
|
@@ -388,6 +628,11 @@ export class JantComposeDialog extends LitElement {
|
|
|
388
628
|
| "post-view"
|
|
389
629
|
| null = null;
|
|
390
630
|
private _replyRefreshId: string | null = null;
|
|
631
|
+
private _publishedAtTimeMinutes: number | null = null;
|
|
632
|
+
private _originalPublishedAt: number | null = null;
|
|
633
|
+
private _initialPublishedAtTimeMinutes: number | null = null;
|
|
634
|
+
private _initialPublishedAtInput = "";
|
|
635
|
+
private _initialSlug = "";
|
|
391
636
|
private _slugCheckTimer: ReturnType<typeof setTimeout> | null = null;
|
|
392
637
|
private _slugSuggestTimer: ReturnType<typeof setTimeout> | null = null;
|
|
393
638
|
private _slugSuggestRequestId = 0;
|
|
@@ -395,8 +640,13 @@ export class JantComposeDialog extends LitElement {
|
|
|
395
640
|
private _slugSuggestionKey = "";
|
|
396
641
|
private _suppressBeforeUnload = false;
|
|
397
642
|
private _dialogEl: HTMLDialogElement | null = null;
|
|
643
|
+
private _mousedownOnBackdrop = false;
|
|
398
644
|
private _filePickerActive = false;
|
|
399
645
|
private _ignoreNextEscapeClose = false;
|
|
646
|
+
private _openEditRequestId = 0;
|
|
647
|
+
private _collectionPickerOrder: string[] = [];
|
|
648
|
+
private _suppressCollectionOptionClickUntil = 0;
|
|
649
|
+
private _suppressedCollectionOptionId: string | null = null;
|
|
400
650
|
|
|
401
651
|
createRenderRoot() {
|
|
402
652
|
this.innerHTML = "";
|
|
@@ -414,6 +664,7 @@ export class JantComposeDialog extends LitElement {
|
|
|
414
664
|
this._format = "note";
|
|
415
665
|
this._status = "published";
|
|
416
666
|
this._loading = false;
|
|
667
|
+
this._openingEdit = false;
|
|
417
668
|
this._collectionIds = [];
|
|
418
669
|
this._showCollection = false;
|
|
419
670
|
this._collectionSearch = "";
|
|
@@ -433,17 +684,28 @@ export class JantComposeDialog extends LitElement {
|
|
|
433
684
|
this._replyToId = null;
|
|
434
685
|
this._replyToData = null;
|
|
435
686
|
this._replyExpanded = false;
|
|
687
|
+
this._threadItems = [];
|
|
688
|
+
this._focusedThreadIndex = 0;
|
|
436
689
|
this._replyThreadRootId = null;
|
|
437
690
|
this._replyRefreshKind = null;
|
|
438
691
|
this._replyRefreshId = null;
|
|
439
692
|
this._slug = "";
|
|
693
|
+
this._publishedAtInput = "";
|
|
694
|
+
this._publishedAtTimeMinutes = null;
|
|
695
|
+
this._originalPublishedAt = null;
|
|
696
|
+
this._initialPublishedAtTimeMinutes = null;
|
|
697
|
+
this._initialPublishedAtInput = "";
|
|
698
|
+
this._initialSlug = "";
|
|
440
699
|
this._visibility = JantComposeDialog._lastNewPostVisibility;
|
|
700
|
+
this._sourceCollectionId = null;
|
|
441
701
|
this._showPublishPanel = false;
|
|
702
|
+
this._publishPanelFullscreen = false;
|
|
442
703
|
this._suggestedSlug = "";
|
|
443
704
|
this._suggestedSlugLoading = false;
|
|
444
705
|
this._slugCheckLoading = false;
|
|
445
706
|
this._slugTaken = false;
|
|
446
707
|
this._visibilityLocked = false;
|
|
708
|
+
this._quietReply = false;
|
|
447
709
|
}
|
|
448
710
|
|
|
449
711
|
private get _editor(): JantComposeEditor | null {
|
|
@@ -467,26 +729,37 @@ export class JantComposeDialog extends LitElement {
|
|
|
467
729
|
titleInput?.select();
|
|
468
730
|
});
|
|
469
731
|
}
|
|
732
|
+
if (changed.has("_showCollection") && this._showCollection) {
|
|
733
|
+
this._scheduleCollectionPickerAutofocus();
|
|
734
|
+
}
|
|
470
735
|
if (
|
|
471
736
|
changed.has("_format") ||
|
|
472
737
|
changed.has("_collectionIds") ||
|
|
473
738
|
changed.has("_slug") ||
|
|
739
|
+
changed.has("_publishedAtInput") ||
|
|
474
740
|
changed.has("_visibility")
|
|
475
741
|
) {
|
|
476
|
-
// Schedule draft auto-save
|
|
477
|
-
if (!this.
|
|
742
|
+
// Schedule draft auto-save (new-post and edit modes, not draft-load)
|
|
743
|
+
if (!this._draftSourceId) {
|
|
478
744
|
this._scheduleDraftSave();
|
|
479
745
|
}
|
|
480
746
|
}
|
|
747
|
+
if (this._showPublishPanel) {
|
|
748
|
+
this._updatePublishPanelLayout();
|
|
749
|
+
}
|
|
750
|
+
if (this._showCollection) {
|
|
751
|
+
this._updateCollectionPopoverSide();
|
|
752
|
+
}
|
|
481
753
|
}
|
|
482
754
|
|
|
483
755
|
reset() {
|
|
756
|
+
this._openEditRequestId += 1;
|
|
484
757
|
this._format = "note";
|
|
485
758
|
this._status = "published";
|
|
486
759
|
this._loading = false;
|
|
760
|
+
this._openingEdit = false;
|
|
487
761
|
this._collectionIds = [];
|
|
488
|
-
this.
|
|
489
|
-
this._collectionSearch = "";
|
|
762
|
+
this._closeCollectionPicker();
|
|
490
763
|
this._altPanelOpen = false;
|
|
491
764
|
this._altPanelIndex = 0;
|
|
492
765
|
this._attachedPanelOpen = false;
|
|
@@ -503,11 +776,20 @@ export class JantComposeDialog extends LitElement {
|
|
|
503
776
|
this._replyToId = null;
|
|
504
777
|
this._replyToData = null;
|
|
505
778
|
this._replyExpanded = false;
|
|
779
|
+
this._threadItems = [];
|
|
780
|
+
this._focusedThreadIndex = 0;
|
|
506
781
|
this._replyThreadRootId = null;
|
|
507
782
|
this._replyRefreshKind = null;
|
|
508
783
|
this._replyRefreshId = null;
|
|
509
784
|
this._slug = "";
|
|
785
|
+
this._publishedAtInput = "";
|
|
786
|
+
this._publishedAtTimeMinutes = null;
|
|
787
|
+
this._originalPublishedAt = null;
|
|
788
|
+
this._initialPublishedAtTimeMinutes = null;
|
|
789
|
+
this._initialPublishedAtInput = "";
|
|
790
|
+
this._initialSlug = "";
|
|
510
791
|
this._visibility = JantComposeDialog._lastNewPostVisibility;
|
|
792
|
+
this._sourceCollectionId = null;
|
|
511
793
|
this._showPublishPanel = false;
|
|
512
794
|
this._suggestedSlug = "";
|
|
513
795
|
this._suggestedSlugLoading = false;
|
|
@@ -515,6 +797,7 @@ export class JantComposeDialog extends LitElement {
|
|
|
515
797
|
this._slugTaken = false;
|
|
516
798
|
this._slugSuggestionKey = "";
|
|
517
799
|
this._visibilityLocked = false;
|
|
800
|
+
this._quietReply = false;
|
|
518
801
|
this._confirmForDrafts = false;
|
|
519
802
|
this._confirmForAttachedText = false;
|
|
520
803
|
this._initialSnapshot = null;
|
|
@@ -534,6 +817,7 @@ export class JantComposeDialog extends LitElement {
|
|
|
534
817
|
async refreshCollections(): Promise<boolean> {
|
|
535
818
|
try {
|
|
536
819
|
const res = await fetch("/api/collections?view=compose", {
|
|
820
|
+
cache: "no-store",
|
|
537
821
|
headers: { Accept: "application/json" },
|
|
538
822
|
});
|
|
539
823
|
if (!res.ok) return false;
|
|
@@ -552,105 +836,99 @@ export class JantComposeDialog extends LitElement {
|
|
|
552
836
|
|
|
553
837
|
async openEdit(id: string) {
|
|
554
838
|
this.reset();
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
if (!res.ok) return;
|
|
558
|
-
const post = (await res.json()) as ComposePostResponse;
|
|
559
|
-
|
|
839
|
+
const requestId = ++this._openEditRequestId;
|
|
840
|
+
this._openingEdit = true;
|
|
560
841
|
this._editPostId = id;
|
|
561
|
-
this._format = post.format;
|
|
562
|
-
this._slug = post.slug ?? "";
|
|
563
|
-
this._slugTaken = false;
|
|
564
|
-
this._slugCheckLoading = false;
|
|
565
|
-
this._suggestedSlug = "";
|
|
566
|
-
this._suggestedSlugLoading = false;
|
|
567
|
-
this._slugSuggestionKey = "";
|
|
568
|
-
this._visibility = post.visibility ?? "public";
|
|
569
|
-
this._visibilityLocked = Boolean(post.replyToId);
|
|
570
842
|
|
|
571
|
-
|
|
572
|
-
if (
|
|
573
|
-
|
|
843
|
+
const dialog = this.closest("dialog");
|
|
844
|
+
if (dialog && !dialog.open) {
|
|
845
|
+
dialog.showModal();
|
|
574
846
|
}
|
|
575
|
-
|
|
576
|
-
// Wait for Lit to render with the new format before populating editor
|
|
577
847
|
await this.updateComplete;
|
|
848
|
+
this._focusDialogShell();
|
|
578
849
|
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
const textMedia = allMedia.filter(
|
|
585
|
-
(m: { mimeType: string }) => m.mimeType === "text/x-tiptap+json",
|
|
586
|
-
);
|
|
850
|
+
try {
|
|
851
|
+
const res = await fetch(`/api/posts/${id}`);
|
|
852
|
+
if (!res.ok) throw new Error("Failed to load post");
|
|
853
|
+
const post = (await res.json()) as ComposePostResponse;
|
|
854
|
+
if (requestId !== this._openEditRequestId) return;
|
|
587
855
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
summary: m.summary ?? "",
|
|
614
|
-
mediaId: m.id,
|
|
615
|
-
};
|
|
616
|
-
},
|
|
617
|
-
),
|
|
618
|
-
);
|
|
856
|
+
this._format = post.format;
|
|
857
|
+
this._slug = post.slug ?? "";
|
|
858
|
+
this._slugTaken = false;
|
|
859
|
+
this._slugCheckLoading = false;
|
|
860
|
+
this._suggestedSlug = "";
|
|
861
|
+
this._suggestedSlugLoading = false;
|
|
862
|
+
this._slugSuggestionKey = "";
|
|
863
|
+
this._publishedAtInput = post.publishedAt
|
|
864
|
+
? toLocalDateInputValue(post.publishedAt)
|
|
865
|
+
: "";
|
|
866
|
+
this._publishedAtTimeMinutes = post.publishedAt
|
|
867
|
+
? getTimestampTimeMinutes(post.publishedAt)
|
|
868
|
+
: null;
|
|
869
|
+
this._originalPublishedAt = post.publishedAt ?? null;
|
|
870
|
+
this._initialPublishedAtTimeMinutes = this._publishedAtTimeMinutes;
|
|
871
|
+
this._initialPublishedAtInput = this._publishedAtInput;
|
|
872
|
+
this._initialSlug = this._slug.trim();
|
|
873
|
+
this._visibility = post.visibility ?? "public";
|
|
874
|
+
this._visibilityLocked = Boolean(post.replyToId);
|
|
875
|
+
|
|
876
|
+
if (post.replyToId) {
|
|
877
|
+
this._replyToId = post.replyToId;
|
|
878
|
+
await this._fetchReplyContext(post.replyToId);
|
|
879
|
+
if (requestId !== this._openEditRequestId) return;
|
|
880
|
+
}
|
|
619
881
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
bodyJson: post.body ?? undefined,
|
|
624
|
-
url:
|
|
625
|
-
post.format === "quote"
|
|
626
|
-
? (post.sourceUrl ?? undefined)
|
|
627
|
-
: (post.url ?? undefined),
|
|
628
|
-
quoteText: post.quoteText ?? undefined,
|
|
629
|
-
quoteAuthor:
|
|
630
|
-
post.format === "quote" ? (post.sourceName ?? undefined) : undefined,
|
|
631
|
-
rating: post.rating ?? undefined,
|
|
632
|
-
media: nonTextMedia.map(
|
|
633
|
-
(m: {
|
|
634
|
-
id: string;
|
|
635
|
-
previewUrl: string;
|
|
636
|
-
alt?: string;
|
|
637
|
-
mimeType: string;
|
|
638
|
-
}) => ({
|
|
639
|
-
id: m.id,
|
|
640
|
-
previewUrl: m.previewUrl,
|
|
641
|
-
alt: m.alt,
|
|
642
|
-
mimeType: m.mimeType,
|
|
643
|
-
}),
|
|
644
|
-
),
|
|
645
|
-
textAttachments,
|
|
646
|
-
attachmentOrder: allMedia.map((m: { id: string }) => m.id),
|
|
647
|
-
});
|
|
882
|
+
if (post.collectionIds?.length) {
|
|
883
|
+
this._collectionIds = post.collectionIds;
|
|
884
|
+
}
|
|
648
885
|
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
this.
|
|
653
|
-
|
|
886
|
+
const allAttachments = post.attachments ?? [];
|
|
887
|
+
const { media, textAttachments, attachmentOrder } =
|
|
888
|
+
await resolveApiAttachments(allAttachments);
|
|
889
|
+
if (requestId !== this._openEditRequestId) return;
|
|
890
|
+
|
|
891
|
+
this._openingEdit = false;
|
|
892
|
+
await this.updateComplete;
|
|
893
|
+
if (requestId !== this._openEditRequestId) return;
|
|
894
|
+
|
|
895
|
+
// Check for a local edit draft (unsaved changes from a previous session)
|
|
896
|
+
const restored = this._restoreEditDraftIfAvailable(id);
|
|
897
|
+
|
|
898
|
+
if (!restored) {
|
|
899
|
+
this._editor?.populate({
|
|
900
|
+
format: post.format,
|
|
901
|
+
title:
|
|
902
|
+
post.format === "quote" ? undefined : (post.title ?? undefined),
|
|
903
|
+
bodyJson: post.body ?? undefined,
|
|
904
|
+
url:
|
|
905
|
+
post.format === "quote"
|
|
906
|
+
? (post.sourceUrl ?? undefined)
|
|
907
|
+
: (post.url ?? undefined),
|
|
908
|
+
quoteText: post.quoteText ?? undefined,
|
|
909
|
+
quoteAuthor:
|
|
910
|
+
post.format === "quote"
|
|
911
|
+
? (post.sourceName ?? undefined)
|
|
912
|
+
: undefined,
|
|
913
|
+
rating: post.rating ?? undefined,
|
|
914
|
+
media,
|
|
915
|
+
textAttachments,
|
|
916
|
+
attachmentOrder,
|
|
917
|
+
});
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
globalThis.requestAnimationFrame(() => {
|
|
921
|
+
if (requestId !== this._openEditRequestId) return;
|
|
922
|
+
this._focusDialogShell();
|
|
923
|
+
this._captureInitialSnapshot();
|
|
924
|
+
});
|
|
925
|
+
} catch {
|
|
926
|
+
if (requestId !== this._openEditRequestId) return;
|
|
927
|
+
this._openingEdit = false;
|
|
928
|
+
this._closeDialog();
|
|
929
|
+
this.reset();
|
|
930
|
+
showToast(this.labels.loadPostFailed, "error");
|
|
931
|
+
}
|
|
654
932
|
}
|
|
655
933
|
|
|
656
934
|
async openNew(options?: ComposeOpenOptions) {
|
|
@@ -660,6 +938,10 @@ export class JantComposeDialog extends LitElement {
|
|
|
660
938
|
await this.restoreLocalDraft();
|
|
661
939
|
}
|
|
662
940
|
|
|
941
|
+
if (options?.initialFormat) {
|
|
942
|
+
this._format = options.initialFormat;
|
|
943
|
+
}
|
|
944
|
+
|
|
663
945
|
if (
|
|
664
946
|
options?.collectionId &&
|
|
665
947
|
!this._collectionIds.includes(options.collectionId)
|
|
@@ -667,6 +949,17 @@ export class JantComposeDialog extends LitElement {
|
|
|
667
949
|
this._collectionIds = [options.collectionId, ...this._collectionIds];
|
|
668
950
|
}
|
|
669
951
|
|
|
952
|
+
// Restore per-collection visibility preference (only for new posts, not restored drafts)
|
|
953
|
+
if (options?.collectionId) {
|
|
954
|
+
this._sourceCollectionId = options.collectionId;
|
|
955
|
+
const saved = JantComposeDialog._getCollectionVisibility(
|
|
956
|
+
options.collectionId,
|
|
957
|
+
);
|
|
958
|
+
if (saved) {
|
|
959
|
+
this._visibility = saved;
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
|
|
670
963
|
this.closest("dialog")?.showModal();
|
|
671
964
|
await this.updateComplete;
|
|
672
965
|
this._editor?.focusInput();
|
|
@@ -689,6 +982,7 @@ export class JantComposeDialog extends LitElement {
|
|
|
689
982
|
kind: "timeline-item" | "post-card" | "post-view";
|
|
690
983
|
id: string;
|
|
691
984
|
},
|
|
985
|
+
options?: ComposeReplyOpenOptions,
|
|
692
986
|
) {
|
|
693
987
|
this.reset();
|
|
694
988
|
this._replyToId = id;
|
|
@@ -696,7 +990,12 @@ export class JantComposeDialog extends LitElement {
|
|
|
696
990
|
this._replyRefreshKind = refreshTarget?.kind ?? null;
|
|
697
991
|
this._replyRefreshId = refreshTarget?.id ?? null;
|
|
698
992
|
this._replyToData = replyData ?? null;
|
|
699
|
-
this.
|
|
993
|
+
this._visibilityLocked = true;
|
|
994
|
+
this._format = options?.initialFormat ?? "note";
|
|
995
|
+
|
|
996
|
+
if (options?.restoreDraft !== false) {
|
|
997
|
+
await this.restoreLocalDraft({ expectedReplyToId: id });
|
|
998
|
+
}
|
|
700
999
|
|
|
701
1000
|
this.closest("dialog")?.showModal();
|
|
702
1001
|
await this.updateComplete;
|
|
@@ -722,9 +1021,20 @@ export class JantComposeDialog extends LitElement {
|
|
|
722
1021
|
day: "numeric",
|
|
723
1022
|
})
|
|
724
1023
|
: "";
|
|
1024
|
+
const media: ReplyToMedia[] = (post.attachments ?? [])
|
|
1025
|
+
.filter((a): a is ApiMediaAttachment => a.type === "media")
|
|
1026
|
+
.map((m) => ({
|
|
1027
|
+
url: m.url ?? m.previewUrl,
|
|
1028
|
+
previewUrl: m.previewUrl,
|
|
1029
|
+
alt: m.alt,
|
|
1030
|
+
mimeType: m.mimeType,
|
|
1031
|
+
width: m.width,
|
|
1032
|
+
height: m.height,
|
|
1033
|
+
}));
|
|
725
1034
|
this._replyToData = {
|
|
726
1035
|
contentHtml: (post.bodyHtml as string) ?? "",
|
|
727
1036
|
dateText,
|
|
1037
|
+
media: media.length > 0 ? media : undefined,
|
|
728
1038
|
};
|
|
729
1039
|
} catch {
|
|
730
1040
|
// Parent unavailable — reply mode still works, just no preview
|
|
@@ -779,6 +1089,22 @@ export class JantComposeDialog extends LitElement {
|
|
|
779
1089
|
}
|
|
780
1090
|
|
|
781
1091
|
private _hasContent(): boolean {
|
|
1092
|
+
if (this._threadItems.length > 0) {
|
|
1093
|
+
// Thread mode: check the first editor for content
|
|
1094
|
+
const firstEditor = this.querySelector<JantComposeEditor>(
|
|
1095
|
+
"jant-compose-editor",
|
|
1096
|
+
);
|
|
1097
|
+
if (!firstEditor) return false;
|
|
1098
|
+
const data = firstEditor.getData();
|
|
1099
|
+
return (
|
|
1100
|
+
!!data.body ||
|
|
1101
|
+
!!data.title.trim() ||
|
|
1102
|
+
!!data.url.trim() ||
|
|
1103
|
+
!!data.quoteText.trim() ||
|
|
1104
|
+
data.attachments.length > 0
|
|
1105
|
+
);
|
|
1106
|
+
}
|
|
1107
|
+
|
|
782
1108
|
const editor = this._editor;
|
|
783
1109
|
if (!editor) return false;
|
|
784
1110
|
|
|
@@ -813,6 +1139,8 @@ export class JantComposeDialog extends LitElement {
|
|
|
813
1139
|
format: this._format,
|
|
814
1140
|
collectionIds: [...this._collectionIds],
|
|
815
1141
|
slug: this._slug,
|
|
1142
|
+
publishedAtInput: this._publishedAtInput,
|
|
1143
|
+
publishedAtTimeMinutes: this._publishedAtTimeMinutes,
|
|
816
1144
|
visibility: this._visibility,
|
|
817
1145
|
title: editorData.title,
|
|
818
1146
|
bodyJson: editor.getNormalizedBodyJson(),
|
|
@@ -869,8 +1197,7 @@ export class JantComposeDialog extends LitElement {
|
|
|
869
1197
|
|
|
870
1198
|
// Dismiss any open dropdowns first
|
|
871
1199
|
if (this._showCollection) {
|
|
872
|
-
this.
|
|
873
|
-
this._collectionSearch = "";
|
|
1200
|
+
this._closeCollectionPicker();
|
|
874
1201
|
}
|
|
875
1202
|
if (this._showPublishPanel) {
|
|
876
1203
|
this._showPublishPanel = false;
|
|
@@ -941,17 +1268,19 @@ export class JantComposeDialog extends LitElement {
|
|
|
941
1268
|
this._confirmForAttachedText = false;
|
|
942
1269
|
this._doneAttachedPanel();
|
|
943
1270
|
} else if (this._confirmForDrafts) {
|
|
944
|
-
this._dispatchSubmit("draft");
|
|
945
1271
|
this._confirmPanelOpen = false;
|
|
946
|
-
this.
|
|
947
|
-
|
|
1272
|
+
if (!this._editor?.hasPendingInlineImageUploads()) {
|
|
1273
|
+
this._finishDraftSaveAndOpenDrafts();
|
|
1274
|
+
} else {
|
|
1275
|
+
void this._saveDraftAndOpenDrafts();
|
|
1276
|
+
}
|
|
948
1277
|
} else if (this._editPostId) {
|
|
949
1278
|
// Editing a published post — publish the update directly
|
|
950
1279
|
this._confirmPanelOpen = false;
|
|
951
|
-
this._submit("published");
|
|
1280
|
+
void this._submit("published");
|
|
952
1281
|
} else {
|
|
953
1282
|
this._confirmPanelOpen = false;
|
|
954
|
-
this._submit("draft");
|
|
1283
|
+
void this._submit("draft");
|
|
955
1284
|
}
|
|
956
1285
|
}
|
|
957
1286
|
|
|
@@ -973,6 +1302,75 @@ export class JantComposeDialog extends LitElement {
|
|
|
973
1302
|
}
|
|
974
1303
|
}
|
|
975
1304
|
|
|
1305
|
+
/** Build the submit payload for a single thread editor (index-aware). */
|
|
1306
|
+
private _buildEditorPostDetail(
|
|
1307
|
+
editor: JantComposeEditor,
|
|
1308
|
+
format: ComposeFormat,
|
|
1309
|
+
index: number,
|
|
1310
|
+
status: "published" | "draft",
|
|
1311
|
+
): ComposeSubmitDetail {
|
|
1312
|
+
const editorData = editor.getData();
|
|
1313
|
+
const mediaAttachments = new Map(
|
|
1314
|
+
(editorData.attachments ?? []).map((a) => [a.clientId, a]),
|
|
1315
|
+
);
|
|
1316
|
+
const textAttachments = new Map(
|
|
1317
|
+
editorData.attachedTexts.map((t) => [t.clientId, t]),
|
|
1318
|
+
);
|
|
1319
|
+
const orderedAttachments: ComposeSubmitAttachment[] = [];
|
|
1320
|
+
for (const clientId of editorData.attachmentOrder) {
|
|
1321
|
+
const media = mediaAttachments.get(clientId);
|
|
1322
|
+
if (media) {
|
|
1323
|
+
orderedAttachments.push({
|
|
1324
|
+
type: "media",
|
|
1325
|
+
clientId,
|
|
1326
|
+
mediaId: media.mediaId,
|
|
1327
|
+
alt: media.alt || undefined,
|
|
1328
|
+
});
|
|
1329
|
+
continue;
|
|
1330
|
+
}
|
|
1331
|
+
const text = textAttachments.get(clientId);
|
|
1332
|
+
if (text?.bodyJson) {
|
|
1333
|
+
orderedAttachments.push({
|
|
1334
|
+
type: "text",
|
|
1335
|
+
clientId,
|
|
1336
|
+
bodyJson: text.bodyJson,
|
|
1337
|
+
summary: text.summary,
|
|
1338
|
+
mediaId: text.mediaId,
|
|
1339
|
+
originalBodyJson: normalizeComposeDoc(text.originalBodyJson ?? null),
|
|
1340
|
+
});
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
// Only root post (index 0) carries shared publish settings
|
|
1344
|
+
const isRoot = index === 0;
|
|
1345
|
+
return {
|
|
1346
|
+
format,
|
|
1347
|
+
title: editorData.title,
|
|
1348
|
+
body: editorData.body,
|
|
1349
|
+
url: editorData.url,
|
|
1350
|
+
quoteText: editorData.quoteText,
|
|
1351
|
+
quoteAuthor: editorData.quoteAuthor,
|
|
1352
|
+
status,
|
|
1353
|
+
slug: isRoot ? this._slug.trim() || undefined : undefined,
|
|
1354
|
+
publishedAt: isRoot ? this._getPublishedAtSubmitValue(status) : undefined,
|
|
1355
|
+
visibility: isRoot
|
|
1356
|
+
? this._visibilityLocked
|
|
1357
|
+
? undefined
|
|
1358
|
+
: this._visibility
|
|
1359
|
+
: undefined,
|
|
1360
|
+
rating: editorData.rating,
|
|
1361
|
+
collectionIds: isRoot ? [...this._collectionIds] : [],
|
|
1362
|
+
attachments: orderedAttachments,
|
|
1363
|
+
replyToId: isRoot ? (this._replyToId ?? undefined) : undefined,
|
|
1364
|
+
replyThreadRootId: isRoot
|
|
1365
|
+
? (this._replyThreadRootId ?? undefined)
|
|
1366
|
+
: undefined,
|
|
1367
|
+
replyRefreshKind: isRoot
|
|
1368
|
+
? (this._replyRefreshKind ?? undefined)
|
|
1369
|
+
: undefined,
|
|
1370
|
+
replyRefreshId: isRoot ? (this._replyRefreshId ?? undefined) : undefined,
|
|
1371
|
+
};
|
|
1372
|
+
}
|
|
1373
|
+
|
|
976
1374
|
private _buildSubmitDetail(
|
|
977
1375
|
status: "published" | "draft",
|
|
978
1376
|
): ComposeSubmitDetail | null {
|
|
@@ -1025,6 +1423,7 @@ export class JantComposeDialog extends LitElement {
|
|
|
1025
1423
|
quoteText: editorData.quoteText,
|
|
1026
1424
|
quoteAuthor: editorData.quoteAuthor,
|
|
1027
1425
|
slug: this._slug.trim() || undefined,
|
|
1426
|
+
publishedAt: this._getPublishedAtSubmitValue(status),
|
|
1028
1427
|
status,
|
|
1029
1428
|
visibility: this._visibilityLocked ? undefined : this._visibility,
|
|
1030
1429
|
rating: editorData.rating,
|
|
@@ -1032,18 +1431,55 @@ export class JantComposeDialog extends LitElement {
|
|
|
1032
1431
|
attachments: orderedAttachments,
|
|
1033
1432
|
editPostId: this._editPostId ?? this._draftSourceId ?? undefined,
|
|
1034
1433
|
replyToId: this._replyToId ?? undefined,
|
|
1434
|
+
quietReply: this._quietReply || undefined,
|
|
1035
1435
|
replyThreadRootId: this._replyThreadRootId ?? undefined,
|
|
1036
1436
|
replyRefreshKind: this._replyRefreshKind ?? undefined,
|
|
1037
1437
|
replyRefreshId: this._replyRefreshId ?? undefined,
|
|
1038
1438
|
};
|
|
1039
1439
|
}
|
|
1040
1440
|
|
|
1041
|
-
private _focusBlockedSubmitField(): boolean {
|
|
1441
|
+
private _focusBlockedSubmitField(status: "published" | "draft"): boolean {
|
|
1442
|
+
if (
|
|
1443
|
+
status === "published" &&
|
|
1444
|
+
this._getPublishedAtValidationMessage() !== null
|
|
1445
|
+
) {
|
|
1446
|
+
this._revealPublishedAtField();
|
|
1447
|
+
return true;
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1042
1450
|
if (this._getSlugValidationMessage()) {
|
|
1043
1451
|
this._revealSlugField();
|
|
1044
1452
|
return true;
|
|
1045
1453
|
}
|
|
1046
1454
|
|
|
1455
|
+
// ── Thread mode: validate each editor against its own format ──────
|
|
1456
|
+
if (this._threadItems.length > 0) {
|
|
1457
|
+
const editors = Array.from(
|
|
1458
|
+
this.querySelectorAll<JantComposeEditor>("jant-compose-editor"),
|
|
1459
|
+
);
|
|
1460
|
+
for (let i = 0; i < this._threadItems.length; i++) {
|
|
1461
|
+
const item = this._threadItems[i];
|
|
1462
|
+
const editor = editors[i];
|
|
1463
|
+
if (!editor) continue;
|
|
1464
|
+
if (editor.getUrlValidationMessage()) {
|
|
1465
|
+
editor.revealUrlValidation();
|
|
1466
|
+
editor.focusUrlInput("end");
|
|
1467
|
+
return true;
|
|
1468
|
+
}
|
|
1469
|
+
if (editor.getLinkTitleValidationMessage()) {
|
|
1470
|
+
editor.revealLinkTitleValidation();
|
|
1471
|
+
editor.focusLinkTitleInput("end");
|
|
1472
|
+
return true;
|
|
1473
|
+
}
|
|
1474
|
+
if (item.format === "quote" && !editor._quoteText.trim()) {
|
|
1475
|
+
editor.focusInput("end");
|
|
1476
|
+
return true;
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
return false;
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
// ── Single-post mode ─────────────────────────────────────────────
|
|
1047
1483
|
const editor = this._editor;
|
|
1048
1484
|
if (!editor) return false;
|
|
1049
1485
|
|
|
@@ -1069,17 +1505,65 @@ export class JantComposeDialog extends LitElement {
|
|
|
1069
1505
|
|
|
1070
1506
|
private _dispatchSubmit(status: "published" | "draft"): boolean {
|
|
1071
1507
|
if (this._loading) return false;
|
|
1508
|
+
if (this._focusBlockedSubmitField(status)) return false;
|
|
1509
|
+
if (!this._draftSourceId) {
|
|
1510
|
+
this._cancelDraftSaveTimer();
|
|
1511
|
+
this._saveDraftToStorage();
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
// ── Thread mode ────────────────────────────────────────────────────
|
|
1515
|
+
if (this._threadItems.length > 0) {
|
|
1516
|
+
if (this._threadItems.length > MAX_THREAD_POSTS) {
|
|
1517
|
+
showToast(this._getThreadLimitMessage(), "error");
|
|
1518
|
+
return false;
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
const editors = Array.from(
|
|
1522
|
+
this.querySelectorAll<JantComposeEditor>("jant-compose-editor"),
|
|
1523
|
+
);
|
|
1524
|
+
if (editors.length !== this._threadItems.length) return false;
|
|
1525
|
+
|
|
1526
|
+
const threadPosts: ComposeSubmitDetail[] = [];
|
|
1527
|
+
const allPending: ComposeAttachment[] = [];
|
|
1528
|
+
|
|
1529
|
+
for (let i = 0; i < this._threadItems.length; i++) {
|
|
1530
|
+
const item = this._threadItems[i];
|
|
1531
|
+
const editor = editors[i];
|
|
1532
|
+
if (!editor) return false;
|
|
1533
|
+
threadPosts.push(
|
|
1534
|
+
this._buildEditorPostDetail(editor, item.format, i, status),
|
|
1535
|
+
);
|
|
1536
|
+
allPending.push(
|
|
1537
|
+
...(editor._attachments ?? []).filter(
|
|
1538
|
+
(a) =>
|
|
1539
|
+
a.status === "pending" ||
|
|
1540
|
+
a.status === "processing" ||
|
|
1541
|
+
a.status === "uploading",
|
|
1542
|
+
),
|
|
1543
|
+
);
|
|
1544
|
+
}
|
|
1545
|
+
this.dispatchEvent(
|
|
1546
|
+
new CustomEvent("jant:compose-submit-deferred", {
|
|
1547
|
+
bubbles: true,
|
|
1548
|
+
detail: {
|
|
1549
|
+
...threadPosts[0],
|
|
1550
|
+
editPostId: this._editPostId ?? this._draftSourceId ?? undefined,
|
|
1551
|
+
threadPosts,
|
|
1552
|
+
pendingAttachments: allPending,
|
|
1553
|
+
},
|
|
1554
|
+
}),
|
|
1555
|
+
);
|
|
1556
|
+
return true;
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
// ── Single-post mode ───────────────────────────────────────────────
|
|
1072
1560
|
const editor = this._editor;
|
|
1073
1561
|
if (!editor) return false;
|
|
1074
|
-
if (this._focusBlockedSubmitField()) {
|
|
1075
|
-
return false;
|
|
1076
|
-
}
|
|
1077
1562
|
|
|
1078
1563
|
const detail = this._buildSubmitDetail(status);
|
|
1079
1564
|
if (!detail) return false;
|
|
1080
1565
|
|
|
1081
|
-
const
|
|
1082
|
-
const pendingAttachments = attachments.filter(
|
|
1566
|
+
const pendingAttachments = (editor._attachments ?? []).filter(
|
|
1083
1567
|
(a) =>
|
|
1084
1568
|
a.status === "pending" ||
|
|
1085
1569
|
a.status === "processing" ||
|
|
@@ -1095,9 +1579,30 @@ export class JantComposeDialog extends LitElement {
|
|
|
1095
1579
|
return true;
|
|
1096
1580
|
}
|
|
1097
1581
|
|
|
1098
|
-
private
|
|
1099
|
-
this.
|
|
1100
|
-
|
|
1582
|
+
private _saveDraftAndOpenDrafts() {
|
|
1583
|
+
this._finishDraftSaveAndOpenDrafts();
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
private _finishDraftSaveAndOpenDrafts() {
|
|
1587
|
+
if (!this._dispatchSubmit("draft")) return;
|
|
1588
|
+
this.reset();
|
|
1589
|
+
// The bridge writes the draft asynchronously via fetch after we dispatch
|
|
1590
|
+
// the submit event, so fetching the drafts list immediately would miss
|
|
1591
|
+
// the new draft. Wait for the bridge's completion event before loading.
|
|
1592
|
+
const onComplete = () => {
|
|
1593
|
+
clearTimeout(fallbackTimer);
|
|
1594
|
+
if (!this.isConnected) return;
|
|
1595
|
+
void this._openDraftsPanel();
|
|
1596
|
+
};
|
|
1597
|
+
const fallbackTimer = globalThis.setTimeout(() => {
|
|
1598
|
+
document.removeEventListener("jant:compose-submit-complete", onComplete);
|
|
1599
|
+
}, 10_000);
|
|
1600
|
+
document.addEventListener("jant:compose-submit-complete", onComplete, {
|
|
1601
|
+
once: true,
|
|
1602
|
+
});
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
private _finishSubmit(status: "published" | "draft") {
|
|
1101
1606
|
if (!this._dispatchSubmit(status)) return;
|
|
1102
1607
|
if (this.pageMode) {
|
|
1103
1608
|
this._loading = true;
|
|
@@ -1107,6 +1612,11 @@ export class JantComposeDialog extends LitElement {
|
|
|
1107
1612
|
this.reset();
|
|
1108
1613
|
}
|
|
1109
1614
|
|
|
1615
|
+
private _submit(status: "published" | "draft") {
|
|
1616
|
+
this._showPublishPanel = false;
|
|
1617
|
+
this._finishSubmit(status);
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1110
1620
|
private _toggleCollection(id: string) {
|
|
1111
1621
|
if (this._collectionIds.includes(id)) {
|
|
1112
1622
|
this._collectionIds = this._collectionIds.filter((cid) => cid !== id);
|
|
@@ -1120,13 +1630,303 @@ export class JantComposeDialog extends LitElement {
|
|
|
1120
1630
|
const first = collections.find((c) => c.id === ids[0]);
|
|
1121
1631
|
if (!first) return "";
|
|
1122
1632
|
if (ids.length === 1) return first.title;
|
|
1633
|
+
if (ids.length === 2) {
|
|
1634
|
+
const second = collections.find((c) => c.id === ids[1]);
|
|
1635
|
+
return second ? `${first.title}, ${second.title}` : first.title;
|
|
1636
|
+
}
|
|
1123
1637
|
return this.labels.collectionCountLabel
|
|
1124
1638
|
.replace("%name%", first.title)
|
|
1125
1639
|
.replace("%count%", String(ids.length - 1));
|
|
1126
1640
|
}
|
|
1127
1641
|
|
|
1128
|
-
private
|
|
1129
|
-
|
|
1642
|
+
private _prepareCollectionPickerOrder() {
|
|
1643
|
+
this._collectionPickerOrder = getSelectedFirstOrder(
|
|
1644
|
+
this.collections ?? [],
|
|
1645
|
+
this._collectionIds,
|
|
1646
|
+
);
|
|
1647
|
+
}
|
|
1648
|
+
|
|
1649
|
+
private _focusCollectionPickerInitialTarget() {
|
|
1650
|
+
this.querySelector<HTMLElement>(
|
|
1651
|
+
".compose-collection-search-input, .compose-collection-option, .compose-collection-add-action",
|
|
1652
|
+
)?.focus();
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
private _focusCollectionSearchInput() {
|
|
1656
|
+
const searchInput = this.querySelector<HTMLInputElement>(
|
|
1657
|
+
".compose-collection-search-input",
|
|
1658
|
+
);
|
|
1659
|
+
if (!searchInput) return false;
|
|
1660
|
+
|
|
1661
|
+
searchInput.focus();
|
|
1662
|
+
const cursor = searchInput.value.length;
|
|
1663
|
+
searchInput.setSelectionRange(cursor, cursor);
|
|
1664
|
+
return true;
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
private _closeCollectionPicker(options?: {
|
|
1668
|
+
restoreFocus?: "trigger" | "editor";
|
|
1669
|
+
}) {
|
|
1670
|
+
this._showCollection = false;
|
|
1671
|
+
this._collectionSearch = "";
|
|
1672
|
+
this._suppressedCollectionOptionId = null;
|
|
1673
|
+
this._suppressCollectionOptionClickUntil = 0;
|
|
1674
|
+
|
|
1675
|
+
if (options?.restoreFocus === "trigger") {
|
|
1676
|
+
this.updateComplete.then(() => {
|
|
1677
|
+
this.querySelector<HTMLElement>(".compose-collection-trigger")?.focus();
|
|
1678
|
+
});
|
|
1679
|
+
return;
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
if (options?.restoreFocus === "editor") {
|
|
1683
|
+
this._restorePageEditorFocus();
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1687
|
+
private _suppressNextCollectionOptionClick(collectionId: string) {
|
|
1688
|
+
this._suppressedCollectionOptionId = collectionId;
|
|
1689
|
+
this._suppressCollectionOptionClickUntil = Date.now() + 250;
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
private _isTouchViewport() {
|
|
1693
|
+
return (
|
|
1694
|
+
globalThis.matchMedia?.("(hover: none) and (pointer: coarse)")?.matches ??
|
|
1695
|
+
false
|
|
1696
|
+
);
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1699
|
+
private _shouldAutofocusCollectionPicker() {
|
|
1700
|
+
return !this._isTouchViewport();
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
private _shouldAutofocusFormatInput() {
|
|
1704
|
+
return !this._isTouchViewport();
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
private _scheduleCollectionPickerAutofocus() {
|
|
1708
|
+
if (!this._shouldAutofocusCollectionPicker()) return;
|
|
1709
|
+
|
|
1710
|
+
globalThis.requestAnimationFrame(() => {
|
|
1711
|
+
if (!this._showCollection) return;
|
|
1712
|
+
this._focusCollectionPickerInitialTarget();
|
|
1713
|
+
});
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1716
|
+
private _scheduleCollectionSearchFocus() {
|
|
1717
|
+
globalThis.requestAnimationFrame(() => {
|
|
1718
|
+
if (!this._showCollection) return;
|
|
1719
|
+
if (!this._focusCollectionSearchInput()) {
|
|
1720
|
+
this._focusCollectionPickerInitialTarget();
|
|
1721
|
+
}
|
|
1722
|
+
});
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
private _isPrintableCollectionSearchKey(key: string) {
|
|
1726
|
+
return key.length === 1 && key.trim().length > 0;
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1729
|
+
private _handleCollectionTriggerKeydown = (
|
|
1730
|
+
event: globalThis.KeyboardEvent,
|
|
1731
|
+
) => {
|
|
1732
|
+
if (
|
|
1733
|
+
event.defaultPrevented ||
|
|
1734
|
+
event.isComposing ||
|
|
1735
|
+
event.altKey ||
|
|
1736
|
+
event.ctrlKey ||
|
|
1737
|
+
event.metaKey
|
|
1738
|
+
) {
|
|
1739
|
+
return;
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
if (event.key === "ArrowDown") {
|
|
1743
|
+
event.preventDefault();
|
|
1744
|
+
this._scheduleCollectionSearchFocus();
|
|
1745
|
+
return;
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
if (
|
|
1749
|
+
!this._showCollection ||
|
|
1750
|
+
!this._isPrintableCollectionSearchKey(event.key)
|
|
1751
|
+
) {
|
|
1752
|
+
return;
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
event.preventDefault();
|
|
1756
|
+
this._collectionSearch += event.key;
|
|
1757
|
+
this._scheduleCollectionSearchFocus();
|
|
1758
|
+
};
|
|
1759
|
+
|
|
1760
|
+
private _getCollectionOptionElements() {
|
|
1761
|
+
return Array.from(
|
|
1762
|
+
this.querySelectorAll<HTMLButtonElement>(".compose-collection-option"),
|
|
1763
|
+
);
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
private _handleCollectionSearchKeydown = (
|
|
1767
|
+
event: globalThis.KeyboardEvent,
|
|
1768
|
+
) => {
|
|
1769
|
+
if (
|
|
1770
|
+
event.defaultPrevented ||
|
|
1771
|
+
event.isComposing ||
|
|
1772
|
+
event.altKey ||
|
|
1773
|
+
event.ctrlKey ||
|
|
1774
|
+
event.metaKey
|
|
1775
|
+
) {
|
|
1776
|
+
return;
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
if (event.key === "Enter") {
|
|
1780
|
+
event.preventDefault();
|
|
1781
|
+
this._closeCollectionPicker({ restoreFocus: "trigger" });
|
|
1782
|
+
return;
|
|
1783
|
+
}
|
|
1784
|
+
|
|
1785
|
+
if (event.key !== "ArrowDown") {
|
|
1786
|
+
return;
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
const [firstOption] = this._getCollectionOptionElements();
|
|
1790
|
+
const addAction = this.querySelector<HTMLButtonElement>(
|
|
1791
|
+
".compose-collection-add-action",
|
|
1792
|
+
);
|
|
1793
|
+
const nextTarget = firstOption ?? addAction;
|
|
1794
|
+
if (!nextTarget) return;
|
|
1795
|
+
|
|
1796
|
+
event.preventDefault();
|
|
1797
|
+
nextTarget.focus();
|
|
1798
|
+
};
|
|
1799
|
+
|
|
1800
|
+
private _handleCollectionOptionKeydown = (
|
|
1801
|
+
event: globalThis.KeyboardEvent,
|
|
1802
|
+
collectionId: string,
|
|
1803
|
+
) => {
|
|
1804
|
+
if (
|
|
1805
|
+
event.defaultPrevented ||
|
|
1806
|
+
event.isComposing ||
|
|
1807
|
+
event.altKey ||
|
|
1808
|
+
event.ctrlKey ||
|
|
1809
|
+
event.metaKey
|
|
1810
|
+
) {
|
|
1811
|
+
return;
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1814
|
+
const options = this._getCollectionOptionElements();
|
|
1815
|
+
const currentTarget = event.currentTarget as HTMLButtonElement | null;
|
|
1816
|
+
const currentIndex = currentTarget ? options.indexOf(currentTarget) : -1;
|
|
1817
|
+
|
|
1818
|
+
if (event.key === "ArrowDown") {
|
|
1819
|
+
const addAction = this.querySelector<HTMLButtonElement>(
|
|
1820
|
+
".compose-collection-add-action",
|
|
1821
|
+
);
|
|
1822
|
+
const nextTarget =
|
|
1823
|
+
currentIndex >= 0
|
|
1824
|
+
? (options[currentIndex + 1] ?? addAction)
|
|
1825
|
+
: options[0];
|
|
1826
|
+
if (!nextTarget) return;
|
|
1827
|
+
|
|
1828
|
+
event.preventDefault();
|
|
1829
|
+
nextTarget.focus();
|
|
1830
|
+
return;
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
if (event.key === "ArrowUp") {
|
|
1834
|
+
const searchInput = this.querySelector<HTMLInputElement>(
|
|
1835
|
+
".compose-collection-search-input",
|
|
1836
|
+
);
|
|
1837
|
+
const previousTarget =
|
|
1838
|
+
currentIndex > 0 ? options[currentIndex - 1] : searchInput;
|
|
1839
|
+
if (!previousTarget) return;
|
|
1840
|
+
|
|
1841
|
+
event.preventDefault();
|
|
1842
|
+
previousTarget.focus();
|
|
1843
|
+
return;
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
if (event.key === " " || event.key === "Spacebar") {
|
|
1847
|
+
event.preventDefault();
|
|
1848
|
+
this._suppressNextCollectionOptionClick(collectionId);
|
|
1849
|
+
this._toggleCollection(collectionId);
|
|
1850
|
+
return;
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1853
|
+
if (event.key === "Enter") {
|
|
1854
|
+
event.preventDefault();
|
|
1855
|
+
this._suppressNextCollectionOptionClick(collectionId);
|
|
1856
|
+
this._closeCollectionPicker({ restoreFocus: "trigger" });
|
|
1857
|
+
}
|
|
1858
|
+
};
|
|
1859
|
+
|
|
1860
|
+
private _handleCollectionOptionClick = (collectionId: string) => {
|
|
1861
|
+
if (
|
|
1862
|
+
this._suppressedCollectionOptionId === collectionId &&
|
|
1863
|
+
Date.now() <= this._suppressCollectionOptionClickUntil
|
|
1864
|
+
) {
|
|
1865
|
+
this._suppressedCollectionOptionId = null;
|
|
1866
|
+
this._suppressCollectionOptionClickUntil = 0;
|
|
1867
|
+
return;
|
|
1868
|
+
}
|
|
1869
|
+
|
|
1870
|
+
this._suppressedCollectionOptionId = null;
|
|
1871
|
+
this._suppressCollectionOptionClickUntil = 0;
|
|
1872
|
+
this._toggleCollection(collectionId);
|
|
1873
|
+
};
|
|
1874
|
+
|
|
1875
|
+
private _handleCollectionAddActionKeydown = (
|
|
1876
|
+
event: globalThis.KeyboardEvent,
|
|
1877
|
+
) => {
|
|
1878
|
+
if (
|
|
1879
|
+
event.defaultPrevented ||
|
|
1880
|
+
event.isComposing ||
|
|
1881
|
+
event.altKey ||
|
|
1882
|
+
event.ctrlKey ||
|
|
1883
|
+
event.metaKey ||
|
|
1884
|
+
event.key !== "ArrowUp"
|
|
1885
|
+
) {
|
|
1886
|
+
return;
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1889
|
+
const options = this._getCollectionOptionElements();
|
|
1890
|
+
const searchInput = this.querySelector<HTMLInputElement>(
|
|
1891
|
+
".compose-collection-search-input",
|
|
1892
|
+
);
|
|
1893
|
+
const previousTarget = options.at(-1) ?? searchInput;
|
|
1894
|
+
if (!previousTarget) return;
|
|
1895
|
+
|
|
1896
|
+
event.preventDefault();
|
|
1897
|
+
previousTarget.focus();
|
|
1898
|
+
};
|
|
1899
|
+
|
|
1900
|
+
private _updateCollectionPopoverSide() {
|
|
1901
|
+
const trigger = this.querySelector<HTMLElement>(
|
|
1902
|
+
".compose-collection-trigger",
|
|
1903
|
+
);
|
|
1904
|
+
const popover = this.querySelector<HTMLElement>(
|
|
1905
|
+
".compose-collection-popover[data-popover]",
|
|
1906
|
+
);
|
|
1907
|
+
if (!trigger || !popover) return;
|
|
1908
|
+
|
|
1909
|
+
const visualViewport = globalThis.visualViewport;
|
|
1910
|
+
const viewportTop = visualViewport?.offsetTop ?? 0;
|
|
1911
|
+
const viewportBottom =
|
|
1912
|
+
viewportTop + (visualViewport?.height ?? globalThis.innerHeight);
|
|
1913
|
+
const triggerRect = trigger.getBoundingClientRect();
|
|
1914
|
+
const edgePadding = 12;
|
|
1915
|
+
const gap = 4;
|
|
1916
|
+
const availableBelow = Math.max(
|
|
1917
|
+
0,
|
|
1918
|
+
viewportBottom - edgePadding - triggerRect.bottom - gap,
|
|
1919
|
+
);
|
|
1920
|
+
const availableAbove = Math.max(
|
|
1921
|
+
0,
|
|
1922
|
+
triggerRect.top - viewportTop - edgePadding - gap,
|
|
1923
|
+
);
|
|
1924
|
+
|
|
1925
|
+
popover.dataset.side = availableBelow >= availableAbove ? "bottom" : "top";
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
private _cancelSlugTimers() {
|
|
1929
|
+
if (this._slugCheckTimer !== null) {
|
|
1130
1930
|
clearTimeout(this._slugCheckTimer);
|
|
1131
1931
|
this._slugCheckTimer = null;
|
|
1132
1932
|
}
|
|
@@ -1310,6 +2110,7 @@ export class JantComposeDialog extends LitElement {
|
|
|
1310
2110
|
|
|
1311
2111
|
connectedCallback() {
|
|
1312
2112
|
super.connectedCallback();
|
|
2113
|
+
this._syncPublishPanelPresentation();
|
|
1313
2114
|
this.addEventListener("keydown", this._handleKeydown);
|
|
1314
2115
|
this.addEventListener("jant:alt-panel-open", this._handleAltPanelOpen);
|
|
1315
2116
|
this.addEventListener("jant:alt-panel-close", this._handleAltPanelClose);
|
|
@@ -1339,11 +2140,24 @@ export class JantComposeDialog extends LitElement {
|
|
|
1339
2140
|
|
|
1340
2141
|
// Flush pending draft save before page unload (covers refresh/close mid-debounce)
|
|
1341
2142
|
window.addEventListener("beforeunload", this._onBeforeUnload);
|
|
2143
|
+
window.addEventListener("resize", this._handleViewportChange);
|
|
2144
|
+
window.addEventListener("scroll", this._handleViewportChange, {
|
|
2145
|
+
passive: true,
|
|
2146
|
+
});
|
|
2147
|
+
globalThis.visualViewport?.addEventListener(
|
|
2148
|
+
"resize",
|
|
2149
|
+
this._handleViewportChange,
|
|
2150
|
+
);
|
|
2151
|
+
globalThis.visualViewport?.addEventListener(
|
|
2152
|
+
"scroll",
|
|
2153
|
+
this._handleViewportChange,
|
|
2154
|
+
);
|
|
1342
2155
|
|
|
1343
2156
|
// Intercept native dialog cancel (ESC) to route through requestClose
|
|
1344
2157
|
this._dialogEl = this.closest("dialog");
|
|
1345
2158
|
if (this._dialogEl) {
|
|
1346
2159
|
this._dialogEl.addEventListener("cancel", this._handleDialogCancel);
|
|
2160
|
+
this._dialogEl.addEventListener("mousedown", this._handleDialogMousedown);
|
|
1347
2161
|
this._dialogEl.addEventListener("click", this._handleDialogClick);
|
|
1348
2162
|
}
|
|
1349
2163
|
|
|
@@ -1383,12 +2197,26 @@ export class JantComposeDialog extends LitElement {
|
|
|
1383
2197
|
this._handleFullscreenClose as EventListener,
|
|
1384
2198
|
);
|
|
1385
2199
|
window.removeEventListener("beforeunload", this._onBeforeUnload);
|
|
2200
|
+
window.removeEventListener("resize", this._handleViewportChange);
|
|
2201
|
+
window.removeEventListener("scroll", this._handleViewportChange);
|
|
2202
|
+
globalThis.visualViewport?.removeEventListener(
|
|
2203
|
+
"resize",
|
|
2204
|
+
this._handleViewportChange,
|
|
2205
|
+
);
|
|
2206
|
+
globalThis.visualViewport?.removeEventListener(
|
|
2207
|
+
"scroll",
|
|
2208
|
+
this._handleViewportChange,
|
|
2209
|
+
);
|
|
1386
2210
|
this._cancelSlugTimers();
|
|
1387
2211
|
this._destroyAttachedEditor();
|
|
1388
2212
|
this._cancelDraftSaveTimer();
|
|
1389
2213
|
|
|
1390
2214
|
if (this._dialogEl) {
|
|
1391
2215
|
this._dialogEl.removeEventListener("cancel", this._handleDialogCancel);
|
|
2216
|
+
this._dialogEl.removeEventListener(
|
|
2217
|
+
"mousedown",
|
|
2218
|
+
this._handleDialogMousedown,
|
|
2219
|
+
);
|
|
1392
2220
|
this._dialogEl.removeEventListener("click", this._handleDialogClick);
|
|
1393
2221
|
this._dialogEl = null;
|
|
1394
2222
|
}
|
|
@@ -1410,6 +2238,25 @@ export class JantComposeDialog extends LitElement {
|
|
|
1410
2238
|
this._clearFilePickerEscapeState();
|
|
1411
2239
|
};
|
|
1412
2240
|
|
|
2241
|
+
private _syncPublishPanelPresentation() {
|
|
2242
|
+
const nextValue =
|
|
2243
|
+
globalThis.matchMedia?.(COMPOSE_PUBLISH_PANEL_FULLSCREEN_QUERY)
|
|
2244
|
+
?.matches ?? false;
|
|
2245
|
+
if (nextValue !== this._publishPanelFullscreen) {
|
|
2246
|
+
this._publishPanelFullscreen = nextValue;
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
2249
|
+
|
|
2250
|
+
private _handleViewportChange = () => {
|
|
2251
|
+
this._syncPublishPanelPresentation();
|
|
2252
|
+
if (this._showPublishPanel) {
|
|
2253
|
+
this.updateComplete.then(() => this._updatePublishPanelLayout());
|
|
2254
|
+
}
|
|
2255
|
+
if (this._showCollection) {
|
|
2256
|
+
this.updateComplete.then(() => this._updateCollectionPopoverSide());
|
|
2257
|
+
}
|
|
2258
|
+
};
|
|
2259
|
+
|
|
1413
2260
|
private _handleDialogCancel = (e: Event) => {
|
|
1414
2261
|
e.preventDefault();
|
|
1415
2262
|
if (this._shouldIgnoreEscapeClose()) return;
|
|
@@ -1417,8 +2264,19 @@ export class JantComposeDialog extends LitElement {
|
|
|
1417
2264
|
this.requestClose();
|
|
1418
2265
|
};
|
|
1419
2266
|
|
|
2267
|
+
private _handleDialogMousedown = (e: Event) => {
|
|
2268
|
+
// Track whether the mousedown originated on the backdrop (the <dialog>
|
|
2269
|
+
// itself). When the user drag-selects text inside the editor and the
|
|
2270
|
+
// pointer overshoots to the backdrop, the subsequent click event fires
|
|
2271
|
+
// with target === dialog. Without this guard, that click triggers
|
|
2272
|
+
// requestClose() and the unsaved-changes confirmation pops up.
|
|
2273
|
+
this._mousedownOnBackdrop = e.target === this._dialogEl;
|
|
2274
|
+
};
|
|
2275
|
+
|
|
1420
2276
|
private _handleDialogClick = (e: Event) => {
|
|
1421
2277
|
if (!this._dialogEl || e.target !== this._dialogEl) return;
|
|
2278
|
+
// Only treat as backdrop click when mousedown also started on the backdrop
|
|
2279
|
+
if (!this._mousedownOnBackdrop) return;
|
|
1422
2280
|
|
|
1423
2281
|
const mouseEvent = e as MouseEvent;
|
|
1424
2282
|
const hitTarget = document.elementFromPoint(
|
|
@@ -1435,8 +2293,17 @@ export class JantComposeDialog extends LitElement {
|
|
|
1435
2293
|
this.requestClose();
|
|
1436
2294
|
};
|
|
1437
2295
|
|
|
1438
|
-
private
|
|
1439
|
-
|
|
2296
|
+
private _focusDialogShell() {
|
|
2297
|
+
const shell = this.querySelector<HTMLElement>(".compose-dialog-inner");
|
|
2298
|
+
if (shell) {
|
|
2299
|
+
shell.focus();
|
|
2300
|
+
return;
|
|
2301
|
+
}
|
|
2302
|
+
this._dialogEl?.focus();
|
|
2303
|
+
}
|
|
2304
|
+
|
|
2305
|
+
private _restorePageEditorFocus() {
|
|
2306
|
+
this.updateComplete.then(() => this._editor?.focusInput());
|
|
1440
2307
|
}
|
|
1441
2308
|
|
|
1442
2309
|
private _dismissEscapeOverlay(): boolean {
|
|
@@ -1456,21 +2323,18 @@ export class JantComposeDialog extends LitElement {
|
|
|
1456
2323
|
}
|
|
1457
2324
|
|
|
1458
2325
|
if (this._showCollection) {
|
|
1459
|
-
this.
|
|
1460
|
-
this._collectionSearch = "";
|
|
1461
|
-
this._focusPageEditorEnd();
|
|
2326
|
+
this._closeCollectionPicker({ restoreFocus: "editor" });
|
|
1462
2327
|
return true;
|
|
1463
2328
|
}
|
|
1464
2329
|
|
|
1465
2330
|
if (this._showPublishPanel) {
|
|
1466
|
-
this.
|
|
1467
|
-
this._focusPageEditorEnd();
|
|
2331
|
+
this._closePublishPanel(true);
|
|
1468
2332
|
return true;
|
|
1469
2333
|
}
|
|
1470
2334
|
|
|
1471
2335
|
if (this._altPanelOpen) {
|
|
1472
2336
|
this._closeAltPanel();
|
|
1473
|
-
this.
|
|
2337
|
+
this._restorePageEditorFocus();
|
|
1474
2338
|
return true;
|
|
1475
2339
|
}
|
|
1476
2340
|
|
|
@@ -1513,10 +2377,32 @@ export class JantComposeDialog extends LitElement {
|
|
|
1513
2377
|
return;
|
|
1514
2378
|
}
|
|
1515
2379
|
if (!this._canPublish()) {
|
|
1516
|
-
this._focusBlockedSubmitField();
|
|
2380
|
+
this._focusBlockedSubmitField("published");
|
|
1517
2381
|
return;
|
|
1518
2382
|
}
|
|
1519
|
-
this._submit("published");
|
|
2383
|
+
void this._submit("published");
|
|
2384
|
+
} else if (
|
|
2385
|
+
(ke.metaKey || ke.ctrlKey) &&
|
|
2386
|
+
!ke.altKey &&
|
|
2387
|
+
!ke.shiftKey &&
|
|
2388
|
+
ke.key >= "1" &&
|
|
2389
|
+
ke.key <= String(JantComposeDialog._FORMATS.length)
|
|
2390
|
+
) {
|
|
2391
|
+
ke.preventDefault();
|
|
2392
|
+
const target = JantComposeDialog._FORMATS[Number(ke.key) - 1];
|
|
2393
|
+
if (this._threadItems.length > 0) {
|
|
2394
|
+
const editor = this.querySelectorAll<JantComposeEditor>(
|
|
2395
|
+
"jant-compose-editor",
|
|
2396
|
+
)[this._focusedThreadIndex];
|
|
2397
|
+
editor?.dispatchEvent(
|
|
2398
|
+
new CustomEvent("jant:thread-format-change", {
|
|
2399
|
+
detail: { format: target },
|
|
2400
|
+
bubbles: true,
|
|
2401
|
+
}),
|
|
2402
|
+
);
|
|
2403
|
+
} else {
|
|
2404
|
+
this._switchFormat(target);
|
|
2405
|
+
}
|
|
1520
2406
|
}
|
|
1521
2407
|
};
|
|
1522
2408
|
|
|
@@ -1555,8 +2441,12 @@ export class JantComposeDialog extends LitElement {
|
|
|
1555
2441
|
e.detail.json as import("@tiptap/core").JSONContent,
|
|
1556
2442
|
e.detail.title,
|
|
1557
2443
|
e.detail.showTitle,
|
|
2444
|
+
e.detail.selection,
|
|
1558
2445
|
);
|
|
1559
|
-
|
|
2446
|
+
// Adopt any in-flight inline image uploads from the fullscreen editor
|
|
2447
|
+
// so blob: placeholder URLs get replaced when uploads complete.
|
|
2448
|
+
editor.adoptPendingUploads();
|
|
2449
|
+
this.updateComplete.then(() => editor.focusSelection(e.detail.selection));
|
|
1560
2450
|
}
|
|
1561
2451
|
this._replyExpanded = e.detail.replyExpanded;
|
|
1562
2452
|
};
|
|
@@ -1680,7 +2570,7 @@ export class JantComposeDialog extends LitElement {
|
|
|
1680
2570
|
| DraftsResponse
|
|
1681
2571
|
| Record<string, unknown>[];
|
|
1682
2572
|
const posts = Array.isArray(json) ? json : (json.posts ?? []);
|
|
1683
|
-
|
|
2573
|
+
const allDraftItems = (posts as Record<string, unknown>[]).map(
|
|
1684
2574
|
(p): DraftItem => ({
|
|
1685
2575
|
id: p.id as string,
|
|
1686
2576
|
format: p.format as ComposeFormat,
|
|
@@ -1698,15 +2588,23 @@ export class JantComposeDialog extends LitElement {
|
|
|
1698
2588
|
replyToId: (p.replyToId as string) ?? null,
|
|
1699
2589
|
updatedAt: p.updatedAt as number,
|
|
1700
2590
|
mediaAttachments: (
|
|
1701
|
-
(p.
|
|
1702
|
-
)
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
2591
|
+
(p.attachments as ApiAttachment[] | undefined) ?? []
|
|
2592
|
+
)
|
|
2593
|
+
.filter((a): a is ApiMediaAttachment => a.type === "media")
|
|
2594
|
+
.map((m) => ({
|
|
2595
|
+
id: m.id,
|
|
2596
|
+
previewUrl: m.previewUrl,
|
|
2597
|
+
alt: m.alt ?? null,
|
|
2598
|
+
mimeType: m.mimeType,
|
|
2599
|
+
})),
|
|
1708
2600
|
}),
|
|
1709
2601
|
);
|
|
2602
|
+
// Filter out thread reply drafts: posts whose replyToId points to another
|
|
2603
|
+
// draft in the same list are inner thread posts — only the root should appear.
|
|
2604
|
+
const draftIds = new Set(allDraftItems.map((d) => d.id));
|
|
2605
|
+
this._drafts = allDraftItems.filter(
|
|
2606
|
+
(d) => !d.replyToId || !draftIds.has(d.replyToId),
|
|
2607
|
+
);
|
|
1710
2608
|
} catch {
|
|
1711
2609
|
this._draftsError = "Could not load drafts. Try again.";
|
|
1712
2610
|
this._drafts = [];
|
|
@@ -1721,6 +2619,36 @@ export class JantComposeDialog extends LitElement {
|
|
|
1721
2619
|
this.updateComplete.then(() => this._editor?.focusInput());
|
|
1722
2620
|
}
|
|
1723
2621
|
|
|
2622
|
+
/**
|
|
2623
|
+
* Resolve text attachments for a post's media list and call editor.populate().
|
|
2624
|
+
* Shared between single-post and thread draft loading.
|
|
2625
|
+
*/
|
|
2626
|
+
private async _populateEditorFromPost(
|
|
2627
|
+
editor: JantComposeEditor,
|
|
2628
|
+
post: ComposePostResponse,
|
|
2629
|
+
) {
|
|
2630
|
+
const allAttachments = post.attachments ?? [];
|
|
2631
|
+
const { media, textAttachments, attachmentOrder } =
|
|
2632
|
+
await resolveApiAttachments(allAttachments);
|
|
2633
|
+
|
|
2634
|
+
editor.populate({
|
|
2635
|
+
format: post.format,
|
|
2636
|
+
title: post.format === "quote" ? undefined : (post.title ?? undefined),
|
|
2637
|
+
bodyJson: post.body ?? undefined,
|
|
2638
|
+
url:
|
|
2639
|
+
post.format === "quote"
|
|
2640
|
+
? (post.sourceUrl ?? undefined)
|
|
2641
|
+
: (post.url ?? undefined),
|
|
2642
|
+
quoteText: post.quoteText ?? undefined,
|
|
2643
|
+
quoteAuthor:
|
|
2644
|
+
post.format === "quote" ? (post.sourceName ?? undefined) : undefined,
|
|
2645
|
+
rating: post.rating ?? undefined,
|
|
2646
|
+
media,
|
|
2647
|
+
textAttachments,
|
|
2648
|
+
attachmentOrder,
|
|
2649
|
+
});
|
|
2650
|
+
}
|
|
2651
|
+
|
|
1724
2652
|
private async _loadDraft(id: string) {
|
|
1725
2653
|
this._draftsPanelOpen = false;
|
|
1726
2654
|
this._draftMenuOpenId = null;
|
|
@@ -1738,6 +2666,16 @@ export class JantComposeDialog extends LitElement {
|
|
|
1738
2666
|
this._suggestedSlug = "";
|
|
1739
2667
|
this._suggestedSlugLoading = false;
|
|
1740
2668
|
this._slugSuggestionKey = "";
|
|
2669
|
+
this._publishedAtInput = post.publishedAt
|
|
2670
|
+
? toLocalDateInputValue(post.publishedAt)
|
|
2671
|
+
: "";
|
|
2672
|
+
this._publishedAtTimeMinutes = post.publishedAt
|
|
2673
|
+
? getTimestampTimeMinutes(post.publishedAt)
|
|
2674
|
+
: null;
|
|
2675
|
+
this._originalPublishedAt = post.publishedAt ?? null;
|
|
2676
|
+
this._initialPublishedAtTimeMinutes = this._publishedAtTimeMinutes;
|
|
2677
|
+
this._initialPublishedAtInput = this._publishedAtInput;
|
|
2678
|
+
this._initialSlug = this._slug.trim();
|
|
1741
2679
|
this._visibility = post.visibility ?? "public";
|
|
1742
2680
|
this._visibilityLocked = Boolean(post.replyToId);
|
|
1743
2681
|
|
|
@@ -1745,83 +2683,128 @@ export class JantComposeDialog extends LitElement {
|
|
|
1745
2683
|
this._collectionIds = post.collectionIds;
|
|
1746
2684
|
}
|
|
1747
2685
|
|
|
1748
|
-
// Restore reply context if this draft was a reply
|
|
2686
|
+
// Restore reply context if this draft was a reply to a published post
|
|
1749
2687
|
if (post.replyToId) {
|
|
1750
2688
|
this._replyToId = post.replyToId;
|
|
1751
2689
|
await this._fetchReplyContext(post.replyToId);
|
|
1752
2690
|
}
|
|
1753
2691
|
|
|
1754
|
-
|
|
2692
|
+
// ── Thread draft: check if this root has other draft posts in its thread ──
|
|
2693
|
+
const isThreadRoot =
|
|
2694
|
+
post.threadId === post.id || post.threadId === undefined;
|
|
2695
|
+
if (isThreadRoot) {
|
|
2696
|
+
// Fetch all drafts to find other posts in this thread
|
|
2697
|
+
try {
|
|
2698
|
+
const draftsRes = await fetch("/api/posts?status=draft&limit=50");
|
|
2699
|
+
if (draftsRes.ok) {
|
|
2700
|
+
const draftsJson = (await draftsRes.json()) as
|
|
2701
|
+
| { posts?: Record<string, unknown>[] }
|
|
2702
|
+
| Record<string, unknown>[];
|
|
2703
|
+
const allDrafts = Array.isArray(draftsJson)
|
|
2704
|
+
? draftsJson
|
|
2705
|
+
: (draftsJson.posts ?? []);
|
|
2706
|
+
// Collect other posts in the same thread, sorted by their implied order
|
|
2707
|
+
// (they have replyToId chains starting from the root)
|
|
2708
|
+
const threadDrafts = (allDrafts as Record<string, unknown>[])
|
|
2709
|
+
.filter(
|
|
2710
|
+
(p) =>
|
|
2711
|
+
p.id !== post.id &&
|
|
2712
|
+
p.threadId === post.id &&
|
|
2713
|
+
p.status === "draft",
|
|
2714
|
+
)
|
|
2715
|
+
.map((p) => ({
|
|
2716
|
+
id: p.id as string,
|
|
2717
|
+
format: p.format as ComposeFormat,
|
|
2718
|
+
replyToId: (p.replyToId as string) ?? null,
|
|
2719
|
+
title: (p.title as string) ?? null,
|
|
2720
|
+
body: (p.body as string) ?? null,
|
|
2721
|
+
url: (p.url as string) ?? null,
|
|
2722
|
+
sourceUrl: (p.sourceUrl as string) ?? null,
|
|
2723
|
+
sourceName: (p.sourceName as string) ?? null,
|
|
2724
|
+
quoteText: (p.quoteText as string) ?? null,
|
|
2725
|
+
rating: (p.rating as number) ?? null,
|
|
2726
|
+
attachments: (p.attachments as ApiAttachment[] | undefined) ?? [],
|
|
2727
|
+
visibility: (p.visibility as ComposeVisibility) ?? null,
|
|
2728
|
+
}));
|
|
2729
|
+
|
|
2730
|
+
if (threadDrafts.length > 0) {
|
|
2731
|
+
// Sort by reply chain: walk replyToId to get ordered list
|
|
2732
|
+
const ordered: typeof threadDrafts = [];
|
|
2733
|
+
let prevId: string = post.id;
|
|
2734
|
+
for (let i = 0; i < threadDrafts.length; i++) {
|
|
2735
|
+
const next = threadDrafts.find((p) => p.replyToId === prevId);
|
|
2736
|
+
if (!next) break;
|
|
2737
|
+
ordered.push(next);
|
|
2738
|
+
prevId = next.id;
|
|
2739
|
+
}
|
|
2740
|
+
// Any remaining posts not in chain (shouldn't happen, but be safe)
|
|
2741
|
+
for (const p of threadDrafts) {
|
|
2742
|
+
if (!ordered.includes(p)) ordered.push(p);
|
|
2743
|
+
}
|
|
1755
2744
|
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
2745
|
+
// Enter thread mode
|
|
2746
|
+
this._threadItems = [
|
|
2747
|
+
{ id: crypto.randomUUID(), format: post.format },
|
|
2748
|
+
...ordered.map((p) => ({
|
|
2749
|
+
id: crypto.randomUUID(),
|
|
2750
|
+
format: p.format,
|
|
2751
|
+
})),
|
|
2752
|
+
];
|
|
2753
|
+
this._focusedThreadIndex = 0;
|
|
2754
|
+
|
|
2755
|
+
await this.updateComplete;
|
|
2756
|
+
|
|
2757
|
+
const editors = Array.from(
|
|
2758
|
+
this.querySelectorAll<JantComposeEditor>("jant-compose-editor"),
|
|
2759
|
+
);
|
|
2760
|
+
|
|
2761
|
+
// Populate root editor
|
|
2762
|
+
const rootEditor = editors[0];
|
|
2763
|
+
if (rootEditor) {
|
|
2764
|
+
await this._populateEditorFromPost(rootEditor, post);
|
|
2765
|
+
}
|
|
1764
2766
|
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
2767
|
+
// Populate reply editors
|
|
2768
|
+
for (let i = 0; i < ordered.length; i++) {
|
|
2769
|
+
const replyEditor = editors[i + 1];
|
|
2770
|
+
if (!replyEditor) continue;
|
|
2771
|
+
const p = ordered[i];
|
|
2772
|
+
await this._populateEditorFromPost(replyEditor, {
|
|
2773
|
+
id: p.id,
|
|
2774
|
+
threadId: post.id,
|
|
2775
|
+
format: p.format,
|
|
2776
|
+
replyToId: p.replyToId,
|
|
2777
|
+
title: p.title,
|
|
2778
|
+
body: p.body,
|
|
2779
|
+
url: p.url,
|
|
2780
|
+
sourceUrl: p.sourceUrl,
|
|
2781
|
+
sourceName: p.sourceName,
|
|
2782
|
+
quoteText: p.quoteText,
|
|
2783
|
+
rating: p.rating,
|
|
2784
|
+
attachments: p.attachments,
|
|
2785
|
+
visibility: p.visibility,
|
|
2786
|
+
});
|
|
1783
2787
|
}
|
|
1784
|
-
|
|
1785
|
-
|
|
2788
|
+
|
|
2789
|
+
globalThis.requestAnimationFrame(() => {
|
|
2790
|
+
editors[0]?.focusInput();
|
|
2791
|
+
this._captureInitialSnapshot();
|
|
2792
|
+
});
|
|
2793
|
+
return;
|
|
1786
2794
|
}
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
};
|
|
1793
|
-
},
|
|
1794
|
-
),
|
|
1795
|
-
);
|
|
2795
|
+
}
|
|
2796
|
+
} catch {
|
|
2797
|
+
// Fall through to single-post load if thread fetch fails
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
1796
2800
|
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
: (post.url ?? undefined),
|
|
1805
|
-
quoteText: post.quoteText ?? undefined,
|
|
1806
|
-
quoteAuthor:
|
|
1807
|
-
post.format === "quote" ? (post.sourceName ?? undefined) : undefined,
|
|
1808
|
-
rating: post.rating ?? undefined,
|
|
1809
|
-
media: nonTextMedia.map(
|
|
1810
|
-
(m: {
|
|
1811
|
-
id: string;
|
|
1812
|
-
previewUrl: string;
|
|
1813
|
-
alt?: string;
|
|
1814
|
-
mimeType: string;
|
|
1815
|
-
}) => ({
|
|
1816
|
-
id: m.id,
|
|
1817
|
-
previewUrl: m.previewUrl,
|
|
1818
|
-
alt: m.alt,
|
|
1819
|
-
mimeType: m.mimeType,
|
|
1820
|
-
}),
|
|
1821
|
-
),
|
|
1822
|
-
textAttachments,
|
|
1823
|
-
attachmentOrder: allMedia.map((m: { id: string }) => m.id),
|
|
1824
|
-
});
|
|
2801
|
+
// ── Single-post draft ─────────────────────────────────────────────
|
|
2802
|
+
await this.updateComplete;
|
|
2803
|
+
|
|
2804
|
+
const editor = this._editor;
|
|
2805
|
+
if (editor) {
|
|
2806
|
+
await this._populateEditorFromPost(editor, post);
|
|
2807
|
+
}
|
|
1825
2808
|
|
|
1826
2809
|
globalThis.requestAnimationFrame(() => {
|
|
1827
2810
|
this._editor?.focusInput();
|
|
@@ -1862,18 +2845,33 @@ export class JantComposeDialog extends LitElement {
|
|
|
1862
2845
|
return null;
|
|
1863
2846
|
}
|
|
1864
2847
|
|
|
2848
|
+
private _getThreadLimitMessage(): string {
|
|
2849
|
+
return (
|
|
2850
|
+
this.labels.threadLimitReached ||
|
|
2851
|
+
`Threads can include up to ${MAX_THREAD_POSTS} posts.`
|
|
2852
|
+
);
|
|
2853
|
+
}
|
|
2854
|
+
|
|
1865
2855
|
// ── Local draft auto-save (globalThis.localStorage) ──────────────────────────
|
|
1866
2856
|
|
|
1867
2857
|
private static _DRAFT_KEY = "jant:compose-draft";
|
|
2858
|
+
private static _EDIT_DRAFT_KEY_PREFIX = "jant:compose-edit:";
|
|
1868
2859
|
private static _DRAFT_MAX_AGE = 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
1869
2860
|
|
|
2861
|
+
private _currentDraftStorageKey(): string {
|
|
2862
|
+
if (this._editPostId) {
|
|
2863
|
+
return JantComposeDialog._EDIT_DRAFT_KEY_PREFIX + this._editPostId;
|
|
2864
|
+
}
|
|
2865
|
+
return JantComposeDialog._DRAFT_KEY;
|
|
2866
|
+
}
|
|
2867
|
+
|
|
1870
2868
|
private _onContentChanged = () => {
|
|
1871
2869
|
this.requestUpdate();
|
|
1872
2870
|
if (!this._hasManualSlug()) {
|
|
1873
2871
|
this._scheduleSuggestedSlugRefresh();
|
|
1874
2872
|
}
|
|
1875
|
-
// Schedule localStorage auto-save
|
|
1876
|
-
if (!this.
|
|
2873
|
+
// Schedule localStorage auto-save (new-post and edit modes, not draft-load)
|
|
2874
|
+
if (!this._draftSourceId) {
|
|
1877
2875
|
this._scheduleDraftSave();
|
|
1878
2876
|
}
|
|
1879
2877
|
};
|
|
@@ -1910,6 +2908,86 @@ export class JantComposeDialog extends LitElement {
|
|
|
1910
2908
|
};
|
|
1911
2909
|
|
|
1912
2910
|
private _saveDraftToStorage() {
|
|
2911
|
+
// ── Thread mode ────────────────────────────────────────────────────
|
|
2912
|
+
if (this._threadItems.length > 0) {
|
|
2913
|
+
const editors = Array.from(
|
|
2914
|
+
this.querySelectorAll<JantComposeEditor>("jant-compose-editor"),
|
|
2915
|
+
);
|
|
2916
|
+
const hasContent = editors.some((editor) => {
|
|
2917
|
+
const data = editor.getData();
|
|
2918
|
+
return (
|
|
2919
|
+
!!data.body ||
|
|
2920
|
+
!!data.title.trim() ||
|
|
2921
|
+
!!data.url.trim() ||
|
|
2922
|
+
!!data.quoteText.trim() ||
|
|
2923
|
+
data.rating > 0 ||
|
|
2924
|
+
data.attachedTexts.length > 0 ||
|
|
2925
|
+
data.attachments.length > 0
|
|
2926
|
+
);
|
|
2927
|
+
});
|
|
2928
|
+
|
|
2929
|
+
if (!hasContent) {
|
|
2930
|
+
globalThis.localStorage.removeItem(this._currentDraftStorageKey());
|
|
2931
|
+
return;
|
|
2932
|
+
}
|
|
2933
|
+
|
|
2934
|
+
const threadItems = this._threadItems
|
|
2935
|
+
.map((item, i) => {
|
|
2936
|
+
const editor = editors[i];
|
|
2937
|
+
if (!editor) return null;
|
|
2938
|
+
const data = editor.getData();
|
|
2939
|
+
return {
|
|
2940
|
+
format: item.format,
|
|
2941
|
+
title: data.title,
|
|
2942
|
+
bodyJson: editor.getNormalizedBodyJson(),
|
|
2943
|
+
url: data.url,
|
|
2944
|
+
quoteText: data.quoteText,
|
|
2945
|
+
quoteAuthor: data.quoteAuthor,
|
|
2946
|
+
attachedTexts: data.attachedTexts.map((t) => ({
|
|
2947
|
+
clientId: t.clientId,
|
|
2948
|
+
bodyJson: t.bodyJson,
|
|
2949
|
+
bodyHtml: t.bodyHtml,
|
|
2950
|
+
summary: t.summary,
|
|
2951
|
+
})),
|
|
2952
|
+
attachmentOrder: [...data.attachmentOrder],
|
|
2953
|
+
};
|
|
2954
|
+
})
|
|
2955
|
+
.filter((item): item is NonNullable<typeof item> => item !== null);
|
|
2956
|
+
|
|
2957
|
+
const draft: LocalDraft = {
|
|
2958
|
+
format: this._threadItems[0]?.format ?? this._format,
|
|
2959
|
+
title: "",
|
|
2960
|
+
bodyJson: null,
|
|
2961
|
+
url: "",
|
|
2962
|
+
quoteText: "",
|
|
2963
|
+
quoteAuthor: "",
|
|
2964
|
+
slug: this._slug,
|
|
2965
|
+
publishedAtInput: this._publishedAtInput,
|
|
2966
|
+
publishedAtTimeMinutes: this._publishedAtTimeMinutes,
|
|
2967
|
+
visibility: this._visibility,
|
|
2968
|
+
rating: 0,
|
|
2969
|
+
showTitle: false,
|
|
2970
|
+
showRating: false,
|
|
2971
|
+
collectionIds: [...this._collectionIds],
|
|
2972
|
+
replyToId: this._replyToId,
|
|
2973
|
+
attachedTexts: [],
|
|
2974
|
+
attachmentOrder: [],
|
|
2975
|
+
threadItems,
|
|
2976
|
+
savedAt: Date.now(),
|
|
2977
|
+
};
|
|
2978
|
+
|
|
2979
|
+
try {
|
|
2980
|
+
globalThis.localStorage.setItem(
|
|
2981
|
+
this._currentDraftStorageKey(),
|
|
2982
|
+
JSON.stringify(draft),
|
|
2983
|
+
);
|
|
2984
|
+
} catch {
|
|
2985
|
+
// Storage full or unavailable — silently ignore
|
|
2986
|
+
}
|
|
2987
|
+
return;
|
|
2988
|
+
}
|
|
2989
|
+
|
|
2990
|
+
// ── Single-post mode ───────────────────────────────────────────────
|
|
1913
2991
|
const editor = this._editor;
|
|
1914
2992
|
if (!editor) return;
|
|
1915
2993
|
|
|
@@ -1936,6 +3014,8 @@ export class JantComposeDialog extends LitElement {
|
|
|
1936
3014
|
quoteText: data.quoteText,
|
|
1937
3015
|
quoteAuthor: data.quoteAuthor,
|
|
1938
3016
|
slug: this._slug,
|
|
3017
|
+
publishedAtInput: this._publishedAtInput,
|
|
3018
|
+
publishedAtTimeMinutes: this._publishedAtTimeMinutes,
|
|
1939
3019
|
visibility: this._visibility,
|
|
1940
3020
|
rating: data.rating,
|
|
1941
3021
|
showTitle:
|
|
@@ -1967,10 +3047,20 @@ export class JantComposeDialog extends LitElement {
|
|
|
1967
3047
|
|
|
1968
3048
|
private _clearDraftFromStorage() {
|
|
1969
3049
|
this._cancelDraftSaveTimer();
|
|
1970
|
-
globalThis.localStorage.removeItem(
|
|
3050
|
+
globalThis.localStorage.removeItem(this._currentDraftStorageKey());
|
|
3051
|
+
}
|
|
3052
|
+
|
|
3053
|
+
clearLocalDraftFromStorage() {
|
|
3054
|
+
this._clearDraftFromStorage();
|
|
3055
|
+
}
|
|
3056
|
+
|
|
3057
|
+
clearEditDraftFromStorage(postId: string) {
|
|
3058
|
+
globalThis.localStorage.removeItem(
|
|
3059
|
+
JantComposeDialog._EDIT_DRAFT_KEY_PREFIX + postId,
|
|
3060
|
+
);
|
|
1971
3061
|
}
|
|
1972
3062
|
|
|
1973
|
-
async restoreLocalDraft() {
|
|
3063
|
+
async restoreLocalDraft(options?: { expectedReplyToId?: string }) {
|
|
1974
3064
|
// Don't restore if already in edit or draft-load mode
|
|
1975
3065
|
if (this._editPostId || this._draftSourceId) return;
|
|
1976
3066
|
// Don't restore if the editor already has content (e.g. reopened dialog)
|
|
@@ -1998,7 +3088,13 @@ export class JantComposeDialog extends LitElement {
|
|
|
1998
3088
|
return;
|
|
1999
3089
|
}
|
|
2000
3090
|
|
|
2001
|
-
|
|
3091
|
+
if (
|
|
3092
|
+
options?.expectedReplyToId !== undefined &&
|
|
3093
|
+
draft.replyToId !== options.expectedReplyToId
|
|
3094
|
+
) {
|
|
3095
|
+
return;
|
|
3096
|
+
}
|
|
3097
|
+
|
|
2002
3098
|
this._collectionIds = [...(draft.collectionIds ?? [])];
|
|
2003
3099
|
this._slug = draft.slug ?? "";
|
|
2004
3100
|
this._slugTaken = false;
|
|
@@ -2006,17 +3102,77 @@ export class JantComposeDialog extends LitElement {
|
|
|
2006
3102
|
this._suggestedSlug = "";
|
|
2007
3103
|
this._suggestedSlugLoading = false;
|
|
2008
3104
|
this._slugSuggestionKey = "";
|
|
3105
|
+
this._publishedAtInput = draft.publishedAtInput ?? "";
|
|
3106
|
+
this._publishedAtTimeMinutes = draft.publishedAtTimeMinutes ?? null;
|
|
2009
3107
|
this._visibility = draft.visibility ?? "public";
|
|
2010
3108
|
|
|
2011
3109
|
// Restore reply context if this draft was a reply
|
|
2012
3110
|
if (draft.replyToId) {
|
|
2013
3111
|
this._replyToId = draft.replyToId;
|
|
3112
|
+
this._visibilityLocked = true;
|
|
2014
3113
|
await this._fetchReplyContext(draft.replyToId);
|
|
2015
3114
|
}
|
|
2016
3115
|
|
|
2017
|
-
|
|
3116
|
+
// ── Thread draft restore ─────────────────────────────────────────
|
|
3117
|
+
if (draft.threadItems && draft.threadItems.length >= 2) {
|
|
3118
|
+
this._format = draft.threadItems[0].format;
|
|
3119
|
+
this._threadItems = draft.threadItems.map((item) => ({
|
|
3120
|
+
id: crypto.randomUUID(),
|
|
3121
|
+
format: item.format,
|
|
3122
|
+
}));
|
|
3123
|
+
this._focusedThreadIndex = 0;
|
|
2018
3124
|
|
|
2019
|
-
|
|
3125
|
+
await this.updateComplete;
|
|
3126
|
+
|
|
3127
|
+
const editors = Array.from(
|
|
3128
|
+
this.querySelectorAll<JantComposeEditor>("jant-compose-editor"),
|
|
3129
|
+
);
|
|
3130
|
+
for (let i = 0; i < draft.threadItems.length; i++) {
|
|
3131
|
+
const item = draft.threadItems[i];
|
|
3132
|
+
const editor = editors[i];
|
|
3133
|
+
if (!editor) continue;
|
|
3134
|
+
|
|
3135
|
+
const textAttachments = item.attachedTexts?.flatMap((t) => {
|
|
3136
|
+
const bodyJson = normalizeComposeDoc(t.bodyJson);
|
|
3137
|
+
if (!bodyJson) return [];
|
|
3138
|
+
return [
|
|
3139
|
+
{
|
|
3140
|
+
clientId: t.clientId,
|
|
3141
|
+
bodyJson: JSON.stringify(bodyJson),
|
|
3142
|
+
bodyHtml: t.bodyHtml,
|
|
3143
|
+
summary: t.summary,
|
|
3144
|
+
},
|
|
3145
|
+
];
|
|
3146
|
+
});
|
|
3147
|
+
|
|
3148
|
+
editor.populate({
|
|
3149
|
+
format: item.format,
|
|
3150
|
+
title: item.title || undefined,
|
|
3151
|
+
bodyJson: item.bodyJson ? JSON.stringify(item.bodyJson) : undefined,
|
|
3152
|
+
url: item.url || undefined,
|
|
3153
|
+
quoteText: item.quoteText || undefined,
|
|
3154
|
+
quoteAuthor: item.quoteAuthor || undefined,
|
|
3155
|
+
textAttachments: textAttachments?.length
|
|
3156
|
+
? textAttachments
|
|
3157
|
+
: undefined,
|
|
3158
|
+
attachmentOrder: item.attachmentOrder,
|
|
3159
|
+
});
|
|
3160
|
+
}
|
|
3161
|
+
|
|
3162
|
+
this._draftRestored = true;
|
|
3163
|
+
showToast(this.labels.draftRestored);
|
|
3164
|
+
globalThis.requestAnimationFrame(() => {
|
|
3165
|
+
this._captureInitialSnapshot();
|
|
3166
|
+
});
|
|
3167
|
+
return;
|
|
3168
|
+
}
|
|
3169
|
+
|
|
3170
|
+
// ── Single-post draft restore ────────────────────────────────────
|
|
3171
|
+
this._format = draft.format;
|
|
3172
|
+
|
|
3173
|
+
await this.updateComplete;
|
|
3174
|
+
|
|
3175
|
+
const textAttachments = draft.attachedTexts?.flatMap((t) => {
|
|
2020
3176
|
const bodyJson = normalizeComposeDoc(t.bodyJson);
|
|
2021
3177
|
if (!bodyJson) return [];
|
|
2022
3178
|
return [
|
|
@@ -2050,6 +3206,75 @@ export class JantComposeDialog extends LitElement {
|
|
|
2050
3206
|
});
|
|
2051
3207
|
}
|
|
2052
3208
|
|
|
3209
|
+
/**
|
|
3210
|
+
* Check for a local edit draft for the given post ID and restore it if valid.
|
|
3211
|
+
* Returns true if a draft was restored, false otherwise.
|
|
3212
|
+
*/
|
|
3213
|
+
private _restoreEditDraftIfAvailable(postId: string): boolean {
|
|
3214
|
+
const key = JantComposeDialog._EDIT_DRAFT_KEY_PREFIX + postId;
|
|
3215
|
+
|
|
3216
|
+
let raw: string | null;
|
|
3217
|
+
try {
|
|
3218
|
+
raw = globalThis.localStorage.getItem(key);
|
|
3219
|
+
} catch {
|
|
3220
|
+
return false;
|
|
3221
|
+
}
|
|
3222
|
+
if (!raw) return false;
|
|
3223
|
+
|
|
3224
|
+
let draft: LocalDraft;
|
|
3225
|
+
try {
|
|
3226
|
+
draft = JSON.parse(raw) as LocalDraft;
|
|
3227
|
+
} catch {
|
|
3228
|
+
globalThis.localStorage.removeItem(key);
|
|
3229
|
+
return false;
|
|
3230
|
+
}
|
|
3231
|
+
|
|
3232
|
+
if (Date.now() - draft.savedAt > JantComposeDialog._DRAFT_MAX_AGE) {
|
|
3233
|
+
globalThis.localStorage.removeItem(key);
|
|
3234
|
+
return false;
|
|
3235
|
+
}
|
|
3236
|
+
|
|
3237
|
+
// Restore metadata
|
|
3238
|
+
this._format = draft.format;
|
|
3239
|
+
this._collectionIds = [...(draft.collectionIds ?? [])];
|
|
3240
|
+
this._slug = draft.slug ?? "";
|
|
3241
|
+
this._publishedAtInput = draft.publishedAtInput ?? "";
|
|
3242
|
+
this._publishedAtTimeMinutes = draft.publishedAtTimeMinutes ?? null;
|
|
3243
|
+
this._visibility = draft.visibility ?? "public";
|
|
3244
|
+
|
|
3245
|
+
// Restore editor content
|
|
3246
|
+
const textAttachments = draft.attachedTexts?.flatMap((t) => {
|
|
3247
|
+
const bodyJson = normalizeComposeDoc(t.bodyJson);
|
|
3248
|
+
if (!bodyJson) return [];
|
|
3249
|
+
return [
|
|
3250
|
+
{
|
|
3251
|
+
clientId: t.clientId,
|
|
3252
|
+
bodyJson: JSON.stringify(bodyJson),
|
|
3253
|
+
bodyHtml: t.bodyHtml,
|
|
3254
|
+
summary: t.summary,
|
|
3255
|
+
},
|
|
3256
|
+
];
|
|
3257
|
+
});
|
|
3258
|
+
|
|
3259
|
+
this._editor?.populate({
|
|
3260
|
+
format: draft.format,
|
|
3261
|
+
title: draft.title || undefined,
|
|
3262
|
+
bodyJson: draft.bodyJson ? JSON.stringify(draft.bodyJson) : undefined,
|
|
3263
|
+
url: draft.url || undefined,
|
|
3264
|
+
quoteText: draft.quoteText || undefined,
|
|
3265
|
+
quoteAuthor: draft.quoteAuthor || undefined,
|
|
3266
|
+
rating: draft.rating || undefined,
|
|
3267
|
+
showTitle: draft.showTitle,
|
|
3268
|
+
showRating: draft.showRating,
|
|
3269
|
+
textAttachments: textAttachments?.length ? textAttachments : undefined,
|
|
3270
|
+
attachmentOrder: draft.attachmentOrder,
|
|
3271
|
+
});
|
|
3272
|
+
|
|
3273
|
+
this._draftRestored = true;
|
|
3274
|
+
showToast(this.labels.draftRestored);
|
|
3275
|
+
return true;
|
|
3276
|
+
}
|
|
3277
|
+
|
|
2053
3278
|
private async _focusPageEditorOnMount() {
|
|
2054
3279
|
if (this._pageFocusApplied) return;
|
|
2055
3280
|
|
|
@@ -2194,7 +3419,7 @@ export class JantComposeDialog extends LitElement {
|
|
|
2194
3419
|
private _renderReplyContext() {
|
|
2195
3420
|
if (!this._replyToId || !this._replyToData) return nothing;
|
|
2196
3421
|
|
|
2197
|
-
const { contentHtml, dateText } = this._replyToData;
|
|
3422
|
+
const { contentHtml, dateText, media } = this._replyToData;
|
|
2198
3423
|
const isExpanded = this._replyExpanded;
|
|
2199
3424
|
|
|
2200
3425
|
return html`
|
|
@@ -2208,6 +3433,38 @@ export class JantComposeDialog extends LitElement {
|
|
|
2208
3433
|
>
|
|
2209
3434
|
<div class="compose-reply-context-body">
|
|
2210
3435
|
${unsafeHTML(contentHtml)}
|
|
3436
|
+
${media?.length
|
|
3437
|
+
? html`<div
|
|
3438
|
+
class="compose-reply-context-media"
|
|
3439
|
+
data-post-media
|
|
3440
|
+
data-lightbox-group=${JSON.stringify(
|
|
3441
|
+
media.map((m) => ({
|
|
3442
|
+
url: m.url,
|
|
3443
|
+
alt: m.alt ?? "",
|
|
3444
|
+
width: m.width,
|
|
3445
|
+
height: m.height,
|
|
3446
|
+
mimeType: m.mimeType,
|
|
3447
|
+
})),
|
|
3448
|
+
)}
|
|
3449
|
+
>
|
|
3450
|
+
${media.map(
|
|
3451
|
+
(m, i) => html`
|
|
3452
|
+
<a
|
|
3453
|
+
href=${m.url}
|
|
3454
|
+
data-lightbox-index=${i}
|
|
3455
|
+
class="compose-reply-context-media-link"
|
|
3456
|
+
>
|
|
3457
|
+
<img
|
|
3458
|
+
src=${m.previewUrl}
|
|
3459
|
+
alt=${m.alt ?? ""}
|
|
3460
|
+
class="compose-reply-context-media-img"
|
|
3461
|
+
loading="lazy"
|
|
3462
|
+
/>
|
|
3463
|
+
</a>
|
|
3464
|
+
`,
|
|
3465
|
+
)}
|
|
3466
|
+
</div>`
|
|
3467
|
+
: nothing}
|
|
2211
3468
|
</div>
|
|
2212
3469
|
${!isExpanded
|
|
2213
3470
|
? html`<div class="compose-reply-fade"></div>`
|
|
@@ -2229,10 +3486,22 @@ export class JantComposeDialog extends LitElement {
|
|
|
2229
3486
|
`;
|
|
2230
3487
|
}
|
|
2231
3488
|
|
|
3489
|
+
private static readonly _FORMATS: ComposeFormat[] = ["note", "link", "quote"];
|
|
3490
|
+
|
|
3491
|
+
private _switchFormat(target: ComposeFormat) {
|
|
3492
|
+
if (this._format === target) return;
|
|
3493
|
+
if (this._editPostId) return;
|
|
3494
|
+
this._format = target;
|
|
3495
|
+
this._showPublishPanel = false;
|
|
3496
|
+
if (this._shouldAutofocusFormatInput()) {
|
|
3497
|
+
globalThis.requestAnimationFrame(() => this._editor?.focusInput());
|
|
3498
|
+
}
|
|
3499
|
+
}
|
|
3500
|
+
|
|
2232
3501
|
// ── Render helpers ────────────────────────────────────────────────
|
|
2233
3502
|
|
|
2234
3503
|
private _renderHeader() {
|
|
2235
|
-
const formats
|
|
3504
|
+
const formats = JantComposeDialog._FORMATS;
|
|
2236
3505
|
const formatLabels: Record<ComposeFormat, string> = {
|
|
2237
3506
|
note: this.labels.note,
|
|
2238
3507
|
link: this.labels.link,
|
|
@@ -2257,37 +3526,35 @@ export class JantComposeDialog extends LitElement {
|
|
|
2257
3526
|
? html`<span class="compose-dialog-title"
|
|
2258
3527
|
>${this.labels.editPost}</span
|
|
2259
3528
|
>`
|
|
2260
|
-
:
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
</div>
|
|
2290
|
-
`}
|
|
3529
|
+
: this._threadItems.length > 0
|
|
3530
|
+
? html`<span class="compose-dialog-title"
|
|
3531
|
+
>${this.labels.newThread}</span
|
|
3532
|
+
>`
|
|
3533
|
+
: html`
|
|
3534
|
+
<div class="compose-segmented">
|
|
3535
|
+
<div
|
|
3536
|
+
class=${classMap({
|
|
3537
|
+
"compose-format-pill": true,
|
|
3538
|
+
"compose-format-pill-link": this._format === "link",
|
|
3539
|
+
"compose-format-pill-quote": this._format === "quote",
|
|
3540
|
+
})}
|
|
3541
|
+
></div>
|
|
3542
|
+
${formats.map(
|
|
3543
|
+
(f) => html`
|
|
3544
|
+
<button
|
|
3545
|
+
type="button"
|
|
3546
|
+
class=${classMap({
|
|
3547
|
+
"compose-segmented-item": true,
|
|
3548
|
+
"compose-segmented-item-active": this._format === f,
|
|
3549
|
+
})}
|
|
3550
|
+
@click=${() => this._switchFormat(f)}
|
|
3551
|
+
>
|
|
3552
|
+
${formatLabels[f]}
|
|
3553
|
+
</button>
|
|
3554
|
+
`,
|
|
3555
|
+
)}
|
|
3556
|
+
</div>
|
|
3557
|
+
`}
|
|
2291
3558
|
</div>
|
|
2292
3559
|
|
|
2293
3560
|
<div class="compose-dialog-header-actions">
|
|
@@ -2310,16 +3577,21 @@ export class JantComposeDialog extends LitElement {
|
|
|
2310
3577
|
|
|
2311
3578
|
private _renderCollectionSelector() {
|
|
2312
3579
|
const collections = this.collections ?? [];
|
|
2313
|
-
const
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
3580
|
+
const orderedCollections = applyItemOrder(
|
|
3581
|
+
collections,
|
|
3582
|
+
this._collectionPickerOrder,
|
|
3583
|
+
);
|
|
3584
|
+
const hasSearch = this._collectionSearch.trim().length > 0;
|
|
3585
|
+
const filtered = filterCollectionsBySearch(
|
|
3586
|
+
orderedCollections,
|
|
3587
|
+
this._collectionSearch,
|
|
3588
|
+
);
|
|
2317
3589
|
const selectedCount = this._collectionIds.length;
|
|
2318
3590
|
const selectedLabel =
|
|
2319
3591
|
selectedCount > 0
|
|
2320
3592
|
? this._selectedCollectionLabel(collections)
|
|
2321
3593
|
: this.labels.collection;
|
|
2322
|
-
const emptyLabel =
|
|
3594
|
+
const emptyLabel = hasSearch
|
|
2323
3595
|
? this.labels.noCollections
|
|
2324
3596
|
: this.labels.emptyCollections;
|
|
2325
3597
|
|
|
@@ -2328,13 +3600,14 @@ export class JantComposeDialog extends LitElement {
|
|
|
2328
3600
|
${this._showCollection
|
|
2329
3601
|
? html`<div
|
|
2330
3602
|
class="compose-dropdown-backdrop"
|
|
2331
|
-
@click=${() =>
|
|
2332
|
-
this._showCollection = false;
|
|
2333
|
-
this._collectionSearch = "";
|
|
2334
|
-
}}
|
|
3603
|
+
@click=${() => this._closeCollectionPicker()}
|
|
2335
3604
|
></div>`
|
|
2336
3605
|
: nothing}
|
|
2337
|
-
<div
|
|
3606
|
+
<div
|
|
3607
|
+
class="select compose-collection-select"
|
|
3608
|
+
data-select-initialized
|
|
3609
|
+
data-open=${this._showCollection ? "true" : nothing}
|
|
3610
|
+
>
|
|
2338
3611
|
<button
|
|
2339
3612
|
type="button"
|
|
2340
3613
|
class="compose-collection-trigger"
|
|
@@ -2342,11 +3615,16 @@ export class JantComposeDialog extends LitElement {
|
|
|
2342
3615
|
aria-expanded=${this._showCollection ? "true" : "false"}
|
|
2343
3616
|
data-open=${this._showCollection ? "true" : nothing}
|
|
2344
3617
|
data-selected=${selectedCount > 0 ? "true" : nothing}
|
|
3618
|
+
@keydown=${this._handleCollectionTriggerKeydown}
|
|
2345
3619
|
@click=${() => {
|
|
3620
|
+
const nextOpen = !this._showCollection;
|
|
2346
3621
|
this._showPublishPanel = false;
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
3622
|
+
if (nextOpen) {
|
|
3623
|
+
this._prepareCollectionPickerOrder();
|
|
3624
|
+
}
|
|
3625
|
+
this._showCollection = nextOpen;
|
|
3626
|
+
if (!nextOpen) {
|
|
3627
|
+
this._closeCollectionPicker();
|
|
2350
3628
|
}
|
|
2351
3629
|
}}
|
|
2352
3630
|
>
|
|
@@ -2365,7 +3643,6 @@ export class JantComposeDialog extends LitElement {
|
|
|
2365
3643
|
<div
|
|
2366
3644
|
class="compose-collection-popover"
|
|
2367
3645
|
data-popover
|
|
2368
|
-
data-side="bottom"
|
|
2369
3646
|
aria-hidden=${this._showCollection ? "false" : "true"}
|
|
2370
3647
|
>
|
|
2371
3648
|
${collections.length > 0
|
|
@@ -2384,6 +3661,7 @@ export class JantComposeDialog extends LitElement {
|
|
|
2384
3661
|
autocorrect="off"
|
|
2385
3662
|
spellcheck="false"
|
|
2386
3663
|
.value=${this._collectionSearch}
|
|
3664
|
+
@keydown=${this._handleCollectionSearchKeydown}
|
|
2387
3665
|
@input=${(e: Event) => {
|
|
2388
3666
|
this._collectionSearch = (
|
|
2389
3667
|
e.target as HTMLInputElement
|
|
@@ -2412,7 +3690,10 @@ export class JantComposeDialog extends LitElement {
|
|
|
2412
3690
|
role="option"
|
|
2413
3691
|
data-value=${col.id}
|
|
2414
3692
|
aria-selected=${selected ? "true" : "false"}
|
|
2415
|
-
@
|
|
3693
|
+
@keydown=${(event: globalThis.KeyboardEvent) =>
|
|
3694
|
+
this._handleCollectionOptionKeydown(event, col.id)}
|
|
3695
|
+
@click=${() =>
|
|
3696
|
+
this._handleCollectionOptionClick(col.id)}
|
|
2416
3697
|
>
|
|
2417
3698
|
<span class="compose-collection-option-label"
|
|
2418
3699
|
>${col.title}</span
|
|
@@ -2463,9 +3744,9 @@ export class JantComposeDialog extends LitElement {
|
|
|
2463
3744
|
<button
|
|
2464
3745
|
type="button"
|
|
2465
3746
|
class="compose-collection-add-action"
|
|
3747
|
+
@keydown=${this._handleCollectionAddActionKeydown}
|
|
2466
3748
|
@click=${() => {
|
|
2467
|
-
this.
|
|
2468
|
-
this._collectionSearch = "";
|
|
3749
|
+
this._closeCollectionPicker();
|
|
2469
3750
|
this._addCollectionPanelOpen = true;
|
|
2470
3751
|
}}
|
|
2471
3752
|
>
|
|
@@ -2514,6 +3795,7 @@ export class JantComposeDialog extends LitElement {
|
|
|
2514
3795
|
const created = (await res.json().catch(() => null)) as {
|
|
2515
3796
|
id: string;
|
|
2516
3797
|
title: string;
|
|
3798
|
+
slug?: string;
|
|
2517
3799
|
error?: string;
|
|
2518
3800
|
} | null;
|
|
2519
3801
|
|
|
@@ -2528,6 +3810,7 @@ export class JantComposeDialog extends LitElement {
|
|
|
2528
3810
|
const newCollection: ComposeCollection = {
|
|
2529
3811
|
id: created.id,
|
|
2530
3812
|
title: created.title,
|
|
3813
|
+
slug: created.slug ?? "",
|
|
2531
3814
|
};
|
|
2532
3815
|
|
|
2533
3816
|
const refreshed = await this.refreshCollections();
|
|
@@ -2859,24 +4142,204 @@ export class JantComposeDialog extends LitElement {
|
|
|
2859
4142
|
return null;
|
|
2860
4143
|
}
|
|
2861
4144
|
|
|
2862
|
-
private
|
|
4145
|
+
private _getCurrentTimestamp(): number {
|
|
4146
|
+
return Math.floor(Date.now() / 1000);
|
|
4147
|
+
}
|
|
4148
|
+
|
|
4149
|
+
private _getPublishedAtMaxInput(): string {
|
|
4150
|
+
return toLocalDateInputValue(this._getCurrentTimestamp());
|
|
4151
|
+
}
|
|
4152
|
+
|
|
4153
|
+
private _getPublishedAtTimeMinutes(): number {
|
|
4154
|
+
return (
|
|
4155
|
+
this._publishedAtTimeMinutes ??
|
|
4156
|
+
getTimestampTimeMinutes(this._getCurrentTimestamp())
|
|
4157
|
+
);
|
|
4158
|
+
}
|
|
4159
|
+
|
|
4160
|
+
private _hasPublishedAtValue(): boolean {
|
|
4161
|
+
return this._publishedAtInput.trim().length > 0;
|
|
4162
|
+
}
|
|
4163
|
+
|
|
4164
|
+
private _getPublishedAtValidationMessage(): string | null {
|
|
4165
|
+
if (!this._hasPublishedAtValue()) return null;
|
|
4166
|
+
|
|
4167
|
+
const parsedDate = parseLocalDateInputValue(this._publishedAtInput);
|
|
4168
|
+
if (parsedDate === null) {
|
|
4169
|
+
return this.labels.publishDateInvalid;
|
|
4170
|
+
}
|
|
4171
|
+
|
|
4172
|
+
if (this._publishedAtInput > this._getPublishedAtMaxInput()) {
|
|
4173
|
+
return this.labels.publishDateFutureError;
|
|
4174
|
+
}
|
|
4175
|
+
|
|
4176
|
+
return null;
|
|
4177
|
+
}
|
|
4178
|
+
|
|
4179
|
+
private _getPublishedAtSummary(): { input: string; text: string } | null {
|
|
4180
|
+
if (this._editPostId || this._draftSourceId) {
|
|
4181
|
+
if (this._publishedAtInput === this._initialPublishedAtInput) {
|
|
4182
|
+
return null;
|
|
4183
|
+
}
|
|
4184
|
+
if (!this._hasPublishedAtValue()) {
|
|
4185
|
+
return {
|
|
4186
|
+
input: "",
|
|
4187
|
+
text: this.labels.publishDateSummaryNow,
|
|
4188
|
+
};
|
|
4189
|
+
}
|
|
4190
|
+
}
|
|
4191
|
+
|
|
4192
|
+
if (this._getPublishedAtValidationMessage() !== null) return null;
|
|
4193
|
+
|
|
4194
|
+
const parsedDate = parseLocalDateInputValue(this._publishedAtInput);
|
|
4195
|
+
if (parsedDate === null) return null;
|
|
4196
|
+
|
|
4197
|
+
return {
|
|
4198
|
+
input: this._publishedAtInput,
|
|
4199
|
+
text: new Intl.DateTimeFormat(undefined, {
|
|
4200
|
+
month: "short",
|
|
4201
|
+
day: "numeric",
|
|
4202
|
+
year: "numeric",
|
|
4203
|
+
}).format(
|
|
4204
|
+
new Date(parsedDate.year, parsedDate.monthIndex, parsedDate.day),
|
|
4205
|
+
),
|
|
4206
|
+
};
|
|
4207
|
+
}
|
|
4208
|
+
|
|
4209
|
+
private _getSlugSummary(): ComposePublishSummaryChip | null {
|
|
4210
|
+
const currentSlug = this._slug.trim();
|
|
4211
|
+
|
|
4212
|
+
if (currentSlug && this._getSlugValidationMessage() !== null) {
|
|
4213
|
+
return null;
|
|
4214
|
+
}
|
|
4215
|
+
|
|
4216
|
+
if (this._editPostId || this._draftSourceId) {
|
|
4217
|
+
if (currentSlug === this._initialSlug) {
|
|
4218
|
+
return null;
|
|
4219
|
+
}
|
|
4220
|
+
|
|
4221
|
+
if (!currentSlug) {
|
|
4222
|
+
return {
|
|
4223
|
+
kind: "slug",
|
|
4224
|
+
text: this.labels.publishSlugSummaryAuto,
|
|
4225
|
+
actionLabel: this.labels.publishSlugSummaryAction,
|
|
4226
|
+
value: currentSlug,
|
|
4227
|
+
};
|
|
4228
|
+
}
|
|
4229
|
+
}
|
|
4230
|
+
|
|
4231
|
+
if (!currentSlug) return null;
|
|
4232
|
+
|
|
4233
|
+
return {
|
|
4234
|
+
kind: "slug",
|
|
4235
|
+
text: `/${currentSlug}`,
|
|
4236
|
+
actionLabel: this.labels.publishSlugSummaryAction,
|
|
4237
|
+
value: currentSlug,
|
|
4238
|
+
};
|
|
4239
|
+
}
|
|
4240
|
+
|
|
4241
|
+
private _getPublishSummaryChips(): ComposePublishSummaryChip[] {
|
|
4242
|
+
const chips: ComposePublishSummaryChip[] = [];
|
|
4243
|
+
const publishedAtSummary = this._getPublishedAtSummary();
|
|
4244
|
+
if (publishedAtSummary !== null) {
|
|
4245
|
+
chips.push({
|
|
4246
|
+
kind: "publishedAt",
|
|
4247
|
+
text: publishedAtSummary.text,
|
|
4248
|
+
actionLabel: this.labels.publishDateSummaryAction,
|
|
4249
|
+
value: publishedAtSummary.input,
|
|
4250
|
+
});
|
|
4251
|
+
}
|
|
4252
|
+
|
|
4253
|
+
const slugSummary = this._getSlugSummary();
|
|
4254
|
+
if (slugSummary !== null) {
|
|
4255
|
+
chips.push(slugSummary);
|
|
4256
|
+
}
|
|
4257
|
+
|
|
4258
|
+
return chips;
|
|
4259
|
+
}
|
|
4260
|
+
|
|
4261
|
+
private _getPublishedAtSubmitValue(
|
|
4262
|
+
status: "published" | "draft",
|
|
4263
|
+
): number | undefined {
|
|
4264
|
+
if (status === "draft") return undefined;
|
|
4265
|
+
|
|
4266
|
+
const publishedAt = buildTimestampFromLocalDate(
|
|
4267
|
+
this._publishedAtInput,
|
|
4268
|
+
this._getPublishedAtTimeMinutes(),
|
|
4269
|
+
);
|
|
4270
|
+
if (publishedAt !== null) {
|
|
4271
|
+
// If editing and the user didn't change date or time, preserve the
|
|
4272
|
+
// original timestamp (including seconds) to avoid silent truncation.
|
|
4273
|
+
if (
|
|
4274
|
+
this._originalPublishedAt !== null &&
|
|
4275
|
+
this._publishedAtInput === this._initialPublishedAtInput &&
|
|
4276
|
+
this._publishedAtTimeMinutes === this._initialPublishedAtTimeMinutes
|
|
4277
|
+
) {
|
|
4278
|
+
return Math.min(this._originalPublishedAt, this._getCurrentTimestamp());
|
|
4279
|
+
}
|
|
4280
|
+
return Math.min(publishedAt, this._getCurrentTimestamp());
|
|
4281
|
+
}
|
|
4282
|
+
|
|
4283
|
+
if (this._publishedAtInput.trim().length > 0) {
|
|
4284
|
+
return undefined;
|
|
4285
|
+
}
|
|
4286
|
+
|
|
4287
|
+
if (this._editPostId) {
|
|
4288
|
+
return this._getCurrentTimestamp();
|
|
4289
|
+
}
|
|
4290
|
+
|
|
4291
|
+
return undefined;
|
|
4292
|
+
}
|
|
4293
|
+
|
|
4294
|
+
private _openPublishPanelAndFocus(selector: string) {
|
|
2863
4295
|
this._showCollection = false;
|
|
2864
4296
|
this._collectionSearch = "";
|
|
2865
4297
|
this._showPublishPanel = true;
|
|
2866
4298
|
this._confirmPanelOpen = false;
|
|
2867
4299
|
this._scheduleSuggestedSlugRefresh(true);
|
|
2868
4300
|
this.updateComplete.then(() => {
|
|
2869
|
-
this.querySelector<
|
|
2870
|
-
".compose-publish-slug-input",
|
|
2871
|
-
)?.focus();
|
|
4301
|
+
this.querySelector<HTMLElement>(selector)?.focus();
|
|
2872
4302
|
});
|
|
2873
4303
|
}
|
|
2874
4304
|
|
|
4305
|
+
private _revealSlugField() {
|
|
4306
|
+
this._openPublishPanelAndFocus(".compose-publish-slug-input");
|
|
4307
|
+
}
|
|
4308
|
+
|
|
4309
|
+
private _revealPublishedAtField() {
|
|
4310
|
+
this._openPublishPanelAndFocus(".compose-publish-date-input");
|
|
4311
|
+
}
|
|
4312
|
+
|
|
2875
4313
|
private _canPublish(): boolean {
|
|
2876
4314
|
if (this._loading) return false;
|
|
4315
|
+
if (this._getPublishedAtValidationMessage()) return false;
|
|
4316
|
+
if (this._getSlugValidationMessage()) return false;
|
|
4317
|
+
|
|
4318
|
+
if (this._threadItems.length > 0) {
|
|
4319
|
+
// Thread mode: validate all editors
|
|
4320
|
+
const editors = Array.from(
|
|
4321
|
+
this.querySelectorAll<JantComposeEditor>("jant-compose-editor"),
|
|
4322
|
+
);
|
|
4323
|
+
if (editors.length === 0) return false;
|
|
4324
|
+
for (const editor of editors) {
|
|
4325
|
+
if (editor.getUrlValidationMessage()) return false;
|
|
4326
|
+
if (editor.getLinkTitleValidationMessage()) return false;
|
|
4327
|
+
}
|
|
4328
|
+
// Every editor in the thread must have content
|
|
4329
|
+
return editors.every((editor) => {
|
|
4330
|
+
const data = editor.getData();
|
|
4331
|
+
return (
|
|
4332
|
+
!!data.body ||
|
|
4333
|
+
!!data.title.trim() ||
|
|
4334
|
+
!!data.url.trim() ||
|
|
4335
|
+
!!data.quoteText.trim() ||
|
|
4336
|
+
data.attachments.length > 0
|
|
4337
|
+
);
|
|
4338
|
+
});
|
|
4339
|
+
}
|
|
4340
|
+
|
|
2877
4341
|
const editor = this._editor;
|
|
2878
4342
|
if (!editor) return false;
|
|
2879
|
-
if (this._getSlugValidationMessage()) return false;
|
|
2880
4343
|
if (editor.getUrlValidationMessage()) return false;
|
|
2881
4344
|
if (editor.getLinkTitleValidationMessage()) return false;
|
|
2882
4345
|
|
|
@@ -2890,6 +4353,21 @@ export class JantComposeDialog extends LitElement {
|
|
|
2890
4353
|
return this._hasContent();
|
|
2891
4354
|
}
|
|
2892
4355
|
|
|
4356
|
+
private _focusPublishPanelInitialField() {
|
|
4357
|
+
const selector = this._visibilityLocked
|
|
4358
|
+
? ".compose-publish-date-input"
|
|
4359
|
+
: ".compose-publish-option[role='radio']";
|
|
4360
|
+
this.querySelector<HTMLElement>(selector)?.focus();
|
|
4361
|
+
}
|
|
4362
|
+
|
|
4363
|
+
private _closePublishPanel(restoreFocus = false) {
|
|
4364
|
+
if (!this._showPublishPanel) return;
|
|
4365
|
+
this._showPublishPanel = false;
|
|
4366
|
+
if (restoreFocus) {
|
|
4367
|
+
this.updateComplete.then(() => this._restorePageEditorFocus());
|
|
4368
|
+
}
|
|
4369
|
+
}
|
|
4370
|
+
|
|
2893
4371
|
private _togglePublishPanel() {
|
|
2894
4372
|
this._showCollection = false;
|
|
2895
4373
|
this._collectionSearch = "";
|
|
@@ -2897,6 +4375,7 @@ export class JantComposeDialog extends LitElement {
|
|
|
2897
4375
|
this._showPublishPanel = nextOpen;
|
|
2898
4376
|
if (nextOpen) {
|
|
2899
4377
|
this._scheduleSuggestedSlugRefresh(true);
|
|
4378
|
+
this.updateComplete.then(() => this._focusPublishPanelInitialField());
|
|
2900
4379
|
}
|
|
2901
4380
|
}
|
|
2902
4381
|
|
|
@@ -2905,8 +4384,13 @@ export class JantComposeDialog extends LitElement {
|
|
|
2905
4384
|
this._visibility = visibility;
|
|
2906
4385
|
if (!this._editPostId && !this._draftSourceId && !this._replyToId) {
|
|
2907
4386
|
JantComposeDialog._lastNewPostVisibility = visibility;
|
|
4387
|
+
if (this._sourceCollectionId) {
|
|
4388
|
+
JantComposeDialog._setCollectionVisibility(
|
|
4389
|
+
this._sourceCollectionId,
|
|
4390
|
+
visibility,
|
|
4391
|
+
);
|
|
4392
|
+
}
|
|
2908
4393
|
}
|
|
2909
|
-
this._showPublishPanel = false;
|
|
2910
4394
|
}
|
|
2911
4395
|
|
|
2912
4396
|
private _onSlugInput(e: Event) {
|
|
@@ -2921,6 +4405,21 @@ export class JantComposeDialog extends LitElement {
|
|
|
2921
4405
|
this._scheduleSuggestedSlugRefresh();
|
|
2922
4406
|
}
|
|
2923
4407
|
|
|
4408
|
+
private _onPublishedAtInput(e: Event) {
|
|
4409
|
+
this._publishedAtInput = (e.target as HTMLInputElement).value;
|
|
4410
|
+
}
|
|
4411
|
+
|
|
4412
|
+
private _resetPublishedAt() {
|
|
4413
|
+
const currentTimestamp = this._getCurrentTimestamp();
|
|
4414
|
+
this._publishedAtInput = toLocalDateInputValue(currentTimestamp);
|
|
4415
|
+
this._publishedAtTimeMinutes = getTimestampTimeMinutes(currentTimestamp);
|
|
4416
|
+
this.updateComplete.then(() => {
|
|
4417
|
+
this.querySelector<HTMLInputElement>(
|
|
4418
|
+
".compose-publish-date-input",
|
|
4419
|
+
)?.focus();
|
|
4420
|
+
});
|
|
4421
|
+
}
|
|
4422
|
+
|
|
2924
4423
|
private _renderVisibilityIcon(
|
|
2925
4424
|
visibility: ComposeVisibility,
|
|
2926
4425
|
variant: "menu" | "toggle" = "menu",
|
|
@@ -2939,10 +4438,9 @@ export class JantComposeDialog extends LitElement {
|
|
|
2939
4438
|
);
|
|
2940
4439
|
}
|
|
2941
4440
|
|
|
2942
|
-
private
|
|
4441
|
+
private _renderPublishVisibilityChip(
|
|
2943
4442
|
visibility: ComposeVisibility,
|
|
2944
4443
|
label: string,
|
|
2945
|
-
hint: string,
|
|
2946
4444
|
) {
|
|
2947
4445
|
const selected = this._visibility === visibility;
|
|
2948
4446
|
|
|
@@ -2950,8 +4448,8 @@ export class JantComposeDialog extends LitElement {
|
|
|
2950
4448
|
<button
|
|
2951
4449
|
type="button"
|
|
2952
4450
|
class=${classMap({
|
|
2953
|
-
"compose-publish-
|
|
2954
|
-
"compose-publish-
|
|
4451
|
+
"compose-publish-chip": true,
|
|
4452
|
+
"compose-publish-chip-selected": selected,
|
|
2955
4453
|
})}
|
|
2956
4454
|
role="radio"
|
|
2957
4455
|
aria-checked=${selected ? "true" : "false"}
|
|
@@ -2959,20 +4457,74 @@ export class JantComposeDialog extends LitElement {
|
|
|
2959
4457
|
@click=${() => this._setVisibility(visibility)}
|
|
2960
4458
|
>
|
|
2961
4459
|
${this._renderVisibilityIcon(visibility)}
|
|
2962
|
-
<span class="compose-publish-
|
|
2963
|
-
<span class="compose-publish-row-label">${label}</span>
|
|
2964
|
-
<span class="compose-publish-row-hint">${hint}</span>
|
|
2965
|
-
</span>
|
|
2966
|
-
${selected
|
|
2967
|
-
? renderComposePublishActionIcon(
|
|
2968
|
-
COMPOSE_PUBLISH_ACTION_ICONS.check,
|
|
2969
|
-
"compose-publish-row-check",
|
|
2970
|
-
)
|
|
2971
|
-
: nothing}
|
|
4460
|
+
<span class="compose-publish-chip-label">${label}</span>
|
|
2972
4461
|
</button>
|
|
2973
4462
|
`;
|
|
2974
4463
|
}
|
|
2975
4464
|
|
|
4465
|
+
private _getVisibilityHint(): string {
|
|
4466
|
+
switch (this._visibility) {
|
|
4467
|
+
case "public":
|
|
4468
|
+
return this.labels.publishVisibilityPublicHint;
|
|
4469
|
+
case "latest_hidden":
|
|
4470
|
+
return this.labels.publishVisibilityHiddenFromLatestHint;
|
|
4471
|
+
case "private":
|
|
4472
|
+
return this.labels.publishVisibilityPrivateHint;
|
|
4473
|
+
}
|
|
4474
|
+
}
|
|
4475
|
+
|
|
4476
|
+
private _renderPublishDateSection() {
|
|
4477
|
+
const publishedAtError = this._getPublishedAtValidationMessage();
|
|
4478
|
+
|
|
4479
|
+
return html`
|
|
4480
|
+
<section class="compose-publish-section">
|
|
4481
|
+
<div class="compose-publish-section-header">
|
|
4482
|
+
<div class="compose-publish-section-copy">
|
|
4483
|
+
<p class="compose-publish-section-label">
|
|
4484
|
+
${this.labels.publishDateLabel}
|
|
4485
|
+
</p>
|
|
4486
|
+
<p class="compose-publish-section-hint">
|
|
4487
|
+
${this.labels.publishDateHint}
|
|
4488
|
+
</p>
|
|
4489
|
+
</div>
|
|
4490
|
+
${this._hasPublishedAtValue()
|
|
4491
|
+
? html`
|
|
4492
|
+
<button
|
|
4493
|
+
type="button"
|
|
4494
|
+
class="compose-publish-section-action"
|
|
4495
|
+
@click=${() => this._resetPublishedAt()}
|
|
4496
|
+
>
|
|
4497
|
+
${this.labels.publishDateReset}
|
|
4498
|
+
</button>
|
|
4499
|
+
`
|
|
4500
|
+
: nothing}
|
|
4501
|
+
</div>
|
|
4502
|
+
<div class="compose-publish-date-field">
|
|
4503
|
+
<div class="compose-publish-date-input-wrap">
|
|
4504
|
+
<input
|
|
4505
|
+
type="date"
|
|
4506
|
+
class="compose-input compose-publish-date-input"
|
|
4507
|
+
.value=${this._publishedAtInput}
|
|
4508
|
+
max=${this._getPublishedAtMaxInput()}
|
|
4509
|
+
aria-invalid=${publishedAtError ? "true" : "false"}
|
|
4510
|
+
@input=${(e: Event) => this._onPublishedAtInput(e)}
|
|
4511
|
+
/>
|
|
4512
|
+
</div>
|
|
4513
|
+
${publishedAtError
|
|
4514
|
+
? html`<p
|
|
4515
|
+
class=${classMap({
|
|
4516
|
+
"compose-publish-date-status": true,
|
|
4517
|
+
"compose-publish-date-status-error": true,
|
|
4518
|
+
})}
|
|
4519
|
+
>
|
|
4520
|
+
${publishedAtError}
|
|
4521
|
+
</p>`
|
|
4522
|
+
: nothing}
|
|
4523
|
+
</div>
|
|
4524
|
+
</section>
|
|
4525
|
+
`;
|
|
4526
|
+
}
|
|
4527
|
+
|
|
2976
4528
|
private _renderPublishSlugSection() {
|
|
2977
4529
|
const slugError = this._getSlugValidationMessage();
|
|
2978
4530
|
const slugStatus = this._getSlugStatusMessage();
|
|
@@ -3084,57 +4636,258 @@ export class JantComposeDialog extends LitElement {
|
|
|
3084
4636
|
`;
|
|
3085
4637
|
}
|
|
3086
4638
|
|
|
3087
|
-
private
|
|
3088
|
-
if (!this.
|
|
4639
|
+
private _renderQuietReplySection() {
|
|
4640
|
+
if (!this._replyToId) return nothing;
|
|
4641
|
+
|
|
4642
|
+
return html`
|
|
4643
|
+
<section class="compose-publish-section">
|
|
4644
|
+
<label class="compose-publish-switch-row">
|
|
4645
|
+
<div class="compose-publish-section-copy">
|
|
4646
|
+
<p class="compose-publish-section-label">
|
|
4647
|
+
${this.labels.quietReplyLabel}
|
|
4648
|
+
</p>
|
|
4649
|
+
<p class="compose-publish-section-hint">
|
|
4650
|
+
${this.labels.quietReplyHint}
|
|
4651
|
+
</p>
|
|
4652
|
+
</div>
|
|
4653
|
+
<input
|
|
4654
|
+
type="checkbox"
|
|
4655
|
+
role="switch"
|
|
4656
|
+
class="input"
|
|
4657
|
+
.checked=${this._quietReply}
|
|
4658
|
+
@change=${(e: Event) => {
|
|
4659
|
+
this._quietReply = (e.target as HTMLInputElement).checked;
|
|
4660
|
+
}}
|
|
4661
|
+
/>
|
|
4662
|
+
</label>
|
|
4663
|
+
</section>
|
|
4664
|
+
`;
|
|
4665
|
+
}
|
|
4666
|
+
|
|
4667
|
+
private _renderPublishPanelSections() {
|
|
4668
|
+
return html`
|
|
4669
|
+
${this._replyToId ? this._renderQuietReplySection() : nothing}
|
|
4670
|
+
${this._visibilityLocked
|
|
4671
|
+
? nothing
|
|
4672
|
+
: html`
|
|
4673
|
+
<section class="compose-publish-section">
|
|
4674
|
+
<div class="compose-publish-section-header">
|
|
4675
|
+
<div class="compose-publish-section-copy">
|
|
4676
|
+
<p class="compose-publish-section-label">
|
|
4677
|
+
${this.labels.publishVisibilityLabel}
|
|
4678
|
+
</p>
|
|
4679
|
+
</div>
|
|
4680
|
+
</div>
|
|
4681
|
+
<div class="compose-publish-chips" role="radiogroup">
|
|
4682
|
+
${this._renderPublishVisibilityChip(
|
|
4683
|
+
"public",
|
|
4684
|
+
this.labels.publishVisibilityPublic,
|
|
4685
|
+
)}
|
|
4686
|
+
${this._renderPublishVisibilityChip(
|
|
4687
|
+
"latest_hidden",
|
|
4688
|
+
this.labels.publishVisibilityHiddenFromLatest,
|
|
4689
|
+
)}
|
|
4690
|
+
${this._renderPublishVisibilityChip(
|
|
4691
|
+
"private",
|
|
4692
|
+
this.labels.publishVisibilityPrivate,
|
|
4693
|
+
)}
|
|
4694
|
+
</div>
|
|
4695
|
+
<p class="compose-publish-chip-hint">
|
|
4696
|
+
${this._getVisibilityHint()}
|
|
4697
|
+
</p>
|
|
4698
|
+
</section>
|
|
4699
|
+
`}
|
|
4700
|
+
${this._visibilityLocked && !this._replyToId
|
|
4701
|
+
? nothing
|
|
4702
|
+
: html`<div class="compose-publish-divider" aria-hidden="true"></div>`}
|
|
4703
|
+
${this._renderPublishDateSection()}
|
|
4704
|
+
<div class="compose-publish-divider" aria-hidden="true"></div>
|
|
4705
|
+
${this._renderPublishSlugSection()}
|
|
4706
|
+
`;
|
|
4707
|
+
}
|
|
4708
|
+
|
|
4709
|
+
private _renderDesktopPublishPanel() {
|
|
4710
|
+
if (!this._showPublishPanel || this._publishPanelFullscreen) {
|
|
4711
|
+
return nothing;
|
|
4712
|
+
}
|
|
3089
4713
|
|
|
3090
4714
|
return html`
|
|
3091
4715
|
<div
|
|
3092
|
-
class="compose-publish-panel"
|
|
4716
|
+
class="compose-publish-panel compose-publish-panel-desktop"
|
|
3093
4717
|
role="dialog"
|
|
3094
4718
|
aria-label=${this.labels.publishSettings}
|
|
4719
|
+
data-position="up"
|
|
3095
4720
|
data-compose-publish-panel
|
|
4721
|
+
data-compose-publish-panel-desktop
|
|
3096
4722
|
>
|
|
3097
|
-
${this.
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
</div>
|
|
3108
|
-
<div class="compose-publish-list" role="radiogroup">
|
|
3109
|
-
${this._renderPublishVisibilityOption(
|
|
3110
|
-
"public",
|
|
3111
|
-
this.labels.publishVisibilityPublic,
|
|
3112
|
-
this.labels.publishVisibilityPublicHint,
|
|
3113
|
-
)}
|
|
3114
|
-
${this._renderPublishVisibilityOption(
|
|
3115
|
-
"latest_hidden",
|
|
3116
|
-
this.labels.publishVisibilityHiddenFromLatest,
|
|
3117
|
-
this.labels.publishVisibilityHiddenFromLatestHint,
|
|
3118
|
-
)}
|
|
3119
|
-
${this._renderPublishVisibilityOption(
|
|
3120
|
-
"private",
|
|
3121
|
-
this.labels.publishVisibilityPrivate,
|
|
3122
|
-
this.labels.publishVisibilityPrivateHint,
|
|
3123
|
-
)}
|
|
3124
|
-
</div>
|
|
3125
|
-
</section>
|
|
3126
|
-
`}
|
|
3127
|
-
${this._visibilityLocked
|
|
3128
|
-
? nothing
|
|
3129
|
-
: html`<div
|
|
3130
|
-
class="compose-publish-divider"
|
|
3131
|
-
aria-hidden="true"
|
|
3132
|
-
></div>`}
|
|
3133
|
-
${this._renderPublishSlugSection()}
|
|
4723
|
+
${this._renderPublishPanelSections()}
|
|
4724
|
+
<div class="compose-publish-panel-footer">
|
|
4725
|
+
<button
|
|
4726
|
+
type="button"
|
|
4727
|
+
class="compose-publish-done"
|
|
4728
|
+
@click=${() => this._closePublishPanel(true)}
|
|
4729
|
+
>
|
|
4730
|
+
${this.labels.done}
|
|
4731
|
+
</button>
|
|
4732
|
+
</div>
|
|
3134
4733
|
</div>
|
|
3135
4734
|
`;
|
|
3136
4735
|
}
|
|
3137
4736
|
|
|
4737
|
+
private _renderMobilePublishPanel() {
|
|
4738
|
+
if (!this._showPublishPanel || !this._publishPanelFullscreen) {
|
|
4739
|
+
return nothing;
|
|
4740
|
+
}
|
|
4741
|
+
|
|
4742
|
+
return html`
|
|
4743
|
+
<div
|
|
4744
|
+
class="compose-publish-panel compose-publish-panel-mobile"
|
|
4745
|
+
role="dialog"
|
|
4746
|
+
aria-label=${this.labels.publishSettings}
|
|
4747
|
+
aria-modal="true"
|
|
4748
|
+
data-compose-publish-panel
|
|
4749
|
+
data-compose-publish-panel-mobile
|
|
4750
|
+
>
|
|
4751
|
+
<div class="compose-alt-header compose-publish-mobile-header">
|
|
4752
|
+
<button
|
|
4753
|
+
type="button"
|
|
4754
|
+
class="compose-attached-panel-back"
|
|
4755
|
+
@click=${() => this._closePublishPanel(true)}
|
|
4756
|
+
>
|
|
4757
|
+
<svg
|
|
4758
|
+
class="icon-fine"
|
|
4759
|
+
width="16"
|
|
4760
|
+
height="16"
|
|
4761
|
+
viewBox="0 0 16 16"
|
|
4762
|
+
fill="none"
|
|
4763
|
+
stroke="currentColor"
|
|
4764
|
+
stroke-width="1.5"
|
|
4765
|
+
stroke-linecap="round"
|
|
4766
|
+
stroke-linejoin="round"
|
|
4767
|
+
>
|
|
4768
|
+
<path d="M11 3L6 8l5 5" />
|
|
4769
|
+
</svg>
|
|
4770
|
+
</button>
|
|
4771
|
+
<span class="compose-alt-title">${this.labels.publishSettings}</span>
|
|
4772
|
+
<button
|
|
4773
|
+
type="button"
|
|
4774
|
+
class="compose-attached-cancel compose-publish-mobile-done"
|
|
4775
|
+
@click=${() => this._closePublishPanel(true)}
|
|
4776
|
+
>
|
|
4777
|
+
${this.labels.done}
|
|
4778
|
+
</button>
|
|
4779
|
+
</div>
|
|
4780
|
+
<div class="compose-publish-mobile-body">
|
|
4781
|
+
${this._renderPublishPanelSections()}
|
|
4782
|
+
</div>
|
|
4783
|
+
</div>
|
|
4784
|
+
`;
|
|
4785
|
+
}
|
|
4786
|
+
|
|
4787
|
+
private _renderPublishSummary(summary: ComposePublishSummaryChip) {
|
|
4788
|
+
const isPublishedAt = summary.kind === "publishedAt";
|
|
4789
|
+
|
|
4790
|
+
return html`
|
|
4791
|
+
<button
|
|
4792
|
+
type="button"
|
|
4793
|
+
class="compose-publish-summary"
|
|
4794
|
+
data-compose-publish-summary=${summary.kind}
|
|
4795
|
+
data-compose-publish-date-summary=${isPublishedAt ? "" : nothing}
|
|
4796
|
+
data-compose-publish-slug-summary=${isPublishedAt ? nothing : ""}
|
|
4797
|
+
data-publish-summary-value=${summary.value}
|
|
4798
|
+
data-publish-date=${isPublishedAt ? summary.value : nothing}
|
|
4799
|
+
data-publish-slug=${isPublishedAt ? nothing : summary.value}
|
|
4800
|
+
aria-label=${summary.actionLabel}
|
|
4801
|
+
title=${summary.actionLabel}
|
|
4802
|
+
?disabled=${this._loading}
|
|
4803
|
+
@click=${() =>
|
|
4804
|
+
isPublishedAt
|
|
4805
|
+
? this._revealPublishedAtField()
|
|
4806
|
+
: this._revealSlugField()}
|
|
4807
|
+
>
|
|
4808
|
+
<span class="compose-publish-summary-icon" aria-hidden="true">
|
|
4809
|
+
${isPublishedAt
|
|
4810
|
+
? html`<svg
|
|
4811
|
+
width="14"
|
|
4812
|
+
height="14"
|
|
4813
|
+
viewBox="0 0 16 16"
|
|
4814
|
+
fill="none"
|
|
4815
|
+
stroke="currentColor"
|
|
4816
|
+
stroke-width="1.35"
|
|
4817
|
+
stroke-linecap="round"
|
|
4818
|
+
stroke-linejoin="round"
|
|
4819
|
+
>
|
|
4820
|
+
<rect x="2.75" y="3.45" width="10.5" height="9.8" rx="2.2" />
|
|
4821
|
+
<path d="M5.35 2.55v2.1" />
|
|
4822
|
+
<path d="M10.65 2.55v2.1" />
|
|
4823
|
+
<path d="M2.75 6.2h10.5" />
|
|
4824
|
+
</svg>`
|
|
4825
|
+
: html`<svg
|
|
4826
|
+
width="14"
|
|
4827
|
+
height="14"
|
|
4828
|
+
viewBox="0 0 16 16"
|
|
4829
|
+
fill="none"
|
|
4830
|
+
stroke="currentColor"
|
|
4831
|
+
stroke-width="1.35"
|
|
4832
|
+
stroke-linecap="round"
|
|
4833
|
+
stroke-linejoin="round"
|
|
4834
|
+
>
|
|
4835
|
+
<path d="M9.75 4.15h1.35a2.75 2.75 0 0 1 0 5.5H9.75" />
|
|
4836
|
+
<path d="M6.25 9.65H4.9a2.75 2.75 0 1 1 0-5.5h1.35" />
|
|
4837
|
+
<path d="M5.65 8h4.7" />
|
|
4838
|
+
</svg>`}
|
|
4839
|
+
</span>
|
|
4840
|
+
<span class="compose-publish-summary-text">${summary.text}</span>
|
|
4841
|
+
</button>
|
|
4842
|
+
`;
|
|
4843
|
+
}
|
|
4844
|
+
|
|
4845
|
+
private _renderPublishSummaries() {
|
|
4846
|
+
const summaries = this._getPublishSummaryChips();
|
|
4847
|
+
if (summaries.length === 0) return nothing;
|
|
4848
|
+
|
|
4849
|
+
return html`
|
|
4850
|
+
<div class="compose-publish-summaries">
|
|
4851
|
+
${summaries.map((summary) => this._renderPublishSummary(summary))}
|
|
4852
|
+
</div>
|
|
4853
|
+
`;
|
|
4854
|
+
}
|
|
4855
|
+
|
|
4856
|
+
private _updatePublishPanelLayout() {
|
|
4857
|
+
if (this._publishPanelFullscreen) return;
|
|
4858
|
+
|
|
4859
|
+
const publishGroup = this.querySelector<HTMLElement>(
|
|
4860
|
+
".compose-publish-group",
|
|
4861
|
+
);
|
|
4862
|
+
const panel = this.querySelector<HTMLElement>(
|
|
4863
|
+
"[data-compose-publish-panel-desktop]",
|
|
4864
|
+
);
|
|
4865
|
+
if (!publishGroup || !panel) return;
|
|
4866
|
+
|
|
4867
|
+
const visualViewport = globalThis.visualViewport;
|
|
4868
|
+
const viewportTop = visualViewport?.offsetTop ?? 0;
|
|
4869
|
+
const viewportBottom =
|
|
4870
|
+
viewportTop + (visualViewport?.height ?? globalThis.innerHeight);
|
|
4871
|
+
const groupRect = publishGroup.getBoundingClientRect();
|
|
4872
|
+
const edgePadding = 12;
|
|
4873
|
+
const gap = 10;
|
|
4874
|
+
const topBoundary = viewportTop + edgePadding;
|
|
4875
|
+
const bottomBoundary = viewportBottom - edgePadding;
|
|
4876
|
+
const availableAbove = Math.max(0, groupRect.top - topBoundary - gap);
|
|
4877
|
+
const availableBelow = Math.max(0, bottomBoundary - groupRect.bottom - gap);
|
|
4878
|
+
const direction = availableBelow >= availableAbove ? "down" : "up";
|
|
4879
|
+
const maxHeight = Math.max(
|
|
4880
|
+
1,
|
|
4881
|
+
Math.floor(direction === "up" ? availableAbove : availableBelow),
|
|
4882
|
+
);
|
|
4883
|
+
|
|
4884
|
+
panel.dataset.position = direction;
|
|
4885
|
+
panel.style.setProperty(
|
|
4886
|
+
"--compose-publish-panel-max-height",
|
|
4887
|
+
`${maxHeight}px`,
|
|
4888
|
+
);
|
|
4889
|
+
}
|
|
4890
|
+
|
|
3138
4891
|
private _renderPublishButton() {
|
|
3139
4892
|
const spinner = html`<svg
|
|
3140
4893
|
class="animate-spin size-4"
|
|
@@ -3152,59 +4905,405 @@ export class JantComposeDialog extends LitElement {
|
|
|
3152
4905
|
const canPublish = this._canPublish();
|
|
3153
4906
|
|
|
3154
4907
|
return html`
|
|
3155
|
-
<div class="compose-publish-
|
|
3156
|
-
${this.
|
|
3157
|
-
? html`<div
|
|
3158
|
-
class="compose-dropdown-backdrop"
|
|
3159
|
-
@click=${() => {
|
|
3160
|
-
this._showPublishPanel = false;
|
|
3161
|
-
}}
|
|
3162
|
-
></div>`
|
|
3163
|
-
: nothing}
|
|
4908
|
+
<div class="compose-publish-shell">
|
|
4909
|
+
${this._renderPublishSummaries()}
|
|
3164
4910
|
<div
|
|
3165
|
-
role="group"
|
|
3166
4911
|
class=${classMap({
|
|
3167
|
-
"compose-publish-
|
|
3168
|
-
"compose-publish-
|
|
4912
|
+
"compose-publish-group": true,
|
|
4913
|
+
"compose-publish-group-open": this._showPublishPanel,
|
|
3169
4914
|
})}
|
|
3170
4915
|
>
|
|
3171
|
-
|
|
3172
|
-
|
|
4916
|
+
${this._showPublishPanel && !this._publishPanelFullscreen
|
|
4917
|
+
? html`<div
|
|
4918
|
+
class="compose-dropdown-backdrop"
|
|
4919
|
+
@click=${() => this._closePublishPanel(true)}
|
|
4920
|
+
></div>`
|
|
4921
|
+
: nothing}
|
|
4922
|
+
<div
|
|
4923
|
+
role="group"
|
|
3173
4924
|
class=${classMap({
|
|
3174
|
-
"compose-publish-
|
|
3175
|
-
"compose-publish-
|
|
4925
|
+
"compose-publish-buttons": true,
|
|
4926
|
+
"compose-publish-buttons-inactive": !canPublish && !this._loading,
|
|
3176
4927
|
})}
|
|
3177
|
-
?disabled=${!canPublish}
|
|
3178
|
-
@click=${() => this._submit("published")}
|
|
3179
4928
|
>
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
4929
|
+
<button
|
|
4930
|
+
type="button"
|
|
4931
|
+
class=${classMap({
|
|
4932
|
+
"compose-publish-main": true,
|
|
4933
|
+
"compose-publish-main-loading": this._loading,
|
|
4934
|
+
})}
|
|
4935
|
+
?disabled=${!canPublish}
|
|
4936
|
+
@click=${() => void this._submit("published")}
|
|
4937
|
+
>
|
|
4938
|
+
${this._loading ? spinner : nothing} ${this._getSubmitLabel()}
|
|
4939
|
+
</button>
|
|
4940
|
+
<button
|
|
4941
|
+
type="button"
|
|
4942
|
+
class=${classMap({
|
|
4943
|
+
"compose-publish-toggle": true,
|
|
4944
|
+
"compose-publish-toggle-loading": this._loading,
|
|
4945
|
+
})}
|
|
4946
|
+
?disabled=${this._loading}
|
|
4947
|
+
aria-haspopup="dialog"
|
|
4948
|
+
aria-expanded=${this._showPublishPanel ? "true" : "false"}
|
|
4949
|
+
aria-label=${this.labels.publishSettings}
|
|
4950
|
+
title=${this.labels.publishSettings}
|
|
4951
|
+
@click=${() => this._togglePublishPanel()}
|
|
4952
|
+
>
|
|
4953
|
+
${renderComposePublishActionIcon(
|
|
4954
|
+
COMPOSE_PUBLISH_ACTION_ICONS.chevron,
|
|
4955
|
+
"compose-publish-toggle-chevron",
|
|
4956
|
+
)}
|
|
4957
|
+
</button>
|
|
4958
|
+
</div>
|
|
4959
|
+
${this._renderDesktopPublishPanel()}
|
|
4960
|
+
</div>
|
|
4961
|
+
</div>
|
|
4962
|
+
`;
|
|
4963
|
+
}
|
|
4964
|
+
|
|
4965
|
+
private _renderEditLoadingState() {
|
|
4966
|
+
return html`
|
|
4967
|
+
<div
|
|
4968
|
+
class="compose-edit-loading"
|
|
4969
|
+
role="status"
|
|
4970
|
+
aria-live="polite"
|
|
4971
|
+
aria-busy="true"
|
|
4972
|
+
>
|
|
4973
|
+
<div class="compose-edit-loading-status">
|
|
4974
|
+
<svg
|
|
4975
|
+
class="animate-spin size-5"
|
|
4976
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
4977
|
+
viewBox="0 0 24 24"
|
|
4978
|
+
fill="none"
|
|
4979
|
+
stroke="currentColor"
|
|
4980
|
+
stroke-width="2"
|
|
4981
|
+
stroke-linecap="round"
|
|
4982
|
+
stroke-linejoin="round"
|
|
4983
|
+
aria-hidden="true"
|
|
3194
4984
|
>
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
)}
|
|
3199
|
-
</button>
|
|
4985
|
+
<path d="M21 12a9 9 0 1 1-6.219-8.56" />
|
|
4986
|
+
</svg>
|
|
4987
|
+
<span>${this.labels.loadingPost}</span>
|
|
3200
4988
|
</div>
|
|
3201
|
-
|
|
4989
|
+
<div class="compose-edit-loading-skeleton">
|
|
4990
|
+
<div class="skel-input compose-edit-loading-title"></div>
|
|
4991
|
+
<div class="skel-section-lg compose-edit-loading-body"></div>
|
|
4992
|
+
<div class="compose-edit-loading-footer">
|
|
4993
|
+
<div class="skel-input compose-edit-loading-chip"></div>
|
|
4994
|
+
<div class="skel-input compose-edit-loading-submit"></div>
|
|
4995
|
+
</div>
|
|
4996
|
+
</div>
|
|
4997
|
+
</div>
|
|
4998
|
+
`;
|
|
4999
|
+
}
|
|
5000
|
+
|
|
5001
|
+
// ── Thread compose ───────────────────────────────────────────────
|
|
5002
|
+
|
|
5003
|
+
private _addThreadItem() {
|
|
5004
|
+
const nextCount =
|
|
5005
|
+
this._threadItems.length === 0 ? 2 : this._threadItems.length + 1;
|
|
5006
|
+
if (nextCount > MAX_THREAD_POSTS) {
|
|
5007
|
+
showToast(this._getThreadLimitMessage(), "error");
|
|
5008
|
+
return;
|
|
5009
|
+
}
|
|
5010
|
+
|
|
5011
|
+
const lastFormat =
|
|
5012
|
+
this._threadItems.length > 0
|
|
5013
|
+
? this._threadItems[this._threadItems.length - 1].format
|
|
5014
|
+
: this._format;
|
|
5015
|
+
|
|
5016
|
+
if (this._threadItems.length === 0) {
|
|
5017
|
+
// Entering thread mode: snapshot current single editor's state
|
|
5018
|
+
const currentEditor = this._editor;
|
|
5019
|
+
const editorState = currentEditor?.getEditorState() ?? null;
|
|
5020
|
+
const editorData = currentEditor?.getData();
|
|
5021
|
+
const bodyJson = currentEditor?.getNormalizedBodyJson() ?? null;
|
|
5022
|
+
|
|
5023
|
+
this._threadItems = [
|
|
5024
|
+
{ id: crypto.randomUUID(), format: this._format },
|
|
5025
|
+
{ id: crypto.randomUUID(), format: lastFormat },
|
|
5026
|
+
];
|
|
5027
|
+
|
|
5028
|
+
// Capture rating state before re-render (these can't change asynchronously)
|
|
5029
|
+
const capturedRating = currentEditor?._rating ?? 0;
|
|
5030
|
+
const capturedShowRating = currentEditor?._showRating ?? false;
|
|
5031
|
+
|
|
5032
|
+
// Restore first thread item's content from the snapshot
|
|
5033
|
+
if (editorState || editorData) {
|
|
5034
|
+
this.updateComplete.then(() => {
|
|
5035
|
+
const editors = this.querySelectorAll<JantComposeEditor>(
|
|
5036
|
+
"jant-compose-editor",
|
|
5037
|
+
);
|
|
5038
|
+
const firstEditor = editors[0];
|
|
5039
|
+
if (!firstEditor) return;
|
|
5040
|
+
if (editorState) {
|
|
5041
|
+
firstEditor.setEditorState(
|
|
5042
|
+
editorState.json,
|
|
5043
|
+
editorState.title,
|
|
5044
|
+
editorState.showTitle,
|
|
5045
|
+
editorState.selection,
|
|
5046
|
+
);
|
|
5047
|
+
}
|
|
5048
|
+
if (editorData) {
|
|
5049
|
+
if (this._format === "link" && editorData.url) {
|
|
5050
|
+
firstEditor._url = editorData.url;
|
|
5051
|
+
} else if (this._format === "quote") {
|
|
5052
|
+
if (editorData.quoteText)
|
|
5053
|
+
firstEditor._quoteText = editorData.quoteText;
|
|
5054
|
+
if (editorData.quoteAuthor)
|
|
5055
|
+
firstEditor._quoteAuthor = editorData.quoteAuthor;
|
|
5056
|
+
}
|
|
5057
|
+
if (bodyJson) {
|
|
5058
|
+
firstEditor._bodyJson = bodyJson;
|
|
5059
|
+
}
|
|
5060
|
+
}
|
|
5061
|
+
// Read attachment state from the old editor NOW (after re-render) so
|
|
5062
|
+
// we get the latest mediaId for any uploads that completed during the
|
|
5063
|
+
// render cycle. The old editor element is still in memory even though
|
|
5064
|
+
// it has been removed from the DOM.
|
|
5065
|
+
const latestAttachments = currentEditor?._attachments ?? [];
|
|
5066
|
+
const latestAttachmentOrder = currentEditor?._attachmentOrder ?? [];
|
|
5067
|
+
const latestAttachedTexts = currentEditor?._attachedTexts ?? [];
|
|
5068
|
+
if (latestAttachments.length > 0) {
|
|
5069
|
+
firstEditor._attachments = [...latestAttachments];
|
|
5070
|
+
firstEditor._attachmentOrder = [...latestAttachmentOrder];
|
|
5071
|
+
}
|
|
5072
|
+
if (latestAttachedTexts.length > 0) {
|
|
5073
|
+
firstEditor._attachedTexts = [...latestAttachedTexts];
|
|
5074
|
+
}
|
|
5075
|
+
if (capturedRating > 0) {
|
|
5076
|
+
firstEditor._rating = capturedRating;
|
|
5077
|
+
firstEditor._showRating = capturedShowRating;
|
|
5078
|
+
}
|
|
5079
|
+
});
|
|
5080
|
+
}
|
|
5081
|
+
} else {
|
|
5082
|
+
this._threadItems = [
|
|
5083
|
+
...this._threadItems,
|
|
5084
|
+
{ id: crypto.randomUUID(), format: lastFormat },
|
|
5085
|
+
];
|
|
5086
|
+
}
|
|
5087
|
+
|
|
5088
|
+
this._focusedThreadIndex = this._threadItems.length - 1;
|
|
5089
|
+
this.updateComplete.then(() => {
|
|
5090
|
+
const editors = this.querySelectorAll<JantComposeEditor>(
|
|
5091
|
+
"jant-compose-editor",
|
|
5092
|
+
);
|
|
5093
|
+
editors[this._focusedThreadIndex]?.focusInput();
|
|
5094
|
+
});
|
|
5095
|
+
}
|
|
5096
|
+
|
|
5097
|
+
private _removeThreadItem(index: number) {
|
|
5098
|
+
if (this._threadItems.length <= 1) return;
|
|
5099
|
+
const newItems = this._threadItems.filter((_, i) => i !== index);
|
|
5100
|
+
|
|
5101
|
+
if (newItems.length === 1) {
|
|
5102
|
+
// Exiting thread mode: capture remaining thread editor's state and restore
|
|
5103
|
+
// it to the single-post editor after thread mode is cleared.
|
|
5104
|
+
const editors = this.querySelectorAll<JantComposeEditor>(
|
|
5105
|
+
"jant-compose-editor",
|
|
5106
|
+
);
|
|
5107
|
+
const remainingIndex = index === 0 ? 1 : 0;
|
|
5108
|
+
const remainingEditor = editors[remainingIndex] ?? null;
|
|
5109
|
+
const editorState = remainingEditor?.getEditorState() ?? null;
|
|
5110
|
+
const editorData = remainingEditor?.getData();
|
|
5111
|
+
const bodyJson = remainingEditor?.getNormalizedBodyJson() ?? null;
|
|
5112
|
+
const remainingFormat = newItems[0].format;
|
|
5113
|
+
// Capture rating state before re-render (can't change asynchronously)
|
|
5114
|
+
const capturedRating = remainingEditor?._rating ?? 0;
|
|
5115
|
+
const capturedShowRating = remainingEditor?._showRating ?? false;
|
|
5116
|
+
|
|
5117
|
+
this._threadItems = [];
|
|
5118
|
+
this._focusedThreadIndex = 0;
|
|
5119
|
+
this._format = remainingFormat;
|
|
5120
|
+
|
|
5121
|
+
this.updateComplete.then(() => {
|
|
5122
|
+
const singleEditor = this._editor;
|
|
5123
|
+
if (!singleEditor) return;
|
|
5124
|
+
singleEditor.format = remainingFormat;
|
|
5125
|
+
if (editorState) {
|
|
5126
|
+
singleEditor.setEditorState(
|
|
5127
|
+
editorState.json,
|
|
5128
|
+
editorState.title,
|
|
5129
|
+
editorState.showTitle,
|
|
5130
|
+
editorState.selection,
|
|
5131
|
+
);
|
|
5132
|
+
}
|
|
5133
|
+
if (editorData) {
|
|
5134
|
+
if (remainingFormat === "link" && editorData.url) {
|
|
5135
|
+
singleEditor._url = editorData.url;
|
|
5136
|
+
} else if (remainingFormat === "quote") {
|
|
5137
|
+
if (editorData.quoteText)
|
|
5138
|
+
singleEditor._quoteText = editorData.quoteText;
|
|
5139
|
+
if (editorData.quoteAuthor)
|
|
5140
|
+
singleEditor._quoteAuthor = editorData.quoteAuthor;
|
|
5141
|
+
}
|
|
5142
|
+
if (bodyJson) {
|
|
5143
|
+
singleEditor._bodyJson = bodyJson;
|
|
5144
|
+
}
|
|
5145
|
+
}
|
|
5146
|
+
// Read attachment state from the old editor NOW so we capture any
|
|
5147
|
+
// mediaIds set by uploads that completed during the render cycle.
|
|
5148
|
+
const latestAttachments = remainingEditor?._attachments ?? [];
|
|
5149
|
+
const latestAttachmentOrder = remainingEditor?._attachmentOrder ?? [];
|
|
5150
|
+
const latestAttachedTexts = remainingEditor?._attachedTexts ?? [];
|
|
5151
|
+
if (latestAttachments.length > 0) {
|
|
5152
|
+
singleEditor._attachments = [...latestAttachments];
|
|
5153
|
+
singleEditor._attachmentOrder = [...latestAttachmentOrder];
|
|
5154
|
+
}
|
|
5155
|
+
if (latestAttachedTexts.length > 0) {
|
|
5156
|
+
singleEditor._attachedTexts = [...latestAttachedTexts];
|
|
5157
|
+
}
|
|
5158
|
+
if (capturedRating > 0) {
|
|
5159
|
+
singleEditor._rating = capturedRating;
|
|
5160
|
+
singleEditor._showRating = capturedShowRating;
|
|
5161
|
+
}
|
|
5162
|
+
singleEditor.focusInput();
|
|
5163
|
+
this.requestUpdate();
|
|
5164
|
+
});
|
|
5165
|
+
} else {
|
|
5166
|
+
this._threadItems = newItems;
|
|
5167
|
+
this._focusedThreadIndex = Math.min(
|
|
5168
|
+
this._focusedThreadIndex,
|
|
5169
|
+
newItems.length - 1,
|
|
5170
|
+
);
|
|
5171
|
+
}
|
|
5172
|
+
}
|
|
5173
|
+
|
|
5174
|
+
private _renderThreadPost(
|
|
5175
|
+
item: ThreadItem,
|
|
5176
|
+
index: number,
|
|
5177
|
+
showRemove: boolean,
|
|
5178
|
+
) {
|
|
5179
|
+
return html`
|
|
5180
|
+
<div
|
|
5181
|
+
class="compose-editor-row compose-thread-post"
|
|
5182
|
+
data-thread-index=${index}
|
|
5183
|
+
@focusin=${() => {
|
|
5184
|
+
this._focusedThreadIndex = index;
|
|
5185
|
+
}}
|
|
5186
|
+
@jant:thread-format-change=${(
|
|
5187
|
+
e: CustomEvent<{ format: ComposeFormat }>,
|
|
5188
|
+
) => {
|
|
5189
|
+
e.stopPropagation();
|
|
5190
|
+
this._threadItems = this._threadItems.map((it, i) =>
|
|
5191
|
+
i === index ? { ...it, format: e.detail.format } : it,
|
|
5192
|
+
);
|
|
5193
|
+
this._format = e.detail.format;
|
|
5194
|
+
}}
|
|
5195
|
+
@jant:thread-remove=${(e: Event) => {
|
|
5196
|
+
e.stopPropagation();
|
|
5197
|
+
this._removeThreadItem(index);
|
|
5198
|
+
}}
|
|
5199
|
+
>
|
|
5200
|
+
<div class="compose-thread-dot"></div>
|
|
5201
|
+
<jant-compose-editor
|
|
5202
|
+
.format=${item.format}
|
|
5203
|
+
.labels=${this.labels}
|
|
5204
|
+
.uploadMaxFileSize=${this.uploadMaxFileSize}
|
|
5205
|
+
.threadItem=${true}
|
|
5206
|
+
.removable=${showRemove}
|
|
5207
|
+
data-thread-id=${item.id}
|
|
5208
|
+
></jant-compose-editor>
|
|
5209
|
+
</div>
|
|
5210
|
+
`;
|
|
5211
|
+
}
|
|
5212
|
+
|
|
5213
|
+
private _renderAddToThreadRow() {
|
|
5214
|
+
const PLUS_ICON = `<circle cx="8" cy="8" r="5.7"/><path d="M8 5.55v4.9M5.55 8h4.9"/>`;
|
|
5215
|
+
const editors = this.querySelectorAll<JantComposeEditor>(
|
|
5216
|
+
"jant-compose-editor",
|
|
5217
|
+
);
|
|
5218
|
+
const lastEditor = editors[editors.length - 1];
|
|
5219
|
+
const lastData = lastEditor?.getData();
|
|
5220
|
+
const lastEmpty =
|
|
5221
|
+
!lastData ||
|
|
5222
|
+
(!lastData.body &&
|
|
5223
|
+
!lastData.title.trim() &&
|
|
5224
|
+
!lastData.url.trim() &&
|
|
5225
|
+
!lastData.quoteText.trim() &&
|
|
5226
|
+
lastData.attachments.length === 0);
|
|
5227
|
+
const atLimit = this._threadItems.length >= MAX_THREAD_POSTS;
|
|
5228
|
+
return html`
|
|
5229
|
+
<div class="compose-thread-add-row">
|
|
5230
|
+
<div class="compose-thread-add-dot"></div>
|
|
5231
|
+
<button
|
|
5232
|
+
type="button"
|
|
5233
|
+
class="compose-thread-add-btn"
|
|
5234
|
+
?disabled=${lastEmpty || atLimit}
|
|
5235
|
+
@click=${() => this._addThreadItem()}
|
|
5236
|
+
>
|
|
5237
|
+
<svg
|
|
5238
|
+
width="14"
|
|
5239
|
+
height="14"
|
|
5240
|
+
viewBox="0 0 16 16"
|
|
5241
|
+
fill="none"
|
|
5242
|
+
stroke="currentColor"
|
|
5243
|
+
stroke-width="1.65"
|
|
5244
|
+
stroke-linecap="round"
|
|
5245
|
+
stroke-linejoin="round"
|
|
5246
|
+
aria-hidden="true"
|
|
5247
|
+
>
|
|
5248
|
+
${unsafeSVG(PLUS_ICON)}
|
|
5249
|
+
</svg>
|
|
5250
|
+
Add to thread
|
|
5251
|
+
</button>
|
|
5252
|
+
</div>
|
|
5253
|
+
`;
|
|
5254
|
+
}
|
|
5255
|
+
|
|
5256
|
+
private _renderThreadComposeLayout() {
|
|
5257
|
+
const isReply = !!(this._replyToId && this._replyToData);
|
|
5258
|
+
const items = this._threadItems;
|
|
5259
|
+
const showRemove = items.length > 1;
|
|
5260
|
+
|
|
5261
|
+
return html`
|
|
5262
|
+
<div
|
|
5263
|
+
class="compose-thread-layout compose-thread-compose-layout"
|
|
5264
|
+
@jant:compose-content-changed=${() => this._scheduleDraftSave()}
|
|
5265
|
+
>
|
|
5266
|
+
${isReply ? this._renderReplyContext() : nothing}
|
|
5267
|
+
${items.map((item, i) => this._renderThreadPost(item, i, showRemove))}
|
|
5268
|
+
${this._renderAddToThreadRow()}
|
|
5269
|
+
</div>
|
|
5270
|
+
`;
|
|
5271
|
+
}
|
|
5272
|
+
|
|
5273
|
+
private _renderAddThreadTrigger() {
|
|
5274
|
+
const PLUS_ICON = `<circle cx="8" cy="8" r="5.7"/><path d="M8 5.55v4.9M5.55 8h4.9"/>`;
|
|
5275
|
+
const disabled = !this._hasContent();
|
|
5276
|
+
return html`
|
|
5277
|
+
<div class="compose-add-thread-trigger">
|
|
5278
|
+
<button
|
|
5279
|
+
type="button"
|
|
5280
|
+
class="compose-add-thread-btn"
|
|
5281
|
+
?disabled=${disabled}
|
|
5282
|
+
@click=${() => this._addThreadItem()}
|
|
5283
|
+
>
|
|
5284
|
+
<svg
|
|
5285
|
+
width="14"
|
|
5286
|
+
height="14"
|
|
5287
|
+
viewBox="0 0 16 16"
|
|
5288
|
+
fill="none"
|
|
5289
|
+
stroke="currentColor"
|
|
5290
|
+
stroke-width="1.65"
|
|
5291
|
+
stroke-linecap="round"
|
|
5292
|
+
stroke-linejoin="round"
|
|
5293
|
+
aria-hidden="true"
|
|
5294
|
+
>
|
|
5295
|
+
${unsafeSVG(PLUS_ICON)}
|
|
5296
|
+
</svg>
|
|
5297
|
+
Add to thread
|
|
5298
|
+
</button>
|
|
3202
5299
|
</div>
|
|
3203
5300
|
`;
|
|
3204
5301
|
}
|
|
3205
5302
|
|
|
3206
5303
|
render() {
|
|
3207
5304
|
const isReply = !!(this._replyToId && this._replyToData);
|
|
5305
|
+
const isThreadMode = this._threadItems.length > 0;
|
|
5306
|
+
const isOpeningEdit = this._openingEdit;
|
|
3208
5307
|
const editor = html`<jant-compose-editor
|
|
3209
5308
|
.format=${this._format}
|
|
3210
5309
|
.labels=${this.labels}
|
|
@@ -3218,27 +5317,43 @@ export class JantComposeDialog extends LitElement {
|
|
|
3218
5317
|
"compose-dialog-inner-page": this.pageMode,
|
|
3219
5318
|
"compose-dialog-inner-suspended": this._addCollectionPanelOpen,
|
|
3220
5319
|
})}
|
|
5320
|
+
tabindex="-1"
|
|
3221
5321
|
aria-hidden=${this._addCollectionPanelOpen ? "true" : "false"}
|
|
3222
5322
|
?inert=${this._addCollectionPanelOpen}
|
|
3223
5323
|
>
|
|
3224
5324
|
${this._renderHeader()}
|
|
3225
|
-
${
|
|
3226
|
-
?
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
${
|
|
3241
|
-
|
|
5325
|
+
${isOpeningEdit
|
|
5326
|
+
? this._renderEditLoadingState()
|
|
5327
|
+
: isThreadMode
|
|
5328
|
+
? this._renderThreadComposeLayout()
|
|
5329
|
+
: isReply
|
|
5330
|
+
? html`
|
|
5331
|
+
<div class="compose-thread-layout">
|
|
5332
|
+
${this._renderReplyContext()}
|
|
5333
|
+
<div class="compose-editor-row">
|
|
5334
|
+
<div class="compose-thread-dot"></div>
|
|
5335
|
+
${editor}
|
|
5336
|
+
</div>
|
|
5337
|
+
</div>
|
|
5338
|
+
`
|
|
5339
|
+
: editor}
|
|
5340
|
+
${isOpeningEdit || isThreadMode || this._editPostId
|
|
5341
|
+
? nothing
|
|
5342
|
+
: this._renderAddThreadTrigger()}
|
|
5343
|
+
${isOpeningEdit
|
|
5344
|
+
? nothing
|
|
5345
|
+
: html`<div
|
|
5346
|
+
class=${classMap({
|
|
5347
|
+
"compose-action-row": true,
|
|
5348
|
+
"compose-action-row-overlay-open":
|
|
5349
|
+
this._showPublishPanel || this._showCollection,
|
|
5350
|
+
})}
|
|
5351
|
+
>
|
|
5352
|
+
${this._renderCollectionSelector()} ${this._renderPublishButton()}
|
|
5353
|
+
</div>`}
|
|
5354
|
+
${this._renderMobilePublishPanel()} ${this._renderAttachedPanel()}
|
|
5355
|
+
${this._renderAltPanel()} ${this._renderDraftsPanel()}
|
|
5356
|
+
${this._renderConfirmPanel()}
|
|
3242
5357
|
</div>
|
|
3243
5358
|
${this._renderAddCollectionPanel()}
|
|
3244
5359
|
`;
|