@jant/core 0.6.6 → 0.6.8
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/bin/commands/uploads/cleanup.js +1 -0
- package/dist/{app-BJkOcMbZ.js → app-9P4rVCe2.js} +396 -117
- package/dist/app-DaxS_Cz-.js +6 -0
- package/dist/client/.vite/manifest.json +3 -3
- package/dist/client/_assets/client-C6peCkkD.css +2 -0
- package/dist/client/_assets/{client-mBvc8KAT.js → client-CXnEhyyv.js} +2 -2
- package/dist/client/_assets/{client-auth-BlfwVtHz.js → client-auth-CSItbyU8.js} +360 -358
- package/dist/{env-CoSe-1y4.js → env-OHRKGcMj.js} +1 -1
- package/dist/{export-DLukCOO3.js → export-Be082J0n.js} +33 -8
- package/dist/{github-api-UD4u_7fa.js → github-api-BgSiE71w.js} +1 -1
- package/dist/{github-app-DeX6Td1O.js → github-app-BbklkFmU.js} +1 -1
- package/dist/{github-sync-BtHY2AST.js → github-sync-D1Cw8mOY.js} +3 -3
- package/dist/{github-sync-BeDecPen.js → github-sync-_kPWM4m9.js} +3 -3
- package/dist/index.js +5 -5
- package/dist/node.js +6 -6
- package/dist/{url-XF0GbKGO.js → url-BMYO-Zlt.js} +42 -2
- package/package.json +1 -1
- package/src/__tests__/bin/uploads-cleanup.test.ts +2 -0
- package/src/client/__tests__/compose-bridge.test.ts +105 -0
- package/src/client/__tests__/hydrate-partial.test.ts +27 -0
- package/src/client/__tests__/json.test.ts +94 -0
- package/src/client/__tests__/note-expand.test.ts +130 -0
- package/src/client/archive-nav.js +2 -1
- package/src/client/audio-player.ts +7 -3
- package/src/client/components/__tests__/compose-format-convert.test.ts +357 -0
- package/src/client/components/__tests__/jant-compose-dialog.test.ts +357 -0
- package/src/client/components/__tests__/jant-compose-editor.test.ts +2 -0
- package/src/client/components/__tests__/jant-compose-fullscreen.test.ts +2 -0
- package/src/client/components/compose-format-convert.ts +255 -0
- package/src/client/components/compose-types.ts +2 -0
- package/src/client/components/jant-collection-directory.ts +1 -0
- package/src/client/components/jant-collection-form.ts +1 -0
- package/src/client/components/jant-command-palette.ts +4 -0
- package/src/client/components/jant-compose-dialog.ts +106 -44
- package/src/client/components/jant-compose-editor.ts +65 -11
- package/src/client/components/jant-compose-fullscreen.ts +3 -0
- package/src/client/components/jant-nav-manager.ts +4 -0
- package/src/client/components/jant-post-menu.ts +3 -0
- package/src/client/components/jant-repo-picker.ts +3 -0
- package/src/client/components/jant-settings-general.ts +3 -0
- package/src/client/compose-bridge.ts +17 -0
- package/src/client/feed-video-player.ts +1 -1
- package/src/client/hydrate-partial.ts +25 -0
- package/src/client/json.ts +56 -2
- package/src/client/multipart-upload.ts +17 -7
- package/src/client/note-expand.ts +63 -0
- package/src/client/upload-session.ts +17 -9
- package/src/client.ts +1 -0
- package/src/i18n/locales/public/en.po +41 -0
- package/src/i18n/locales/public/en.ts +1 -1
- package/src/i18n/locales/public/zh-Hans.po +41 -0
- package/src/i18n/locales/public/zh-Hans.ts +1 -1
- package/src/i18n/locales/public/zh-Hant.po +41 -0
- package/src/i18n/locales/public/zh-Hant.ts +1 -1
- package/src/i18n/locales/settings/en.po +12 -12
- package/src/i18n/locales/settings/en.ts +1 -1
- package/src/i18n/locales/settings/zh-Hans.po +12 -12
- package/src/i18n/locales/settings/zh-Hans.ts +1 -1
- package/src/i18n/locales/settings/zh-Hant.po +12 -12
- package/src/i18n/locales/settings/zh-Hant.ts +1 -1
- package/src/lib/__tests__/markdown-to-tiptap.test.ts +1 -1
- package/src/lib/__tests__/markdown.test.ts +1 -1
- package/src/lib/__tests__/summary.test.ts +87 -0
- package/src/lib/__tests__/timeline.test.ts +48 -1
- package/src/lib/__tests__/tiptap-render.test.ts +4 -4
- package/src/lib/__tests__/url.test.ts +44 -0
- package/src/lib/__tests__/view.test.ts +168 -1
- package/src/lib/navigation.ts +1 -0
- package/src/lib/resolve-config.ts +2 -2
- package/src/lib/summary.ts +42 -3
- package/src/lib/tiptap-render.ts +6 -2
- package/src/lib/upload.ts +2 -2
- package/src/lib/url.ts +41 -0
- package/src/lib/view.ts +102 -40
- package/src/preset.css +7 -1
- package/src/routes/api/internal/__tests__/uploads.test.ts +68 -0
- package/src/routes/api/internal/sites.ts +77 -1
- package/src/routes/api/public/__tests__/archive.test.ts +66 -0
- package/src/routes/api/public/archive.ts +22 -6
- package/src/routes/api/telegram.ts +2 -1
- package/src/routes/dash/custom-urls.tsx +1 -1
- package/src/routes/dash/settings.tsx +8 -5
- package/src/routes/pages/__tests__/archive-params.test.ts +135 -0
- package/src/routes/pages/archive.tsx +116 -20
- package/src/routes/pages/collections.tsx +1 -0
- package/src/services/__tests__/media.test.ts +83 -0
- package/src/services/__tests__/post.test.ts +81 -0
- package/src/services/export-theme/assets/client-site.js +1 -1
- package/src/services/export-theme/styles/main.css +49 -15
- package/src/services/media.ts +31 -1
- package/src/services/post.ts +22 -2
- package/src/services/search.ts +4 -4
- package/src/services/site-admin.ts +121 -0
- package/src/services/upload-session.ts +18 -0
- package/src/styles/tokens.css +1 -1
- package/src/styles/ui.css +163 -34
- package/src/types/config.ts +1 -1
- package/src/types/props.ts +3 -0
- package/src/ui/compose/ComposeDialog.tsx +13 -0
- package/src/ui/dash/settings/AccountMenuContent.tsx +0 -39
- package/src/ui/dash/settings/SettingsDirectory.tsx +26 -1
- package/src/ui/dash/settings/SettingsRootContent.tsx +46 -1
- package/src/ui/dash/settings/__tests__/SettingsRootContent.test.tsx +55 -0
- package/src/ui/feed/NoteCard.tsx +54 -5
- package/src/ui/feed/__tests__/timeline-cards.test.ts +73 -0
- package/src/ui/pages/ArchivePage.tsx +89 -6
- package/src/ui/pages/CollectionsPage.tsx +7 -1
- package/src/ui/pages/__tests__/ArchivePage.test.tsx +37 -0
- package/src/ui/shared/CollectionDirectory.tsx +13 -3
- package/src/ui/shared/CollectionsManager.tsx +3 -0
- package/dist/app-CL2PC1Fl.js +0 -6
- package/dist/client/_assets/client-BMPMuwvV.css +0 -2
|
@@ -8,6 +8,7 @@ import { createMediaService } from "../media.js";
|
|
|
8
8
|
import { createPostService } from "../post.js";
|
|
9
9
|
import type { Database } from "../../db/index.js";
|
|
10
10
|
import { MediaQuotaExceededError } from "../../lib/errors.js";
|
|
11
|
+
import { now } from "../../lib/time.js";
|
|
11
12
|
|
|
12
13
|
interface MockStorageFile {
|
|
13
14
|
body: Uint8Array;
|
|
@@ -961,6 +962,88 @@ describe("MediaService", () => {
|
|
|
961
962
|
});
|
|
962
963
|
});
|
|
963
964
|
|
|
965
|
+
describe("listOrphanedMediaIds", () => {
|
|
966
|
+
it("returns unattached media created before the cutoff", async () => {
|
|
967
|
+
const m1 = await mediaService.create({
|
|
968
|
+
...sampleMedia,
|
|
969
|
+
storageKey: "media/a.jpg",
|
|
970
|
+
});
|
|
971
|
+
const m2 = await mediaService.create({
|
|
972
|
+
...sampleMedia,
|
|
973
|
+
storageKey: "media/b.jpg",
|
|
974
|
+
});
|
|
975
|
+
|
|
976
|
+
const ids = await mediaService.listOrphanedMediaIds({
|
|
977
|
+
before: now() + 1,
|
|
978
|
+
limit: 10,
|
|
979
|
+
});
|
|
980
|
+
|
|
981
|
+
expect(ids).toHaveLength(2);
|
|
982
|
+
expect(ids).toContain(m1.id);
|
|
983
|
+
expect(ids).toContain(m2.id);
|
|
984
|
+
});
|
|
985
|
+
|
|
986
|
+
it("excludes media attached to a post", async () => {
|
|
987
|
+
const post = await postService.create({
|
|
988
|
+
format: "note",
|
|
989
|
+
bodyMarkdown: "p",
|
|
990
|
+
});
|
|
991
|
+
const attached = await mediaService.create({
|
|
992
|
+
...sampleMedia,
|
|
993
|
+
storageKey: "media/a.jpg",
|
|
994
|
+
});
|
|
995
|
+
const orphan = await mediaService.create({
|
|
996
|
+
...sampleMedia,
|
|
997
|
+
storageKey: "media/b.jpg",
|
|
998
|
+
});
|
|
999
|
+
await mediaService.attachToPost(post.id, [attached.id]);
|
|
1000
|
+
|
|
1001
|
+
const ids = await mediaService.listOrphanedMediaIds({
|
|
1002
|
+
before: now() + 1,
|
|
1003
|
+
limit: 10,
|
|
1004
|
+
});
|
|
1005
|
+
|
|
1006
|
+
expect(ids).toEqual([orphan.id]);
|
|
1007
|
+
});
|
|
1008
|
+
|
|
1009
|
+
it("excludes media created at or after the cutoff", async () => {
|
|
1010
|
+
await mediaService.create({
|
|
1011
|
+
...sampleMedia,
|
|
1012
|
+
storageKey: "media/a.jpg",
|
|
1013
|
+
});
|
|
1014
|
+
|
|
1015
|
+
const ids = await mediaService.listOrphanedMediaIds({
|
|
1016
|
+
before: 1,
|
|
1017
|
+
limit: 10,
|
|
1018
|
+
});
|
|
1019
|
+
|
|
1020
|
+
expect(ids).toEqual([]);
|
|
1021
|
+
});
|
|
1022
|
+
|
|
1023
|
+
it("respects the batch limit", async () => {
|
|
1024
|
+
await mediaService.create({ ...sampleMedia, storageKey: "media/a.jpg" });
|
|
1025
|
+
await mediaService.create({ ...sampleMedia, storageKey: "media/b.jpg" });
|
|
1026
|
+
|
|
1027
|
+
const ids = await mediaService.listOrphanedMediaIds({
|
|
1028
|
+
before: now() + 1,
|
|
1029
|
+
limit: 1,
|
|
1030
|
+
});
|
|
1031
|
+
|
|
1032
|
+
expect(ids).toHaveLength(1);
|
|
1033
|
+
});
|
|
1034
|
+
|
|
1035
|
+
it("returns no IDs when the limit is zero", async () => {
|
|
1036
|
+
await mediaService.create({ ...sampleMedia, storageKey: "media/a.jpg" });
|
|
1037
|
+
|
|
1038
|
+
const ids = await mediaService.listOrphanedMediaIds({
|
|
1039
|
+
before: now() + 1,
|
|
1040
|
+
limit: 0,
|
|
1041
|
+
});
|
|
1042
|
+
|
|
1043
|
+
expect(ids).toEqual([]);
|
|
1044
|
+
});
|
|
1045
|
+
});
|
|
1046
|
+
|
|
964
1047
|
describe("validateIds", () => {
|
|
965
1048
|
it("passes for valid IDs", async () => {
|
|
966
1049
|
const m1 = await mediaService.create({
|
|
@@ -842,6 +842,59 @@ describe("PostService", () => {
|
|
|
842
842
|
expect(posts[0]?.bodyText).toBe("root post");
|
|
843
843
|
});
|
|
844
844
|
|
|
845
|
+
it("filters thread roots by reply presence", async () => {
|
|
846
|
+
const threadRoot = await postService.create({
|
|
847
|
+
format: "note",
|
|
848
|
+
bodyMarkdown: "thread root",
|
|
849
|
+
});
|
|
850
|
+
await postService.create({
|
|
851
|
+
format: "note",
|
|
852
|
+
bodyMarkdown: "reply",
|
|
853
|
+
replyToId: threadRoot.id,
|
|
854
|
+
});
|
|
855
|
+
await postService.create({
|
|
856
|
+
format: "note",
|
|
857
|
+
bodyMarkdown: "standalone",
|
|
858
|
+
});
|
|
859
|
+
|
|
860
|
+
const threads = await postService.list({
|
|
861
|
+
excludeReplies: true,
|
|
862
|
+
hasReplies: true,
|
|
863
|
+
});
|
|
864
|
+
expect(threads.map((p) => p.bodyText)).toEqual(["thread root"]);
|
|
865
|
+
|
|
866
|
+
const singles = await postService.list({
|
|
867
|
+
excludeReplies: true,
|
|
868
|
+
hasReplies: false,
|
|
869
|
+
});
|
|
870
|
+
expect(singles.map((p) => p.bodyText)).toEqual(["standalone"]);
|
|
871
|
+
});
|
|
872
|
+
|
|
873
|
+
it("ignores draft replies when filtering by reply presence", async () => {
|
|
874
|
+
const root = await postService.create({
|
|
875
|
+
format: "note",
|
|
876
|
+
bodyMarkdown: "root with draft reply",
|
|
877
|
+
});
|
|
878
|
+
await postService.create({
|
|
879
|
+
format: "note",
|
|
880
|
+
bodyMarkdown: "draft reply",
|
|
881
|
+
replyToId: root.id,
|
|
882
|
+
status: "draft",
|
|
883
|
+
});
|
|
884
|
+
|
|
885
|
+
const threads = await postService.list({
|
|
886
|
+
excludeReplies: true,
|
|
887
|
+
hasReplies: true,
|
|
888
|
+
});
|
|
889
|
+
expect(threads).toHaveLength(0);
|
|
890
|
+
|
|
891
|
+
const singles = await postService.list({
|
|
892
|
+
excludeReplies: true,
|
|
893
|
+
hasReplies: false,
|
|
894
|
+
});
|
|
895
|
+
expect(singles.map((p) => p.bodyText)).toEqual(["root with draft reply"]);
|
|
896
|
+
});
|
|
897
|
+
|
|
845
898
|
it("supports offset pagination", async () => {
|
|
846
899
|
for (let i = 0; i < 5; i++) {
|
|
847
900
|
await postService.create({
|
|
@@ -944,6 +997,34 @@ describe("PostService", () => {
|
|
|
944
997
|
expect(count).toBe(1);
|
|
945
998
|
});
|
|
946
999
|
|
|
1000
|
+
it("counts thread roots by reply presence", async () => {
|
|
1001
|
+
const threadRoot = await postService.create({
|
|
1002
|
+
format: "note",
|
|
1003
|
+
bodyMarkdown: "thread root",
|
|
1004
|
+
});
|
|
1005
|
+
await postService.create({
|
|
1006
|
+
format: "note",
|
|
1007
|
+
bodyMarkdown: "reply",
|
|
1008
|
+
replyToId: threadRoot.id,
|
|
1009
|
+
});
|
|
1010
|
+
await postService.create({
|
|
1011
|
+
format: "note",
|
|
1012
|
+
bodyMarkdown: "standalone",
|
|
1013
|
+
});
|
|
1014
|
+
|
|
1015
|
+
const threadCount = await postService.count({
|
|
1016
|
+
excludeReplies: true,
|
|
1017
|
+
hasReplies: true,
|
|
1018
|
+
});
|
|
1019
|
+
expect(threadCount).toBe(1);
|
|
1020
|
+
|
|
1021
|
+
const singleCount = await postService.count({
|
|
1022
|
+
excludeReplies: true,
|
|
1023
|
+
hasReplies: false,
|
|
1024
|
+
});
|
|
1025
|
+
expect(singleCount).toBe(1);
|
|
1026
|
+
});
|
|
1027
|
+
|
|
947
1028
|
it("can stop counting after a small limit", async () => {
|
|
948
1029
|
const collection = await collectionService.create({
|
|
949
1030
|
slug: "rated",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var e=null,t=0,n=!1,r=null,i=null,a=0;function o(e){return!isFinite(e)||e<0?`0:00`:`${Math.floor(e/60)}:${String(Math.floor(e%60)).padStart(2,`0`)}`}function s(e){return e.querySelector(`audio.media-audio-el`)}function c(e){return e.querySelector(`[data-audio-range]`)}function l(e){return e.querySelector(`[data-audio-waveform]`)}function u(e,t){let n=`${(t*100).toFixed(1)}%`;e.style.background=`linear-gradient(to right, var(--site-text-primary) ${n}, transparent ${n})`}var d=new WeakMap,f=new WeakSet;async function p(e,t){let n=await(await fetch(e)).arrayBuffer(),r=new AudioContext;try{let e=(await r.decodeAudioData(n)).getChannelData(0),i=Math.max(1,Math.floor(e.length/t)),a=Array(t);for(let n=0;n<t;n++){let t=0,r=n*i,o=Math.min(r+i,e.length);for(let n=r;n<o;n++){let r=Math.abs(e[n]);r>t&&(t=r)}a[n]=t}let o=0;for(let e of a)e>o&&(o=e);if(o>0)for(let e=0;e<t;e++)a[e]/=o;return a}finally{await r.close()}}function m(){let e=document.querySelectorAll(`[data-audio-peaks]`),t=[];for(let n of e){let e=n.dataset.audioPeaks;if(!e)continue;let r=n.closest(`.media-audio-card`);if(!(!r||d.has(r)))try{let n=JSON.parse(e);if(!Array.isArray(n))continue;d.set(r,n),r.classList.add(`has-waveform`),t.push(r)}catch{}}t.length>0&&requestAnimationFrame(()=>{for(let e of t)h(e,0)})}async function ee(e){if(d.has(e)||f.has(e))return;f.add(e);let t=e.querySelector(`audio.media-audio-el source`),n=l(e);if(!t?.src||!n)return;let r=n.getBoundingClientRect().width,i=Math.max(20,Math.floor(r/3));try{let n=await p(t.src,i);d.set(e,n),e.classList.add(`has-waveform`);let r=s(e),a=r?.duration??0;h(e,r&&isFinite(a)&&a>0?r.currentTime/a:0)}catch{}}function h(e,t){let n=d.get(e),r=l(e);if(!n||!r)return;let i=window.devicePixelRatio||1,a=r.getBoundingClientRect(),o=Math.round(a.width*i),s=Math.round(a.height*i);if(o===0||s===0)return;(r.width!==o||r.height!==s)&&(r.width=o,r.height=s);let c=r.getContext(`2d`);if(!c)return;c.clearRect(0,0,o,s);let u=n.length,f=o/u,p=Math.max(1,Math.round(f*.6)),m=Math.round(2*i),ee=s*.85,h=getComputedStyle(r).getPropertyValue(`--site-text-primary`).trim()||`#000`;for(let e=0;e<u;e++){let r=Math.round(e*f+(f-p)/2),a=Math.max(m,Math.round(n[e]*ee)),o=Math.round((s-a)/2);c.globalAlpha=(e+.5)/u<=t?.9:.2,c.fillStyle=h;let l=Math.min(p/2,i);c.beginPath(),c.roundRect(r,o,p,a,l),c.fill()}c.globalAlpha=1}function te(e,t){let{currentTime:r,duration:i}=t,a=isFinite(i)&&i>0,s=a?r/i:0;if(!n){let t=c(e);t&&a&&(t.value=String(Math.round(s*1e3)),u(t,s)),d.has(e)&&h(e,s);let n=e.querySelector(`[data-audio-time]`);n&&(n.textContent=a?`${o(r)} / ${o(i)}`:o(r))}}function ne(){if(!e)return;let n=s(e);!n||n.paused||(te(e,n),t=requestAnimationFrame(ne))}function re(){if(e){let t=s(e);t&&!t.paused&&t.pause(),e.classList.remove(`is-playing`),e=null}cancelAnimationFrame(t)}async function ie(n){let r=s(n);if(r)if(e&&e!==n&&re(),r.paused){e=n,n.classList.add(`is-playing`);try{await r.play()}catch{n.classList.remove(`is-playing`),e=null;return}t=requestAnimationFrame(ne),ee(n)}else r.pause(),n.classList.remove(`is-playing`),cancelAnimationFrame(t),e=null}function ae(e){let t=e.closest(`.media-audio-card`);if(!t)return;let n=s(t);if(!n)return;let r=Number(e.value)/1e3,i=n.duration;isFinite(i)&&i>0&&(n.currentTime=r*i)}function oe(e,t){let n=e.getBoundingClientRect();a=Math.max(0,Math.min(1,(t.clientX-n.left)/n.width));let r=e.closest(`.media-audio-card`);if(!r)return;h(r,a);let i=s(r),c=r.querySelector(`[data-audio-time]`);if(i&&c){let e=i.duration;isFinite(e)&&e>0&&(c.textContent=`${o(a*e)} / ${o(e)}`)}}async function se(n){let r=n.closest(`.media-audio-card`);if(!r)return;let i=s(r);if(i)if(i.paused){e&&e!==r&&re(),e=r,r.classList.add(`is-playing`);try{await i.play()}catch{r.classList.remove(`is-playing`),e=null;return}let n=i.duration;isFinite(n)&&n>0&&(i.currentTime=a*n),t=requestAnimationFrame(ne),ee(r)}else{let e=i.duration;isFinite(e)&&e>0&&(i.currentTime=a*e)}}document.addEventListener(`pointerdown`,e=>{let t=e.target;t.matches(`[data-audio-range]`)?(n=!0,r=t):t.matches(`[data-audio-waveform]`)&&(n=!0,i=t,oe(t,e))},!0),document.addEventListener(`pointermove`,e=>{n&&i&&oe(i,e)},!0),document.addEventListener(`pointerup`,()=>{n&&(r?(ae(r),r=null):i&&=(se(i),null)),n=!1},!0),document.addEventListener(`pointercancel`,()=>{r=null,i=null,n=!1},!0),document.addEventListener(`click`,e=>{let t=e.target.closest(`[data-audio-play]`);if(!t)return;e.preventDefault();let n=t.closest(`.media-audio-card`);n&&ie(n)}),document.addEventListener(`input`,e=>{let t=e.target;if(!t.matches(`[data-audio-range]`))return;let n=t.closest(`.media-audio-card`);if(!n)return;let r=s(n);if(!r)return;let i=Number(t.value)/1e3;u(t,i);let a=r.duration,c=n.querySelector(`[data-audio-time]`);c&&isFinite(a)&&a>0&&(c.textContent=`${o(i*a)} / ${o(a)}`)},!0),document.addEventListener(`ended`,n=>{let r=n.target;if(!r.closest)return;let i=r.closest(`.media-audio-card`);if(!i)return;i.classList.remove(`is-playing`),cancelAnimationFrame(t),e=null;let a=c(i);a&&(a.value=`0`,u(a,0)),d.has(i)&&h(i,0);let o=i.querySelector(`[data-audio-time]`);o&&(o.textContent=`0:00`)},!0),document.readyState===`loading`?document.addEventListener(`DOMContentLoaded`,m):m();var g=`jant:media-lightbox-toggle`,ce=`75% 0px`,le=.6,_=.25,ue=160,v=new Set,de=new WeakMap,fe=new WeakSet,y=null,b=null,x=null,pe=!1,S=null,C=null,w=null;function me(e){return e.length===0?null:[...e].sort((e,t)=>t.visibleArea===e.visibleArea?e.centerDistance===t.centerDistance?t.intersectionRatio-e.intersectionRatio:e.centerDistance-t.centerDistance:t.visibleArea-e.visibleArea)[0]??null}function he(){return{x:(globalThis.innerWidth||document.documentElement.clientWidth||0)/2,y:(globalThis.innerHeight||document.documentElement.clientHeight||0)/2}}function ge(e){return de.get(e)}function _e(e){return e.closest(`.media-video-wrap`)?.querySelector(`[data-feed-video-mute-toggle]`)??null}function T(e){let t=_e(e);if(!t)return;let n=x!==e||e.muted;t.dataset.muted=n?`true`:`false`,t.setAttribute(`aria-label`,n?`Play with sound`:`Mute video`)}function ve(){for(let e of v)T(e)}function ye(e){if(fe.has(e))return;let t=e.dataset.videoSrc;t&&(e.getAttribute(`src`)!==t&&(e.src=t),e.load(),fe.add(e))}function E(e){e?.pause()}function be(e){ye(e),e.muted=x!==e,e.playsInline=!0,e.loop=!0,T(e),e.play().catch(()=>{})}function xe(){for(let e of v)e.isConnected||(C?.unobserve(e),w?.unobserve(e),v.delete(e),e===y&&(y=null),e===b&&(b=null),e===x&&(x=null))}function Se(){if(S=null,xe(),document.hidden||pe){E(y);return}let e=[];for(let t of v){let n=ge(t);n&&(n.intersectionRatio<le||e.push({video:t,...n}))}let t=null;if(b?.isConnected){let e=ge(b);e&&e.intersectionRatio>_?t=b:(!e||e.intersectionRatio<=_)&&(b=null)}if(t||=me(e)?.video??null,!t){let e=y?ge(y):void 0;if(y&&e&&e.intersectionRatio>_)return;E(y),y=null;return}t!==y&&(E(y),y=t),be(t);for(let e of v)e!==t&&(E(e),T(e))}function D(){S!==null&&globalThis.clearTimeout(S),S=globalThis.setTimeout(Se,ue)}function Ce(e){let t=he();for(let n of e){let e=n.target,r=n.boundingClientRect,i=r.left+r.width/2,a=r.top+r.height/2,o=n.intersectionRect.width*n.intersectionRect.height,s=Math.hypot(i-t.x,a-t.y);de.set(e,{intersectionRatio:n.intersectionRatio,visibleArea:o,centerDistance:s})}D()}function we(e){for(let t of e)t.isIntersecting&&ye(t.target)}function Te(){C&&w||globalThis.IntersectionObserver!==void 0&&(C=new globalThis.IntersectionObserver(Ce,{threshold:[0,_,le,1]}),w=new globalThis.IntersectionObserver(we,{rootMargin:ce,threshold:0}))}function Ee(e){v.has(e)||(Te(),!(!C||!w)&&(v.add(e),C.observe(e),w.observe(e),_e(e)?.addEventListener(`click`,De),T(e)))}function De(e){e.preventDefault(),e.stopPropagation();let t=e.currentTarget.closest(`.media-video-wrap`)?.querySelector(`[data-feed-short-video]`);t&&(b=t,x!==t||t.muted?(x&&x!==t&&(x.muted=!0),x=t,y!==t&&(E(y),y=t),be(t)):(x=null,t.muted=!0,T(t)),ve(),D())}function Oe(e=document){let t=e.querySelectorAll(`[data-feed-short-video]`);for(let e of t)Ee(e);D()}document.addEventListener(g,e=>{pe=e.detail?.open===!0,pe&&E(y),D()}),document.addEventListener(`visibilitychange`,()=>{document.hidden&&E(y),D()}),document.readyState===`loading`?document.addEventListener(`DOMContentLoaded`,()=>Oe(),{once:!0}):queueMicrotask(()=>Oe());var ke=4;function Ae(e){return e.querySelector(`[data-post-media]`)}function je(e){let t=Ae(e);if(!t)return;let{scrollLeft:n,scrollWidth:r,clientWidth:i}=t;e.classList.toggle(`can-scroll-start`,n>ke),e.classList.toggle(`can-scroll-end`,n+i<r-ke)}function Me(e){return Math.max(160,Math.round(e.clientWidth*.85))}function O(e,t){e.scrollBy({left:t*Me(e),behavior:`smooth`})}function k(e){if(e.dataset.scrollHintReady===`1`)return;let t=Ae(e);t&&(e.dataset.scrollHintReady=`1`,je(e),t.addEventListener(`scroll`,()=>je(e),{passive:!0}),e.querySelector(`.media-gallery-nav-prev`)?.addEventListener(`click`,()=>O(t,-1)),e.querySelector(`.media-gallery-nav-next`)?.addEventListener(`click`,()=>O(t,1)),t.addEventListener(`keydown`,e=>{if(e.target===t)switch(e.key){case`ArrowRight`:e.preventDefault(),O(t,1);break;case`ArrowLeft`:e.preventDefault(),O(t,-1);break;case`Home`:e.preventDefault(),t.scrollTo({left:0,behavior:`smooth`});break;case`End`:e.preventDefault(),t.scrollTo({left:t.scrollWidth,behavior:`smooth`});break}}))}function Ne(){document.querySelectorAll(`.media-gallery-scroll-wrap`).forEach(k)}var Pe=new globalThis.MutationObserver(e=>{for(let t of e)for(let e of t.addedNodes)e instanceof HTMLElement&&(e.matches(`.media-gallery-scroll-wrap`)&&k(e),e.querySelectorAll(`.media-gallery-scroll-wrap`).forEach(k))});document.readyState===`loading`?document.addEventListener(`DOMContentLoaded`,()=>{Ne(),Pe.observe(document.body,{childList:!0,subtree:!0})}):(Ne(),Pe.observe(document.body,{childList:!0,subtree:!0}));function Fe(e){let t=e.querySelector(`.site-header-more-btn`),n=e.querySelector(`.site-header-more-popover`);if(!t||!n||t.dataset.moreInitialized===`true`)return;t.dataset.moreInitialized=`true`;function r(){n.setAttribute(`aria-hidden`,`false`),t.setAttribute(`aria-expanded`,`true`),document.dispatchEvent(new CustomEvent(`basecoat:popover`,{detail:{source:t.parentElement}}))}function i(e=!1){n.setAttribute(`aria-hidden`,`true`),t.setAttribute(`aria-expanded`,`false`),e&&t.focus()}t.addEventListener(`click`,e=>{e.preventDefault(),e.stopPropagation(),t.getAttribute(`aria-expanded`)===`true`?i():r()}),document.addEventListener(`click`,e=>{e.target instanceof Node&&(t.parentElement?.contains(e.target)||i())}),document.addEventListener(`keydown`,e=>{e.key===`Escape`&&n.getAttribute(`aria-hidden`)===`false`&&i(!0)}),document.addEventListener(`basecoat:popover`,e=>{e.detail?.source!==t.parentElement&&i()})}var Ie=`jant:nav-fresh-visits`;function Le(){let e=document.createElement(`span`);return e.className=`site-header-link-fresh`,e.setAttribute(`aria-hidden`,`true`),e.textContent=`*`,e}function Re(e){try{let t=JSON.parse(localStorage.getItem(Ie)||`{}`),n=location.pathname,r=e.querySelectorAll(`[data-fresh-at]`);for(let e of r){let r=new URL(e.href).pathname,i=parseInt(e.dataset.freshAt,10);if(r===n)t[r]=Math.floor(Date.now()/1e3);else{let n=t[r];if(!n||n<i){let t=e.querySelector(`svg`);e.insertBefore(Le(),t)}}}localStorage.setItem(Ie,JSON.stringify(t))}catch{}}function ze(e=document){let t=e.querySelector(`.site-header-hamburger`),n=e.querySelector(`#site-nav-drawer`),r=e.querySelector(`.site-nav-drawer-backdrop`),i=n?.querySelector(`.site-nav-drawer-close`);if(Re(e),Fe(e),!t||!n||!r||t.dataset.drawerInitialized===`true`)return;t.dataset.drawerInitialized=`true`;function a(){n.setAttribute(`aria-hidden`,`false`),n.removeAttribute(`inert`),r.setAttribute(`aria-hidden`,`false`),t.setAttribute(`aria-expanded`,`true`),document.documentElement.classList.add(`drawer-open`);let e=n.querySelector(`.site-nav-drawer-close`)??n.querySelector(`a[href], button`);e&&e.focus()}function o(e=!0){n.setAttribute(`aria-hidden`,`true`),r.setAttribute(`aria-hidden`,`true`),t.setAttribute(`aria-expanded`,`false`),document.documentElement.classList.remove(`drawer-open`),n.addEventListener(`transitionend`,()=>{n.getAttribute(`aria-hidden`)===`true`&&n.setAttribute(`inert`,``)},{once:!0}),e&&t.focus()}t.addEventListener(`click`,()=>{t.getAttribute(`aria-expanded`)===`true`?o():a()}),i?.addEventListener(`click`,()=>o()),r.addEventListener(`click`,()=>o()),n.addEventListener(`click`,e=>{e.target instanceof Element&&e.target.closest(`a[href]`)&&o(!1)}),n.addEventListener(`keydown`,e=>{e.key===`Escape`&&(e.preventDefault(),o())})}ze();var A=globalThis,j=A.ShadowRoot&&(A.ShadyCSS===void 0||A.ShadyCSS.nativeShadow)&&`adoptedStyleSheets`in Document.prototype&&`replace`in CSSStyleSheet.prototype,Be=Symbol(),Ve=new WeakMap,He=class{constructor(e,t,n){if(this._$cssResult$=!0,n!==Be)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=e,this.t=t}get styleSheet(){let e=this.o,t=this.t;if(j&&e===void 0){let n=t!==void 0&&t.length===1;n&&(e=Ve.get(t)),e===void 0&&((this.o=e=new CSSStyleSheet).replaceSync(this.cssText),n&&Ve.set(t,e))}return e}toString(){return this.cssText}},Ue=e=>new He(typeof e==`string`?e:e+``,void 0,Be),We=(e,t)=>{if(j)e.adoptedStyleSheets=t.map(e=>e instanceof CSSStyleSheet?e:e.styleSheet);else for(let n of t){let t=document.createElement(`style`),r=A.litNonce;r!==void 0&&t.setAttribute(`nonce`,r),t.textContent=n.cssText,e.appendChild(t)}},Ge=j?e=>e:e=>e instanceof CSSStyleSheet?(e=>{let t=``;for(let n of e.cssRules)t+=n.cssText;return Ue(t)})(e):e,{is:Ke,defineProperty:qe,getOwnPropertyDescriptor:Je,getOwnPropertyNames:Ye,getOwnPropertySymbols:Xe,getPrototypeOf:Ze}=Object,M=globalThis,Qe=M.trustedTypes,$e=Qe?Qe.emptyScript:``,et=M.reactiveElementPolyfillSupport,N=(e,t)=>e,P={toAttribute(e,t){switch(t){case Boolean:e=e?$e:null;break;case Object:case Array:e=e==null?e:JSON.stringify(e)}return e},fromAttribute(e,t){let n=e;switch(t){case Boolean:n=e!==null;break;case Number:n=e===null?null:Number(e);break;case Object:case Array:try{n=JSON.parse(e)}catch{n=null}}return n}},tt=(e,t)=>!Ke(e,t),nt={attribute:!0,type:String,converter:P,reflect:!1,useDefault:!1,hasChanged:tt};Symbol.metadata??=Symbol(`metadata`),M.litPropertyMetadata??=new WeakMap;var F=class extends HTMLElement{static addInitializer(e){this._$Ei(),(this.l??=[]).push(e)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(e,t=nt){if(t.state&&(t.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(e)&&((t=Object.create(t)).wrapped=!0),this.elementProperties.set(e,t),!t.noAccessor){let n=Symbol(),r=this.getPropertyDescriptor(e,n,t);r!==void 0&&qe(this.prototype,e,r)}}static getPropertyDescriptor(e,t,n){let{get:r,set:i}=Je(this.prototype,e)??{get(){return this[t]},set(e){this[t]=e}};return{get:r,set(t){let a=r?.call(this);i?.call(this,t),this.requestUpdate(e,a,n)},configurable:!0,enumerable:!0}}static getPropertyOptions(e){return this.elementProperties.get(e)??nt}static _$Ei(){if(this.hasOwnProperty(N(`elementProperties`)))return;let e=Ze(this);e.finalize(),e.l!==void 0&&(this.l=[...e.l]),this.elementProperties=new Map(e.elementProperties)}static finalize(){if(this.hasOwnProperty(N(`finalized`)))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(N(`properties`))){let e=this.properties,t=[...Ye(e),...Xe(e)];for(let n of t)this.createProperty(n,e[n])}let e=this[Symbol.metadata];if(e!==null){let t=litPropertyMetadata.get(e);if(t!==void 0)for(let[e,n]of t)this.elementProperties.set(e,n)}this._$Eh=new Map;for(let[e,t]of this.elementProperties){let n=this._$Eu(e,t);n!==void 0&&this._$Eh.set(n,e)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(e){let t=[];if(Array.isArray(e)){let n=new Set(e.flat(1/0).reverse());for(let e of n)t.unshift(Ge(e))}else e!==void 0&&t.push(Ge(e));return t}static _$Eu(e,t){let n=t.attribute;return!1===n?void 0:typeof n==`string`?n:typeof e==`string`?e.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(e=>this.enableUpdating=e),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(e=>e(this))}addController(e){(this._$EO??=new Set).add(e),this.renderRoot!==void 0&&this.isConnected&&e.hostConnected?.()}removeController(e){this._$EO?.delete(e)}_$E_(){let e=new Map,t=this.constructor.elementProperties;for(let n of t.keys())this.hasOwnProperty(n)&&(e.set(n,this[n]),delete this[n]);e.size>0&&(this._$Ep=e)}createRenderRoot(){let e=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return We(e,this.constructor.elementStyles),e}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(e=>e.hostConnected?.())}enableUpdating(e){}disconnectedCallback(){this._$EO?.forEach(e=>e.hostDisconnected?.())}attributeChangedCallback(e,t,n){this._$AK(e,n)}_$ET(e,t){let n=this.constructor.elementProperties.get(e),r=this.constructor._$Eu(e,n);if(r!==void 0&&!0===n.reflect){let i=(n.converter?.toAttribute===void 0?P:n.converter).toAttribute(t,n.type);this._$Em=e,i==null?this.removeAttribute(r):this.setAttribute(r,i),this._$Em=null}}_$AK(e,t){let n=this.constructor,r=n._$Eh.get(e);if(r!==void 0&&this._$Em!==r){let e=n.getPropertyOptions(r),i=typeof e.converter==`function`?{fromAttribute:e.converter}:e.converter?.fromAttribute===void 0?P:e.converter;this._$Em=r;let a=i.fromAttribute(t,e.type);this[r]=a??this._$Ej?.get(r)??a,this._$Em=null}}requestUpdate(e,t,n,r=!1,i){if(e!==void 0){let a=this.constructor;if(!1===r&&(i=this[e]),n??=a.getPropertyOptions(e),!((n.hasChanged??tt)(i,t)||n.useDefault&&n.reflect&&i===this._$Ej?.get(e)&&!this.hasAttribute(a._$Eu(e,n))))return;this.C(e,t,n)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(e,t,{useDefault:n,reflect:r,wrapped:i},a){n&&!(this._$Ej??=new Map).has(e)&&(this._$Ej.set(e,a??t??this[e]),!0!==i||a!==void 0)||(this._$AL.has(e)||(this.hasUpdated||n||(t=void 0),this._$AL.set(e,t)),!0===r&&this._$Em!==e&&(this._$Eq??=new Set).add(e))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(e){Promise.reject(e)}let e=this.scheduleUpdate();return e!=null&&await e,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(let[e,t]of this._$Ep)this[e]=t;this._$Ep=void 0}let e=this.constructor.elementProperties;if(e.size>0)for(let[t,n]of e){let{wrapped:e}=n,r=this[t];!0!==e||this._$AL.has(t)||r===void 0||this.C(t,void 0,n,r)}}let e=!1,t=this._$AL;try{e=this.shouldUpdate(t),e?(this.willUpdate(t),this._$EO?.forEach(e=>e.hostUpdate?.()),this.update(t)):this._$EM()}catch(t){throw e=!1,this._$EM(),t}e&&this._$AE(t)}willUpdate(e){}_$AE(e){this._$EO?.forEach(e=>e.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(e)),this.updated(e)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(e){return!0}update(e){this._$Eq&&=this._$Eq.forEach(e=>this._$ET(e,this[e])),this._$EM()}updated(e){}firstUpdated(e){}};F.elementStyles=[],F.shadowRootOptions={mode:`open`},F[N(`elementProperties`)]=new Map,F[N(`finalized`)]=new Map,et?.({ReactiveElement:F}),(M.reactiveElementVersions??=[]).push(`2.1.2`);var I=globalThis,rt=e=>e,L=I.trustedTypes,it=L?L.createPolicy(`lit-html`,{createHTML:e=>e}):void 0,at=`$lit$`,R=`lit$${Math.random().toFixed(9).slice(2)}$`,ot=`?`+R,st=`<${ot}>`,z=document,B=()=>z.createComment(``),V=e=>e===null||typeof e!=`object`&&typeof e!=`function`,ct=Array.isArray,lt=e=>ct(e)||typeof e?.[Symbol.iterator]==`function`,ut=`[
|
|
1
|
+
var e=null,t=0,n=!1,r=null,i=null,a=0;function o(e){return!isFinite(e)||e<0?`0:00`:`${Math.floor(e/60)}:${String(Math.floor(e%60)).padStart(2,`0`)}`}function s(e){return e.querySelector(`audio.media-audio-el`)}function c(e){return e.querySelector(`[data-audio-range]`)}function l(e){return e.querySelector(`[data-audio-waveform]`)}function u(e,t){let n=`${(t*100).toFixed(1)}%`;e.style.background=`linear-gradient(to right, var(--site-text-primary) ${n}, transparent ${n})`}var d=new WeakMap,f=new WeakSet;async function p(e,t){let n=await(await fetch(e)).arrayBuffer(),r=new AudioContext;try{let e=(await r.decodeAudioData(n)).getChannelData(0),i=Math.max(1,Math.floor(e.length/t)),a=Array(t);for(let n=0;n<t;n++){let t=0,r=n*i,o=Math.min(r+i,e.length);for(let n=r;n<o;n++){let r=Math.abs(e[n]);r>t&&(t=r)}a[n]=t}let o=0;for(let e of a)e>o&&(o=e);if(o>0)for(let e=0;e<t;e++)a[e]/=o;return a}finally{await r.close()}}function m(e=document){let t=e.querySelectorAll(`[data-audio-peaks]`),n=[];for(let e of t){let t=e.dataset.audioPeaks;if(!t)continue;let r=e.closest(`.media-audio-card`);if(!(!r||d.has(r)))try{let e=JSON.parse(t);if(!Array.isArray(e))continue;d.set(r,e),r.classList.add(`has-waveform`),n.push(r)}catch{}}n.length>0&&requestAnimationFrame(()=>{for(let e of n)h(e,0)})}async function ee(e){if(d.has(e)||f.has(e))return;f.add(e);let t=e.querySelector(`audio.media-audio-el source`),n=l(e);if(!t?.src||!n)return;let r=n.getBoundingClientRect().width,i=Math.max(20,Math.floor(r/3));try{let n=await p(t.src,i);d.set(e,n),e.classList.add(`has-waveform`);let r=s(e),a=r?.duration??0;h(e,r&&isFinite(a)&&a>0?r.currentTime/a:0)}catch{}}function h(e,t){let n=d.get(e),r=l(e);if(!n||!r)return;let i=window.devicePixelRatio||1,a=r.getBoundingClientRect(),o=Math.round(a.width*i),s=Math.round(a.height*i);if(o===0||s===0)return;(r.width!==o||r.height!==s)&&(r.width=o,r.height=s);let c=r.getContext(`2d`);if(!c)return;c.clearRect(0,0,o,s);let u=n.length,f=o/u,p=Math.max(1,Math.round(f*.6)),m=Math.round(2*i),ee=s*.85,h=getComputedStyle(r).getPropertyValue(`--site-text-primary`).trim()||`#000`;for(let e=0;e<u;e++){let r=Math.round(e*f+(f-p)/2),a=Math.max(m,Math.round(n[e]*ee)),o=Math.round((s-a)/2);c.globalAlpha=(e+.5)/u<=t?.9:.2,c.fillStyle=h;let l=Math.min(p/2,i);c.beginPath(),c.roundRect(r,o,p,a,l),c.fill()}c.globalAlpha=1}function te(e,t){let{currentTime:r,duration:i}=t,a=isFinite(i)&&i>0,s=a?r/i:0;if(!n){let t=c(e);t&&a&&(t.value=String(Math.round(s*1e3)),u(t,s)),d.has(e)&&h(e,s);let n=e.querySelector(`[data-audio-time]`);n&&(n.textContent=a?`${o(r)} / ${o(i)}`:o(r))}}function ne(){if(!e)return;let n=s(e);!n||n.paused||(te(e,n),t=requestAnimationFrame(ne))}function re(){if(e){let t=s(e);t&&!t.paused&&t.pause(),e.classList.remove(`is-playing`),e=null}cancelAnimationFrame(t)}async function ie(n){let r=s(n);if(r)if(e&&e!==n&&re(),r.paused){e=n,n.classList.add(`is-playing`);try{await r.play()}catch{n.classList.remove(`is-playing`),e=null;return}t=requestAnimationFrame(ne),ee(n)}else r.pause(),n.classList.remove(`is-playing`),cancelAnimationFrame(t),e=null}function ae(e){let t=e.closest(`.media-audio-card`);if(!t)return;let n=s(t);if(!n)return;let r=Number(e.value)/1e3,i=n.duration;isFinite(i)&&i>0&&(n.currentTime=r*i)}function oe(e,t){let n=e.getBoundingClientRect();a=Math.max(0,Math.min(1,(t.clientX-n.left)/n.width));let r=e.closest(`.media-audio-card`);if(!r)return;h(r,a);let i=s(r),c=r.querySelector(`[data-audio-time]`);if(i&&c){let e=i.duration;isFinite(e)&&e>0&&(c.textContent=`${o(a*e)} / ${o(e)}`)}}async function se(n){let r=n.closest(`.media-audio-card`);if(!r)return;let i=s(r);if(i)if(i.paused){e&&e!==r&&re(),e=r,r.classList.add(`is-playing`);try{await i.play()}catch{r.classList.remove(`is-playing`),e=null;return}let n=i.duration;isFinite(n)&&n>0&&(i.currentTime=a*n),t=requestAnimationFrame(ne),ee(r)}else{let e=i.duration;isFinite(e)&&e>0&&(i.currentTime=a*e)}}document.addEventListener(`pointerdown`,e=>{let t=e.target;t.matches(`[data-audio-range]`)?(n=!0,r=t):t.matches(`[data-audio-waveform]`)&&(n=!0,i=t,oe(t,e))},!0),document.addEventListener(`pointermove`,e=>{n&&i&&oe(i,e)},!0),document.addEventListener(`pointerup`,()=>{n&&(r?(ae(r),r=null):i&&=(se(i),null)),n=!1},!0),document.addEventListener(`pointercancel`,()=>{r=null,i=null,n=!1},!0),document.addEventListener(`click`,e=>{let t=e.target.closest(`[data-audio-play]`);if(!t)return;e.preventDefault();let n=t.closest(`.media-audio-card`);n&&ie(n)}),document.addEventListener(`input`,e=>{let t=e.target;if(!t.matches(`[data-audio-range]`))return;let n=t.closest(`.media-audio-card`);if(!n)return;let r=s(n);if(!r)return;let i=Number(t.value)/1e3;u(t,i);let a=r.duration,c=n.querySelector(`[data-audio-time]`);c&&isFinite(a)&&a>0&&(c.textContent=`${o(i*a)} / ${o(a)}`)},!0),document.addEventListener(`ended`,n=>{let r=n.target;if(!r.closest)return;let i=r.closest(`.media-audio-card`);if(!i)return;i.classList.remove(`is-playing`),cancelAnimationFrame(t),e=null;let a=c(i);a&&(a.value=`0`,u(a,0)),d.has(i)&&h(i,0);let o=i.querySelector(`[data-audio-time]`);o&&(o.textContent=`0:00`)},!0),document.readyState===`loading`?document.addEventListener(`DOMContentLoaded`,()=>m()):m();var g=`jant:media-lightbox-toggle`,ce=`75% 0px`,le=.6,_=.25,ue=160,v=new Set,de=new WeakMap,fe=new WeakSet,y=null,b=null,x=null,pe=!1,S=null,C=null,w=null;function me(e){return e.length===0?null:[...e].sort((e,t)=>t.visibleArea===e.visibleArea?e.centerDistance===t.centerDistance?t.intersectionRatio-e.intersectionRatio:e.centerDistance-t.centerDistance:t.visibleArea-e.visibleArea)[0]??null}function he(){return{x:(globalThis.innerWidth||document.documentElement.clientWidth||0)/2,y:(globalThis.innerHeight||document.documentElement.clientHeight||0)/2}}function ge(e){return de.get(e)}function _e(e){return e.closest(`.media-video-wrap`)?.querySelector(`[data-feed-video-mute-toggle]`)??null}function T(e){let t=_e(e);if(!t)return;let n=x!==e||e.muted;t.dataset.muted=n?`true`:`false`,t.setAttribute(`aria-label`,n?`Play with sound`:`Mute video`)}function ve(){for(let e of v)T(e)}function ye(e){if(fe.has(e))return;let t=e.dataset.videoSrc;t&&(e.getAttribute(`src`)!==t&&(e.src=t),e.load(),fe.add(e))}function E(e){e?.pause()}function be(e){ye(e),e.muted=x!==e,e.playsInline=!0,e.loop=!0,T(e),e.play().catch(()=>{})}function xe(){for(let e of v)e.isConnected||(C?.unobserve(e),w?.unobserve(e),v.delete(e),e===y&&(y=null),e===b&&(b=null),e===x&&(x=null))}function Se(){if(S=null,xe(),document.hidden||pe){E(y);return}let e=[];for(let t of v){let n=ge(t);n&&(n.intersectionRatio<le||e.push({video:t,...n}))}let t=null;if(b?.isConnected){let e=ge(b);e&&e.intersectionRatio>_?t=b:(!e||e.intersectionRatio<=_)&&(b=null)}if(t||=me(e)?.video??null,!t){let e=y?ge(y):void 0;if(y&&e&&e.intersectionRatio>_)return;E(y),y=null;return}t!==y&&(E(y),y=t),be(t);for(let e of v)e!==t&&(E(e),T(e))}function D(){S!==null&&globalThis.clearTimeout(S),S=globalThis.setTimeout(Se,ue)}function Ce(e){let t=he();for(let n of e){let e=n.target,r=n.boundingClientRect,i=r.left+r.width/2,a=r.top+r.height/2,o=n.intersectionRect.width*n.intersectionRect.height,s=Math.hypot(i-t.x,a-t.y);de.set(e,{intersectionRatio:n.intersectionRatio,visibleArea:o,centerDistance:s})}D()}function we(e){for(let t of e)t.isIntersecting&&ye(t.target)}function Te(){C&&w||globalThis.IntersectionObserver!==void 0&&(C=new globalThis.IntersectionObserver(Ce,{threshold:[0,_,le,1]}),w=new globalThis.IntersectionObserver(we,{rootMargin:ce,threshold:0}))}function Ee(e){v.has(e)||(Te(),!(!C||!w)&&(v.add(e),C.observe(e),w.observe(e),_e(e)?.addEventListener(`click`,De),T(e)))}function De(e){e.preventDefault(),e.stopPropagation();let t=e.currentTarget.closest(`.media-video-wrap`)?.querySelector(`[data-feed-short-video]`);t&&(b=t,x!==t||t.muted?(x&&x!==t&&(x.muted=!0),x=t,y!==t&&(E(y),y=t),be(t)):(x=null,t.muted=!0,T(t)),ve(),D())}function Oe(e=document){let t=e.querySelectorAll(`[data-feed-short-video]`);for(let e of t)Ee(e);D()}document.addEventListener(g,e=>{pe=e.detail?.open===!0,pe&&E(y),D()}),document.addEventListener(`visibilitychange`,()=>{document.hidden&&E(y),D()}),document.readyState===`loading`?document.addEventListener(`DOMContentLoaded`,()=>Oe(),{once:!0}):queueMicrotask(()=>Oe());var ke=4;function Ae(e){return e.querySelector(`[data-post-media]`)}function je(e){let t=Ae(e);if(!t)return;let{scrollLeft:n,scrollWidth:r,clientWidth:i}=t;e.classList.toggle(`can-scroll-start`,n>ke),e.classList.toggle(`can-scroll-end`,n+i<r-ke)}function Me(e){return Math.max(160,Math.round(e.clientWidth*.85))}function O(e,t){e.scrollBy({left:t*Me(e),behavior:`smooth`})}function k(e){if(e.dataset.scrollHintReady===`1`)return;let t=Ae(e);t&&(e.dataset.scrollHintReady=`1`,je(e),t.addEventListener(`scroll`,()=>je(e),{passive:!0}),e.querySelector(`.media-gallery-nav-prev`)?.addEventListener(`click`,()=>O(t,-1)),e.querySelector(`.media-gallery-nav-next`)?.addEventListener(`click`,()=>O(t,1)),t.addEventListener(`keydown`,e=>{if(e.target===t)switch(e.key){case`ArrowRight`:e.preventDefault(),O(t,1);break;case`ArrowLeft`:e.preventDefault(),O(t,-1);break;case`Home`:e.preventDefault(),t.scrollTo({left:0,behavior:`smooth`});break;case`End`:e.preventDefault(),t.scrollTo({left:t.scrollWidth,behavior:`smooth`});break}}))}function Ne(){document.querySelectorAll(`.media-gallery-scroll-wrap`).forEach(k)}var Pe=new globalThis.MutationObserver(e=>{for(let t of e)for(let e of t.addedNodes)e instanceof HTMLElement&&(e.matches(`.media-gallery-scroll-wrap`)&&k(e),e.querySelectorAll(`.media-gallery-scroll-wrap`).forEach(k))});document.readyState===`loading`?document.addEventListener(`DOMContentLoaded`,()=>{Ne(),Pe.observe(document.body,{childList:!0,subtree:!0})}):(Ne(),Pe.observe(document.body,{childList:!0,subtree:!0}));function Fe(e){let t=e.querySelector(`.site-header-more-btn`),n=e.querySelector(`.site-header-more-popover`);if(!t||!n||t.dataset.moreInitialized===`true`)return;t.dataset.moreInitialized=`true`;function r(){n.setAttribute(`aria-hidden`,`false`),t.setAttribute(`aria-expanded`,`true`),document.dispatchEvent(new CustomEvent(`basecoat:popover`,{detail:{source:t.parentElement}}))}function i(e=!1){n.setAttribute(`aria-hidden`,`true`),t.setAttribute(`aria-expanded`,`false`),e&&t.focus()}t.addEventListener(`click`,e=>{e.preventDefault(),e.stopPropagation(),t.getAttribute(`aria-expanded`)===`true`?i():r()}),document.addEventListener(`click`,e=>{e.target instanceof Node&&(t.parentElement?.contains(e.target)||i())}),document.addEventListener(`keydown`,e=>{e.key===`Escape`&&n.getAttribute(`aria-hidden`)===`false`&&i(!0)}),document.addEventListener(`basecoat:popover`,e=>{e.detail?.source!==t.parentElement&&i()})}var Ie=`jant:nav-fresh-visits`;function Le(){let e=document.createElement(`span`);return e.className=`site-header-link-fresh`,e.setAttribute(`aria-hidden`,`true`),e.textContent=`*`,e}function Re(e){try{let t=JSON.parse(localStorage.getItem(Ie)||`{}`),n=location.pathname,r=e.querySelectorAll(`[data-fresh-at]`);for(let e of r){let r=new URL(e.href).pathname,i=parseInt(e.dataset.freshAt,10);if(r===n)t[r]=Math.floor(Date.now()/1e3);else{let n=t[r];if(!n||n<i){let t=e.querySelector(`svg`);e.insertBefore(Le(),t)}}}localStorage.setItem(Ie,JSON.stringify(t))}catch{}}function ze(e=document){let t=e.querySelector(`.site-header-hamburger`),n=e.querySelector(`#site-nav-drawer`),r=e.querySelector(`.site-nav-drawer-backdrop`),i=n?.querySelector(`.site-nav-drawer-close`);if(Re(e),Fe(e),!t||!n||!r||t.dataset.drawerInitialized===`true`)return;t.dataset.drawerInitialized=`true`;function a(){n.setAttribute(`aria-hidden`,`false`),n.removeAttribute(`inert`),r.setAttribute(`aria-hidden`,`false`),t.setAttribute(`aria-expanded`,`true`),document.documentElement.classList.add(`drawer-open`);let e=n.querySelector(`.site-nav-drawer-close`)??n.querySelector(`a[href], button`);e&&e.focus()}function o(e=!0){n.setAttribute(`aria-hidden`,`true`),r.setAttribute(`aria-hidden`,`true`),t.setAttribute(`aria-expanded`,`false`),document.documentElement.classList.remove(`drawer-open`),n.addEventListener(`transitionend`,()=>{n.getAttribute(`aria-hidden`)===`true`&&n.setAttribute(`inert`,``)},{once:!0}),e&&t.focus()}t.addEventListener(`click`,()=>{t.getAttribute(`aria-expanded`)===`true`?o():a()}),i?.addEventListener(`click`,()=>o()),r.addEventListener(`click`,()=>o()),n.addEventListener(`click`,e=>{e.target instanceof Element&&e.target.closest(`a[href]`)&&o(!1)}),n.addEventListener(`keydown`,e=>{e.key===`Escape`&&(e.preventDefault(),o())})}ze();var A=globalThis,j=A.ShadowRoot&&(A.ShadyCSS===void 0||A.ShadyCSS.nativeShadow)&&`adoptedStyleSheets`in Document.prototype&&`replace`in CSSStyleSheet.prototype,Be=Symbol(),Ve=new WeakMap,He=class{constructor(e,t,n){if(this._$cssResult$=!0,n!==Be)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=e,this.t=t}get styleSheet(){let e=this.o,t=this.t;if(j&&e===void 0){let n=t!==void 0&&t.length===1;n&&(e=Ve.get(t)),e===void 0&&((this.o=e=new CSSStyleSheet).replaceSync(this.cssText),n&&Ve.set(t,e))}return e}toString(){return this.cssText}},Ue=e=>new He(typeof e==`string`?e:e+``,void 0,Be),We=(e,t)=>{if(j)e.adoptedStyleSheets=t.map(e=>e instanceof CSSStyleSheet?e:e.styleSheet);else for(let n of t){let t=document.createElement(`style`),r=A.litNonce;r!==void 0&&t.setAttribute(`nonce`,r),t.textContent=n.cssText,e.appendChild(t)}},Ge=j?e=>e:e=>e instanceof CSSStyleSheet?(e=>{let t=``;for(let n of e.cssRules)t+=n.cssText;return Ue(t)})(e):e,{is:Ke,defineProperty:qe,getOwnPropertyDescriptor:Je,getOwnPropertyNames:Ye,getOwnPropertySymbols:Xe,getPrototypeOf:Ze}=Object,M=globalThis,Qe=M.trustedTypes,$e=Qe?Qe.emptyScript:``,et=M.reactiveElementPolyfillSupport,N=(e,t)=>e,P={toAttribute(e,t){switch(t){case Boolean:e=e?$e:null;break;case Object:case Array:e=e==null?e:JSON.stringify(e)}return e},fromAttribute(e,t){let n=e;switch(t){case Boolean:n=e!==null;break;case Number:n=e===null?null:Number(e);break;case Object:case Array:try{n=JSON.parse(e)}catch{n=null}}return n}},tt=(e,t)=>!Ke(e,t),nt={attribute:!0,type:String,converter:P,reflect:!1,useDefault:!1,hasChanged:tt};Symbol.metadata??=Symbol(`metadata`),M.litPropertyMetadata??=new WeakMap;var F=class extends HTMLElement{static addInitializer(e){this._$Ei(),(this.l??=[]).push(e)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(e,t=nt){if(t.state&&(t.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(e)&&((t=Object.create(t)).wrapped=!0),this.elementProperties.set(e,t),!t.noAccessor){let n=Symbol(),r=this.getPropertyDescriptor(e,n,t);r!==void 0&&qe(this.prototype,e,r)}}static getPropertyDescriptor(e,t,n){let{get:r,set:i}=Je(this.prototype,e)??{get(){return this[t]},set(e){this[t]=e}};return{get:r,set(t){let a=r?.call(this);i?.call(this,t),this.requestUpdate(e,a,n)},configurable:!0,enumerable:!0}}static getPropertyOptions(e){return this.elementProperties.get(e)??nt}static _$Ei(){if(this.hasOwnProperty(N(`elementProperties`)))return;let e=Ze(this);e.finalize(),e.l!==void 0&&(this.l=[...e.l]),this.elementProperties=new Map(e.elementProperties)}static finalize(){if(this.hasOwnProperty(N(`finalized`)))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(N(`properties`))){let e=this.properties,t=[...Ye(e),...Xe(e)];for(let n of t)this.createProperty(n,e[n])}let e=this[Symbol.metadata];if(e!==null){let t=litPropertyMetadata.get(e);if(t!==void 0)for(let[e,n]of t)this.elementProperties.set(e,n)}this._$Eh=new Map;for(let[e,t]of this.elementProperties){let n=this._$Eu(e,t);n!==void 0&&this._$Eh.set(n,e)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(e){let t=[];if(Array.isArray(e)){let n=new Set(e.flat(1/0).reverse());for(let e of n)t.unshift(Ge(e))}else e!==void 0&&t.push(Ge(e));return t}static _$Eu(e,t){let n=t.attribute;return!1===n?void 0:typeof n==`string`?n:typeof e==`string`?e.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(e=>this.enableUpdating=e),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(e=>e(this))}addController(e){(this._$EO??=new Set).add(e),this.renderRoot!==void 0&&this.isConnected&&e.hostConnected?.()}removeController(e){this._$EO?.delete(e)}_$E_(){let e=new Map,t=this.constructor.elementProperties;for(let n of t.keys())this.hasOwnProperty(n)&&(e.set(n,this[n]),delete this[n]);e.size>0&&(this._$Ep=e)}createRenderRoot(){let e=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return We(e,this.constructor.elementStyles),e}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(e=>e.hostConnected?.())}enableUpdating(e){}disconnectedCallback(){this._$EO?.forEach(e=>e.hostDisconnected?.())}attributeChangedCallback(e,t,n){this._$AK(e,n)}_$ET(e,t){let n=this.constructor.elementProperties.get(e),r=this.constructor._$Eu(e,n);if(r!==void 0&&!0===n.reflect){let i=(n.converter?.toAttribute===void 0?P:n.converter).toAttribute(t,n.type);this._$Em=e,i==null?this.removeAttribute(r):this.setAttribute(r,i),this._$Em=null}}_$AK(e,t){let n=this.constructor,r=n._$Eh.get(e);if(r!==void 0&&this._$Em!==r){let e=n.getPropertyOptions(r),i=typeof e.converter==`function`?{fromAttribute:e.converter}:e.converter?.fromAttribute===void 0?P:e.converter;this._$Em=r;let a=i.fromAttribute(t,e.type);this[r]=a??this._$Ej?.get(r)??a,this._$Em=null}}requestUpdate(e,t,n,r=!1,i){if(e!==void 0){let a=this.constructor;if(!1===r&&(i=this[e]),n??=a.getPropertyOptions(e),!((n.hasChanged??tt)(i,t)||n.useDefault&&n.reflect&&i===this._$Ej?.get(e)&&!this.hasAttribute(a._$Eu(e,n))))return;this.C(e,t,n)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(e,t,{useDefault:n,reflect:r,wrapped:i},a){n&&!(this._$Ej??=new Map).has(e)&&(this._$Ej.set(e,a??t??this[e]),!0!==i||a!==void 0)||(this._$AL.has(e)||(this.hasUpdated||n||(t=void 0),this._$AL.set(e,t)),!0===r&&this._$Em!==e&&(this._$Eq??=new Set).add(e))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(e){Promise.reject(e)}let e=this.scheduleUpdate();return e!=null&&await e,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(let[e,t]of this._$Ep)this[e]=t;this._$Ep=void 0}let e=this.constructor.elementProperties;if(e.size>0)for(let[t,n]of e){let{wrapped:e}=n,r=this[t];!0!==e||this._$AL.has(t)||r===void 0||this.C(t,void 0,n,r)}}let e=!1,t=this._$AL;try{e=this.shouldUpdate(t),e?(this.willUpdate(t),this._$EO?.forEach(e=>e.hostUpdate?.()),this.update(t)):this._$EM()}catch(t){throw e=!1,this._$EM(),t}e&&this._$AE(t)}willUpdate(e){}_$AE(e){this._$EO?.forEach(e=>e.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(e)),this.updated(e)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(e){return!0}update(e){this._$Eq&&=this._$Eq.forEach(e=>this._$ET(e,this[e])),this._$EM()}updated(e){}firstUpdated(e){}};F.elementStyles=[],F.shadowRootOptions={mode:`open`},F[N(`elementProperties`)]=new Map,F[N(`finalized`)]=new Map,et?.({ReactiveElement:F}),(M.reactiveElementVersions??=[]).push(`2.1.2`);var I=globalThis,rt=e=>e,L=I.trustedTypes,it=L?L.createPolicy(`lit-html`,{createHTML:e=>e}):void 0,at=`$lit$`,R=`lit$${Math.random().toFixed(9).slice(2)}$`,ot=`?`+R,st=`<${ot}>`,z=document,B=()=>z.createComment(``),V=e=>e===null||typeof e!=`object`&&typeof e!=`function`,ct=Array.isArray,lt=e=>ct(e)||typeof e?.[Symbol.iterator]==`function`,ut=`[
|
|
2
2
|
\f\r]`,H=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,dt=/-->/g,ft=/>/g,U=RegExp(`>|${ut}(?:([^\\s"'>=/]+)(${ut}*=${ut}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,`g`),pt=/'/g,mt=/"/g,ht=/^(?:script|style|textarea|title)$/i,gt=e=>(t,...n)=>({_$litType$:e,strings:t,values:n}),W=gt(1),_t=gt(2),G=Symbol.for(`lit-noChange`),K=Symbol.for(`lit-nothing`),vt=new WeakMap,q=z.createTreeWalker(z,129);function yt(e,t){if(!ct(e)||!e.hasOwnProperty(`raw`))throw Error(`invalid template strings array`);return it===void 0?t:it.createHTML(t)}var bt=(e,t)=>{let n=e.length-1,r=[],i,a=t===2?`<svg>`:t===3?`<math>`:``,o=H;for(let t=0;t<n;t++){let n=e[t],s,c,l=-1,u=0;for(;u<n.length&&(o.lastIndex=u,c=o.exec(n),c!==null);)u=o.lastIndex,o===H?c[1]===`!--`?o=dt:c[1]===void 0?c[2]===void 0?c[3]!==void 0&&(o=U):(ht.test(c[2])&&(i=RegExp(`</`+c[2],`g`)),o=U):o=ft:o===U?c[0]===`>`?(o=i??H,l=-1):c[1]===void 0?l=-2:(l=o.lastIndex-c[2].length,s=c[1],o=c[3]===void 0?U:c[3]===`"`?mt:pt):o===mt||o===pt?o=U:o===dt||o===ft?o=H:(o=U,i=void 0);let d=o===U&&e[t+1].startsWith(`/>`)?` `:``;a+=o===H?n+st:l>=0?(r.push(s),n.slice(0,l)+at+n.slice(l)+R+d):n+R+(l===-2?t:d)}return[yt(e,a+(e[n]||`<?>`)+(t===2?`</svg>`:t===3?`</math>`:``)),r]},xt=class e{constructor({strings:t,_$litType$:n},r){let i;this.parts=[];let a=0,o=0,s=t.length-1,c=this.parts,[l,u]=bt(t,n);if(this.el=e.createElement(l,r),q.currentNode=this.el.content,n===2||n===3){let e=this.el.content.firstChild;e.replaceWith(...e.childNodes)}for(;(i=q.nextNode())!==null&&c.length<s;){if(i.nodeType===1){if(i.hasAttributes())for(let e of i.getAttributeNames())if(e.endsWith(at)){let t=u[o++],n=i.getAttribute(e).split(R),r=/([.?@])?(.*)/.exec(t);c.push({type:1,index:a,name:r[2],strings:n,ctor:r[1]===`.`?Ct:r[1]===`?`?wt:r[1]===`@`?Tt:X}),i.removeAttribute(e)}else e.startsWith(R)&&(c.push({type:6,index:a}),i.removeAttribute(e));if(ht.test(i.tagName)){let e=i.textContent.split(R),t=e.length-1;if(t>0){i.textContent=L?L.emptyScript:``;for(let n=0;n<t;n++)i.append(e[n],B()),q.nextNode(),c.push({type:2,index:++a});i.append(e[t],B())}}}else if(i.nodeType===8)if(i.data===ot)c.push({type:2,index:a});else{let e=-1;for(;(e=i.data.indexOf(R,e+1))!==-1;)c.push({type:7,index:a}),e+=R.length-1}a++}}static createElement(e,t){let n=z.createElement(`template`);return n.innerHTML=e,n}};function J(e,t,n=e,r){if(t===G)return t;let i=r===void 0?n._$Cl:n._$Co?.[r],a=V(t)?void 0:t._$litDirective$;return i?.constructor!==a&&(i?._$AO?.(!1),a===void 0?i=void 0:(i=new a(e),i._$AT(e,n,r)),r===void 0?n._$Cl=i:(n._$Co??=[])[r]=i),i!==void 0&&(t=J(e,i._$AS(e,t.values),i,r)),t}var St=class{constructor(e,t){this._$AV=[],this._$AN=void 0,this._$AD=e,this._$AM=t}get parentNode(){return this._$AM.parentNode}get _$AU(){return this._$AM._$AU}u(e){let{el:{content:t},parts:n}=this._$AD,r=(e?.creationScope??z).importNode(t,!0);q.currentNode=r;let i=q.nextNode(),a=0,o=0,s=n[0];for(;s!==void 0;){if(a===s.index){let t;s.type===2?t=new Y(i,i.nextSibling,this,e):s.type===1?t=new s.ctor(i,s.name,s.strings,this,e):s.type===6&&(t=new Et(i,this,e)),this._$AV.push(t),s=n[++o]}a!==s?.index&&(i=q.nextNode(),a++)}return q.currentNode=z,r}p(e){let t=0;for(let n of this._$AV)n!==void 0&&(n.strings===void 0?n._$AI(e[t]):(n._$AI(e,n,t),t+=n.strings.length-2)),t++}},Y=class e{get _$AU(){return this._$AM?._$AU??this._$Cv}constructor(e,t,n,r){this.type=2,this._$AH=K,this._$AN=void 0,this._$AA=e,this._$AB=t,this._$AM=n,this.options=r,this._$Cv=r?.isConnected??!0}get parentNode(){let e=this._$AA.parentNode,t=this._$AM;return t!==void 0&&e?.nodeType===11&&(e=t.parentNode),e}get startNode(){return this._$AA}get endNode(){return this._$AB}_$AI(e,t=this){e=J(this,e,t),V(e)?e===K||e==null||e===``?(this._$AH!==K&&this._$AR(),this._$AH=K):e!==this._$AH&&e!==G&&this._(e):e._$litType$===void 0?e.nodeType===void 0?lt(e)?this.k(e):this._(e):this.T(e):this.$(e)}O(e){return this._$AA.parentNode.insertBefore(e,this._$AB)}T(e){this._$AH!==e&&(this._$AR(),this._$AH=this.O(e))}_(e){this._$AH!==K&&V(this._$AH)?this._$AA.nextSibling.data=e:this.T(z.createTextNode(e)),this._$AH=e}$(e){let{values:t,_$litType$:n}=e,r=typeof n==`number`?this._$AC(e):(n.el===void 0&&(n.el=xt.createElement(yt(n.h,n.h[0]),this.options)),n);if(this._$AH?._$AD===r)this._$AH.p(t);else{let e=new St(r,this),n=e.u(this.options);e.p(t),this.T(n),this._$AH=e}}_$AC(e){let t=vt.get(e.strings);return t===void 0&&vt.set(e.strings,t=new xt(e)),t}k(t){ct(this._$AH)||(this._$AH=[],this._$AR());let n=this._$AH,r,i=0;for(let a of t)i===n.length?n.push(r=new e(this.O(B()),this.O(B()),this,this.options)):r=n[i],r._$AI(a),i++;i<n.length&&(this._$AR(r&&r._$AB.nextSibling,i),n.length=i)}_$AR(e=this._$AA.nextSibling,t){for(this._$AP?.(!1,!0,t);e!==this._$AB;){let t=rt(e).nextSibling;rt(e).remove(),e=t}}setConnected(e){this._$AM===void 0&&(this._$Cv=e,this._$AP?.(e))}},X=class{get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}constructor(e,t,n,r,i){this.type=1,this._$AH=K,this._$AN=void 0,this.element=e,this.name=t,this._$AM=r,this.options=i,n.length>2||n[0]!==``||n[1]!==``?(this._$AH=Array(n.length-1).fill(new String),this.strings=n):this._$AH=K}_$AI(e,t=this,n,r){let i=this.strings,a=!1;if(i===void 0)e=J(this,e,t,0),a=!V(e)||e!==this._$AH&&e!==G,a&&(this._$AH=e);else{let r=e,o,s;for(e=i[0],o=0;o<i.length-1;o++)s=J(this,r[n+o],t,o),s===G&&(s=this._$AH[o]),a||=!V(s)||s!==this._$AH[o],s===K?e=K:e!==K&&(e+=(s??``)+i[o+1]),this._$AH[o]=s}a&&!r&&this.j(e)}j(e){e===K?this.element.removeAttribute(this.name):this.element.setAttribute(this.name,e??``)}},Ct=class extends X{constructor(){super(...arguments),this.type=3}j(e){this.element[this.name]=e===K?void 0:e}},wt=class extends X{constructor(){super(...arguments),this.type=4}j(e){this.element.toggleAttribute(this.name,!!e&&e!==K)}},Tt=class extends X{constructor(e,t,n,r,i){super(e,t,n,r,i),this.type=5}_$AI(e,t=this){if((e=J(this,e,t,0)??K)===G)return;let n=this._$AH,r=e===K&&n!==K||e.capture!==n.capture||e.once!==n.once||e.passive!==n.passive,i=e!==K&&(n===K||r);r&&this.element.removeEventListener(this.name,this,n),i&&this.element.addEventListener(this.name,this,e),this._$AH=e}handleEvent(e){typeof this._$AH==`function`?this._$AH.call(this.options?.host??this.element,e):this._$AH.handleEvent(e)}},Et=class{constructor(e,t,n){this.element=e,this.type=6,this._$AN=void 0,this._$AM=t,this.options=n}get _$AU(){return this._$AM._$AU}_$AI(e){J(this,e)}},Dt=I.litHtmlPolyfillSupport;Dt?.(xt,Y),(I.litHtmlVersions??=[]).push(`3.3.2`);var Ot=(e,t,n)=>{let r=n?.renderBefore??t,i=r._$litPart$;if(i===void 0){let e=n?.renderBefore??null;r._$litPart$=i=new Y(t.insertBefore(B(),e),e,void 0,n??{})}return i._$AI(e),i},Z=globalThis,Q=class extends F{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){let e=super.createRenderRoot();return this.renderOptions.renderBefore??=e.firstChild,e}update(e){let t=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(e),this._$Do=Ot(t,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return G}};Q._$litElement$=!0,Q.finalized=!0,Z.litElementHydrateSupport?.({LitElement:Q});var kt=Z.litElementPolyfillSupport;kt?.({LitElement:Q}),(Z.litElementVersions??=[]).push(`4.2.2`);function At(e){if(!e.mimeType?.startsWith(`video/`)||!Number.isFinite(e.durationSeconds)||!e.durationSeconds||e.durationSeconds<=0||e.durationSeconds>15)return!1;let t=e.size;return!(typeof t==`number`&&t>12582912)}var jt=640,Mt=8,Nt=72,Pt=16,Ft=704,It=.9,Lt=.85;function $(e){if(!(!Number.isFinite(e)||!e||e<=0))return e}function Rt(){return{width:globalThis.innerWidth||document.documentElement.clientWidth||0,height:globalThis.innerHeight||document.documentElement.clientHeight||0}}function zt(e,t){let n=e<=jt?Mt:Nt;return{width:Math.max(0,e-n*2),height:Math.max(0,t-Pt*2)}}function Bt(e,t,n){let r=$(e?.width),i=$(e?.height);if(!r||!i)return null;let a=zt(t,n);if(a.width<=0||a.height<=0)return null;let o=Math.min(a.width/r,a.height/i);return{width:Math.max(1,Math.round(r*o)),height:Math.max(1,Math.round(i*o))}}function Vt(e,t,n){if(!e||e.mimeType?.startsWith(`video/`)||!$(e.width)||!$(e.height)||t<=0||n<=0)return!1;let r=$(e.width),i=$(e.height);if(!r||!i)return!1;let a=t<=jt,o=zt(t,n),s=o.width,c=o.height;if(s<=0||c<=0)return!1;let l=r/i,u=Math.min(s,c*l);return l<It&&u<(a?s:Math.min(s,Ft))*Lt}var Ht=class extends Q{static properties={_images:{state:!0},_currentIndex:{state:!0},_open:{state:!0},_viewportWidth:{state:!0},_viewportHeight:{state:!0},_videoCurrentTime:{state:!0},_videoDuration:{state:!0},_videoMuted:{state:!0},_imageZoomed:{state:!0}};createRenderRoot(){return this.innerHTML=``,this}constructor(){super();let e=Rt();this._images=[],this._currentIndex=0,this._open=!1,this._viewportWidth=e.width,this._viewportHeight=e.height,this._videoCurrentTime=0,this._videoDuration=0,this._videoMuted=!1,this._imageZoomed=!1}connectedCallback(){super.connectedCallback(),document.addEventListener(`click`,this.#e),window.addEventListener(`resize`,this.#l),this.#u()}disconnectedCallback(){super.disconnectedCallback(),document.removeEventListener(`click`,this.#e),window.removeEventListener(`resize`,this.#l)}open(e,t){this.#u(),this.#d(),this._images=e,this._currentIndex=Math.max(0,Math.min(t,e.length-1)),this.#m(this._images[this._currentIndex]),this._imageZoomed=!1,this._open=!0,document.dispatchEvent(new CustomEvent(g,{detail:{open:!0}})),this.updateComplete.then(()=>{this.querySelector(`.media-lightbox`)?.showModal(),this.#f()})}close(){this.#d(),this.querySelector(`.media-lightbox`)?.close(),this._open=!1,document.dispatchEvent(new CustomEvent(g,{detail:{open:!1}}))}#e=e=>{let t=e.target,n=t.closest(`[data-post-media] a[data-lightbox-index]`);if(n){let t=n.closest(`[data-lightbox-group]`);if(!t)return;e.preventDefault();let r=parseInt(n.dataset.lightboxIndex??`0`,10);try{let e=JSON.parse(t.dataset.lightboxGroup??`[]`);e.length>0&&this.open(e,r)}catch{}return}let r=t.closest(`[data-post-body] img`);if(r){e.preventDefault();let t=r.closest(`[data-post-body]`);if(!t)return;let n=Array.from(t.querySelectorAll(`img`)),i=n.map(e=>({url:e.src,alt:e.alt||``,width:$(e.naturalWidth||Number(e.getAttribute(`width`))),height:$(e.naturalHeight||Number(e.getAttribute(`height`)))})),a=n.indexOf(r);i.length>0&&this.open(i,Math.max(0,a))}};#t(){this._images.length<=1||(this.#d(),this._imageZoomed=!1,this._currentIndex=(this._currentIndex-1+this._images.length)%this._images.length)}#n(){this._images.length<=1||(this.#d(),this._imageZoomed=!1,this._currentIndex=(this._currentIndex+1)%this._images.length)}#r=e=>{let t=this._images[this._currentIndex];Vt(t,this._viewportWidth,this._viewportHeight)&&(e.stopPropagation(),this._imageZoomed=!this._imageZoomed)};#i=e=>{let t=e,n=e.target;if(t.key===`Escape`){e.preventDefault(),this.close();return}if(n instanceof HTMLInputElement||n instanceof HTMLButtonElement||n instanceof HTMLVideoElement)return;if(!this._images[this._currentIndex]?.mimeType?.startsWith(`video/`)){t.key===`ArrowLeft`?(e.preventDefault(),this.#t()):t.key===`ArrowRight`&&(e.preventDefault(),this.#n());return}let r=this.querySelector(`.media-lightbox-video`);r&&this.#a(t,r)};#a(e,t){let n=Number.isFinite(t.duration)&&t.duration>0?t.duration:null,r=e=>{let r=n==null?Math.max(0,e):Math.max(0,Math.min(e,n));t.currentTime=r,this._videoCurrentTime=r},i=e.key,a=i.toLowerCase();if(i===` `||a===`k`)e.preventDefault(),t.paused?t.play().catch(()=>{}):t.pause();else if(i===`ArrowLeft`)e.preventDefault(),r(t.currentTime-2);else if(i===`ArrowRight`)e.preventDefault(),r(t.currentTime+2);else if(i===`Home`)e.preventDefault(),r(0);else if(i===`End`)n!=null&&(e.preventDefault(),r(n));else if(i.length===1&&i>=`0`&&i<=`9`)n!=null&&(e.preventDefault(),r(Number(i)/10*n));else if(i===`ArrowUp`)e.preventDefault(),t.volume=Math.min(1,t.volume+.05);else if(i===`ArrowDown`)e.preventDefault(),t.volume=Math.max(0,t.volume-.05);else if(a===`m`){e.preventDefault();let n=!t.muted;t.muted=n,this._videoMuted=n}else a===`f`&&(e.preventDefault(),this.#o(t))}#o(e){let t=document,n=e;if(document.fullscreenElement??t.webkitFullscreenElement){document.exitFullscreen?document.exitFullscreen().catch(()=>{}):t.webkitExitFullscreen?.();return}e.requestFullscreen?e.requestFullscreen().catch(()=>{}):n.webkitRequestFullscreen?n.webkitRequestFullscreen():n.webkitEnterFullscreen&&n.webkitEnterFullscreen()}#s=e=>{let t=e.target;(t===e.currentTarget||t.classList.contains(`media-lightbox-content`)||t.classList.contains(`media-lightbox-stage`))&&this.close()};#c=()=>{this.#d(),this._open&&document.dispatchEvent(new CustomEvent(g,{detail:{open:!1}})),this._open=!1};#l=()=>{this.#u()};#u(){let e=Rt();e.width===this._viewportWidth&&e.height===this._viewportHeight||(this._viewportWidth=e.width,this._viewportHeight=e.height)}#d(){this.querySelector(`.media-lightbox-video`)?.pause()}#f(){this.querySelector(`.media-lightbox-content`)?.focus()}#p=()=>{this.querySelector(`.media-lightbox-content`)?.focus({preventScroll:!0})};#m(e){this._videoCurrentTime=0,this._videoDuration=e?.durationSeconds&&e.durationSeconds>0?e.durationSeconds:0,this._videoMuted=!1}#h(){let e=this._images[this._currentIndex];if(!At(e)){this.#m(e);return}let t=this.querySelector(`.media-lightbox-video`);t&&(t.currentTime=0,t.muted=this._videoMuted,t.play().catch(()=>{}))}#g=e=>{let t=e.currentTarget;Number.isFinite(t.duration)&&t.duration>0&&(this._videoDuration=t.duration),this._videoCurrentTime=t.currentTime,t.muted=this._videoMuted};#_=e=>{let t=e.currentTarget;this._videoCurrentTime=t.currentTime,Number.isFinite(t.duration)&&t.duration>0&&(this._videoDuration=t.duration)};#v=e=>{let t=e.currentTarget,n=this.querySelector(`.media-lightbox-video`),r=Number.parseFloat(t.value);!n||!Number.isFinite(r)||r<0||(n.currentTime=r,this._videoCurrentTime=r)};#y=()=>{this._videoMuted=!this._videoMuted;let e=this.querySelector(`.media-lightbox-video`);e&&(e.muted=this._videoMuted)};updated(e){if(super.updated(e),!this._open||!e.has(`_currentIndex`)&&!e.has(`_open`)&&!e.has(`_imageZoomed`))return;let t=this.querySelector(`.media-lightbox-stage`);t&&(t.scrollTop=0,t.scrollLeft=0,(e.has(`_currentIndex`)||e.has(`_open`))&&(this.#h(),this.#f()))}render(){if(!this._open)return K;let e=this._images[this._currentIndex],t=this._images.length>1,n=e?.mimeType?.startsWith(`video/`),r=At(e),i=Vt(e,this._viewportWidth,this._viewportHeight),a=i&&this._imageZoomed,o=r?Bt(e,this._viewportWidth,this._viewportHeight):null,s=r&&!!o&&o.height>o.width,c=o?`width:${o.width}px;height:${o.height}px;`:K,l=this._videoDuration>0?this._videoDuration:e?.durationSeconds??1,u=Math.min(this._videoCurrentTime,l),d=l>0?u/l*100:0;return W`
|
|
3
3
|
<dialog
|
|
4
4
|
class=${`media-lightbox${r?` media-lightbox-short`:``}`}
|
|
@@ -1351,8 +1351,10 @@ hr.feed-divider {
|
|
|
1351
1351
|
z-index: 1;
|
|
1352
1352
|
}
|
|
1353
1353
|
|
|
1354
|
-
/* Gap "N more posts" row has no card so we place the
|
|
1355
|
-
vertical midpoint instead of the card's top area.
|
|
1354
|
+
/* Gap "N more posts" row has no card so we place the marker at its
|
|
1355
|
+
vertical midpoint instead of the card's top area. Swap the single dot
|
|
1356
|
+
for a short column of small beads (a vertical "⋮") so the rail visibly
|
|
1357
|
+
"skips" a run of posts — mirrors the main site's `.thread-item-gap`. */
|
|
1356
1358
|
.thread-preview .thread-item-gap {
|
|
1357
1359
|
display: flex;
|
|
1358
1360
|
align-items: center;
|
|
@@ -1362,35 +1364,67 @@ hr.feed-divider {
|
|
|
1362
1364
|
.thread-preview .thread-item-gap::before {
|
|
1363
1365
|
top: 50%;
|
|
1364
1366
|
transform: translateY(-50%);
|
|
1367
|
+
left: calc(
|
|
1368
|
+
var(--thread-rail-left) + var(--thread-rail-width) / 2 - 2px -
|
|
1369
|
+
var(--thread-rail-indent)
|
|
1370
|
+
);
|
|
1371
|
+
width: 4px;
|
|
1372
|
+
height: 24px;
|
|
1373
|
+
border: 0;
|
|
1374
|
+
border-radius: 0;
|
|
1375
|
+
background: radial-gradient(
|
|
1376
|
+
circle,
|
|
1377
|
+
var(--site-threadline) 1.75px,
|
|
1378
|
+
transparent 2px
|
|
1379
|
+
);
|
|
1380
|
+
background-size: 4px 8px;
|
|
1381
|
+
background-repeat: repeat-y;
|
|
1382
|
+
background-position: center top;
|
|
1383
|
+
box-shadow: none;
|
|
1365
1384
|
}
|
|
1366
1385
|
|
|
1367
|
-
/* Hidden-posts count —
|
|
1368
|
-
|
|
1386
|
+
/* Hidden-posts count — a calm route into the thread, matching the main
|
|
1387
|
+
site's `.thread-gap-link`: styled like a sibling of the Show more toggle
|
|
1388
|
+
with a small right-pointing chevron rather than an underline, firming up
|
|
1389
|
+
to the primary text colour on hover. */
|
|
1369
1390
|
.thread-preview-gap {
|
|
1370
1391
|
display: inline-flex;
|
|
1371
1392
|
align-items: center;
|
|
1372
|
-
gap: 0.
|
|
1393
|
+
gap: 0.4rem;
|
|
1373
1394
|
align-self: flex-start;
|
|
1374
1395
|
margin: 0.15rem 0;
|
|
1375
|
-
padding: 0.
|
|
1376
|
-
border: 1px dashed var(--site-thread-context-border);
|
|
1377
|
-
border-radius: 999px;
|
|
1378
|
-
background: var(--site-thread-gap-bg);
|
|
1396
|
+
padding: 0.25rem 0;
|
|
1379
1397
|
color: var(--site-text-secondary);
|
|
1380
1398
|
font-size: var(--type-thread-context-meta);
|
|
1381
|
-
|
|
1399
|
+
font-weight: 500;
|
|
1400
|
+
line-height: 1.2;
|
|
1382
1401
|
text-decoration: none;
|
|
1402
|
+
transition: color 0.18s ease;
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
.thread-preview-gap::after {
|
|
1406
|
+
content: "";
|
|
1407
|
+
width: 0.36em;
|
|
1408
|
+
height: 0.36em;
|
|
1409
|
+
border: 1.5px solid currentColor;
|
|
1410
|
+
border-left: 0;
|
|
1411
|
+
border-bottom: 0;
|
|
1412
|
+
opacity: 0.65;
|
|
1413
|
+
transform: rotate(45deg);
|
|
1383
1414
|
transition:
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
background-color 0.18s ease;
|
|
1415
|
+
transform 0.18s ease,
|
|
1416
|
+
opacity 0.18s ease;
|
|
1387
1417
|
}
|
|
1388
1418
|
|
|
1389
1419
|
.thread-preview-gap:hover,
|
|
1390
1420
|
.thread-preview-gap:focus {
|
|
1391
|
-
border-color: color-mix(in srgb, var(--site-accent) 22%, var(--site-divider));
|
|
1392
1421
|
color: var(--site-text-primary);
|
|
1393
|
-
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
.thread-preview-gap:hover::after,
|
|
1425
|
+
.thread-preview-gap:focus::after {
|
|
1426
|
+
opacity: 1;
|
|
1427
|
+
transform: translateX(2px) rotate(45deg);
|
|
1394
1428
|
}
|
|
1395
1429
|
|
|
1396
1430
|
/* Hero (latest reply) row: spacing above and below matches the main
|
package/src/services/media.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Handles media upload and management with pluggable storage backends.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { eq, desc, inArray, asc, sql, and, or } from "drizzle-orm";
|
|
7
|
+
import { eq, desc, inArray, asc, sql, and, or, isNull, lt } from "drizzle-orm";
|
|
8
8
|
import { generateKeyBetween } from "fractional-indexing";
|
|
9
9
|
import { type Database, supportsDrizzleTransaction } from "../db/index.js";
|
|
10
10
|
import type { DatabaseDialect } from "../db/dialect.js";
|
|
@@ -198,6 +198,19 @@ export interface MediaService {
|
|
|
198
198
|
*/
|
|
199
199
|
deleteByIds(ids: string[], storage?: StorageDriver | null): Promise<void>;
|
|
200
200
|
getByStorageKey(storageKey: string, provider: string): Promise<Media | null>;
|
|
201
|
+
/**
|
|
202
|
+
* Return IDs of orphaned media — rows never attached to a post
|
|
203
|
+
* (`postId IS NULL`) created before the given cutoff. Used by the upload
|
|
204
|
+
* cleanup sweep to reap media uploaded during compose but never published.
|
|
205
|
+
*
|
|
206
|
+
* @param input.before - Unix-seconds cutoff; only rows with `createdAt < before` are returned
|
|
207
|
+
* @param input.limit - Maximum number of IDs to return (batch bound)
|
|
208
|
+
* @returns Orphaned media IDs, oldest first
|
|
209
|
+
*/
|
|
210
|
+
listOrphanedMediaIds(input: {
|
|
211
|
+
before: number;
|
|
212
|
+
limit: number;
|
|
213
|
+
}): Promise<string[]>;
|
|
201
214
|
createTextAttachment(
|
|
202
215
|
data: CreateTextAttachmentData,
|
|
203
216
|
deps: TextAttachmentDeps,
|
|
@@ -501,6 +514,23 @@ export function createMediaService(
|
|
|
501
514
|
return rows.map(toMedia);
|
|
502
515
|
},
|
|
503
516
|
|
|
517
|
+
async listOrphanedMediaIds({ before, limit }) {
|
|
518
|
+
if (limit <= 0) return [];
|
|
519
|
+
const rows = await db
|
|
520
|
+
.select({ id: media.id })
|
|
521
|
+
.from(media)
|
|
522
|
+
.where(
|
|
523
|
+
and(
|
|
524
|
+
eq(media.siteId, siteId),
|
|
525
|
+
isNull(media.postId),
|
|
526
|
+
lt(media.createdAt, before),
|
|
527
|
+
),
|
|
528
|
+
)
|
|
529
|
+
.orderBy(asc(media.createdAt), asc(media.id))
|
|
530
|
+
.limit(limit);
|
|
531
|
+
return rows.map((r) => r.id);
|
|
532
|
+
},
|
|
533
|
+
|
|
504
534
|
async getByPostId(postId) {
|
|
505
535
|
const rows = await db
|
|
506
536
|
.select()
|
package/src/services/post.ts
CHANGED
|
@@ -112,6 +112,11 @@ export interface PostFilters {
|
|
|
112
112
|
hasTitle?: boolean;
|
|
113
113
|
/** Filter by rating presence */
|
|
114
114
|
hasRating?: boolean;
|
|
115
|
+
/**
|
|
116
|
+
* Filter thread roots by whether their thread contains published replies.
|
|
117
|
+
* Only meaningful together with `excludeReplies` (replies never match).
|
|
118
|
+
*/
|
|
119
|
+
hasReplies?: boolean;
|
|
115
120
|
/** Explicit result sort order */
|
|
116
121
|
sortOrder?: SortOrder;
|
|
117
122
|
limit?: number;
|
|
@@ -527,7 +532,7 @@ export function createPostService(
|
|
|
527
532
|
|
|
528
533
|
const effectiveVisibilityExpr = sql<string>`coalesce(
|
|
529
534
|
${posts.visibility},
|
|
530
|
-
(SELECT root.visibility FROM post AS root WHERE root.id = ${posts.threadId})
|
|
535
|
+
(SELECT root.visibility FROM post AS root WHERE root.id = ${posts.threadId} AND root.site_id = ${siteId})
|
|
531
536
|
)`;
|
|
532
537
|
|
|
533
538
|
/** Check if a slug is available (not used by posts or path_registry) */
|
|
@@ -800,6 +805,21 @@ export function createPostService(
|
|
|
800
805
|
filters.hasRating ? isNotNull(posts.rating) : isNull(posts.rating),
|
|
801
806
|
);
|
|
802
807
|
}
|
|
808
|
+
if (filters.hasReplies !== undefined) {
|
|
809
|
+
// Same notion of "reply" as getReplyCounts: published posts in the
|
|
810
|
+
// thread with reply_to_id set.
|
|
811
|
+
const repliesExist = sql`EXISTS (
|
|
812
|
+
SELECT 1
|
|
813
|
+
FROM post AS reply
|
|
814
|
+
WHERE reply.site_id = ${siteId}
|
|
815
|
+
AND reply.thread_id = ${posts.id}
|
|
816
|
+
AND reply.reply_to_id IS NOT NULL
|
|
817
|
+
AND reply.status = 'published'
|
|
818
|
+
)`;
|
|
819
|
+
conditions.push(
|
|
820
|
+
filters.hasReplies ? repliesExist : sql`NOT ${repliesExist}`,
|
|
821
|
+
);
|
|
822
|
+
}
|
|
803
823
|
|
|
804
824
|
return conditions;
|
|
805
825
|
}
|
|
@@ -1072,7 +1092,7 @@ export function createPostService(
|
|
|
1072
1092
|
}
|
|
1073
1093
|
|
|
1074
1094
|
function buildThreadRootPageConditions(options?: ThreadRootPageOptions) {
|
|
1075
|
-
const conditions: SQL[] = [];
|
|
1095
|
+
const conditions: SQL[] = [eq(posts.siteId, siteId)];
|
|
1076
1096
|
const status = options?.status;
|
|
1077
1097
|
|
|
1078
1098
|
if (status) {
|
package/src/services/search.ts
CHANGED
|
@@ -180,7 +180,7 @@ export function createSearchService(
|
|
|
180
180
|
snippet(post_fts, 1, char(2), char(3), '...', 32) AS snippet
|
|
181
181
|
FROM post_fts
|
|
182
182
|
JOIN post ON post.rowid = post_fts.rowid
|
|
183
|
-
JOIN post AS root_post ON root_post.id = post.thread_id
|
|
183
|
+
JOIN post AS root_post ON root_post.id = post.thread_id AND root_post.site_id = post.site_id
|
|
184
184
|
JOIN path_registry
|
|
185
185
|
ON path_registry.post_id = post.id
|
|
186
186
|
AND path_registry.site_id = post.site_id
|
|
@@ -254,7 +254,7 @@ export function createSearchService(
|
|
|
254
254
|
) AS snippet
|
|
255
255
|
FROM post
|
|
256
256
|
CROSS JOIN search_query
|
|
257
|
-
JOIN post AS root_post ON root_post.id = post.thread_id
|
|
257
|
+
JOIN post AS root_post ON root_post.id = post.thread_id AND root_post.site_id = post.site_id
|
|
258
258
|
JOIN path_registry
|
|
259
259
|
ON path_registry.post_id = post.id
|
|
260
260
|
AND path_registry.site_id = post.site_id
|
|
@@ -300,7 +300,7 @@ export function createSearchService(
|
|
|
300
300
|
) AS rank,
|
|
301
301
|
NULL AS snippet
|
|
302
302
|
FROM post
|
|
303
|
-
JOIN post AS root_post ON root_post.id = post.thread_id
|
|
303
|
+
JOIN post AS root_post ON root_post.id = post.thread_id AND root_post.site_id = post.site_id
|
|
304
304
|
JOIN path_registry
|
|
305
305
|
ON path_registry.post_id = post.id
|
|
306
306
|
AND path_registry.site_id = post.site_id
|
|
@@ -342,7 +342,7 @@ export function createSearchService(
|
|
|
342
342
|
0 AS rank,
|
|
343
343
|
NULL AS snippet
|
|
344
344
|
FROM post
|
|
345
|
-
JOIN post AS root_post ON root_post.id = post.thread_id
|
|
345
|
+
JOIN post AS root_post ON root_post.id = post.thread_id AND root_post.site_id = post.site_id
|
|
346
346
|
JOIN path_registry
|
|
347
347
|
ON path_registry.post_id = post.id
|
|
348
348
|
AND path_registry.site_id = post.site_id
|