-6
actions/createPublicationDraft.ts
-6
actions/createPublicationDraft.ts
···
11
11
redirectUser: false,
12
12
firstBlockType: "text",
13
13
});
14
-
let { data: publication } = await supabaseServerClient
15
-
.from("publications")
16
-
.select("*")
17
-
.eq("uri", publication_uri)
18
-
.single();
19
-
if (publication?.identity_did !== identity.atp_did) return;
20
14
21
15
await supabaseServerClient
22
16
.from("leaflets_in_publications")
-71
actions/deleteLeaflet.ts
-71
actions/deleteLeaflet.ts
···
1
1
"use server";
2
-
import { refresh } from "next/cache";
3
2
4
3
import { drizzle } from "drizzle-orm/node-postgres";
5
4
import {
···
10
9
import { eq } from "drizzle-orm";
11
10
import { PermissionToken } from "src/replicache";
12
11
import { pool } from "supabase/pool";
13
-
import { getIdentityData } from "./getIdentityData";
14
-
import { supabaseServerClient } from "supabase/serverClient";
15
12
16
13
export async function deleteLeaflet(permission_token: PermissionToken) {
17
14
const client = await pool.connect();
···
35
32
.where(eq(permission_tokens.id, permission_token.id));
36
33
});
37
34
client.release();
38
-
39
-
refresh();
40
-
return;
41
-
}
42
-
43
-
export async function archivePost(token: string) {
44
-
let identity = await getIdentityData();
45
-
if (!identity) throw new Error("No Identity");
46
-
47
-
// Archive on homepage
48
-
await supabaseServerClient
49
-
.from("permission_token_on_homepage")
50
-
.update({ archived: true })
51
-
.eq("token", token)
52
-
.eq("identity", identity.id);
53
-
54
-
// Check if leaflet is in any publications where user is the creator
55
-
let { data: leafletInPubs } = await supabaseServerClient
56
-
.from("leaflets_in_publications")
57
-
.select("publication, publications!inner(identity_did)")
58
-
.eq("leaflet", token);
59
-
60
-
if (leafletInPubs) {
61
-
for (let pub of leafletInPubs) {
62
-
if (pub.publications.identity_did === identity.atp_did) {
63
-
await supabaseServerClient
64
-
.from("leaflets_in_publications")
65
-
.update({ archived: true })
66
-
.eq("leaflet", token)
67
-
.eq("publication", pub.publication);
68
-
}
69
-
}
70
-
}
71
-
72
-
refresh();
73
-
return;
74
-
}
75
-
76
-
export async function unarchivePost(token: string) {
77
-
let identity = await getIdentityData();
78
-
if (!identity) throw new Error("No Identity");
79
-
80
-
// Unarchive on homepage
81
-
await supabaseServerClient
82
-
.from("permission_token_on_homepage")
83
-
.update({ archived: false })
84
-
.eq("token", token)
85
-
.eq("identity", identity.id);
86
-
87
-
// Check if leaflet is in any publications where user is the creator
88
-
let { data: leafletInPubs } = await supabaseServerClient
89
-
.from("leaflets_in_publications")
90
-
.select("publication, publications!inner(identity_did)")
91
-
.eq("leaflet", token);
92
-
93
-
if (leafletInPubs) {
94
-
for (let pub of leafletInPubs) {
95
-
if (pub.publications.identity_did === identity.atp_did) {
96
-
await supabaseServerClient
97
-
.from("leaflets_in_publications")
98
-
.update({ archived: false })
99
-
.eq("leaflet", token)
100
-
.eq("publication", pub.publication);
101
-
}
102
-
}
103
-
}
104
-
105
-
refresh();
106
35
return;
107
36
}
-2
actions/getIdentityData.ts
-2
actions/getIdentityData.ts
···
24
24
entity_sets(entities(facts(*)))
25
25
)),
26
26
permission_token_on_homepage(
27
-
archived,
28
27
created_at,
29
28
permission_tokens!inner(
30
29
id,
31
30
root_entity,
32
31
permission_token_rights(*),
33
-
leaflets_to_documents(*, documents(*)),
34
32
leaflets_in_publications(*, publications(*), documents(*))
35
33
)
36
34
)
-33
actions/publications/moveLeafletToPublication.ts
-33
actions/publications/moveLeafletToPublication.ts
···
1
-
"use server";
2
-
3
-
import { getIdentityData } from "actions/getIdentityData";
4
-
import { supabaseServerClient } from "supabase/serverClient";
5
-
6
-
export async function moveLeafletToPublication(
7
-
leaflet_id: string,
8
-
publication_uri: string,
9
-
metadata: { title: string; description: string },
10
-
entitiesToDelete: string[],
11
-
) {
12
-
let identity = await getIdentityData();
13
-
if (!identity || !identity.atp_did) return null;
14
-
let { data: publication } = await supabaseServerClient
15
-
.from("publications")
16
-
.select("*")
17
-
.eq("uri", publication_uri)
18
-
.single();
19
-
if (publication?.identity_did !== identity.atp_did) return;
20
-
21
-
await supabaseServerClient.from("leaflets_in_publications").insert({
22
-
publication: publication_uri,
23
-
leaflet: leaflet_id,
24
-
doc: null,
25
-
title: metadata.title,
26
-
description: metadata.description,
27
-
});
28
-
29
-
await supabaseServerClient
30
-
.from("entities")
31
-
.delete()
32
-
.in("id", entitiesToDelete);
33
-
}
+26
actions/publications/updateLeafletDraftMetadata.ts
+26
actions/publications/updateLeafletDraftMetadata.ts
···
1
+
"use server";
2
+
3
+
import { getIdentityData } from "actions/getIdentityData";
4
+
import { supabaseServerClient } from "supabase/serverClient";
5
+
6
+
export async function updateLeafletDraftMetadata(
7
+
leafletID: string,
8
+
publication_uri: string,
9
+
title: string,
10
+
description: string,
11
+
) {
12
+
let identity = await getIdentityData();
13
+
if (!identity?.atp_did) return null;
14
+
let { data: publication } = await supabaseServerClient
15
+
.from("publications")
16
+
.select()
17
+
.eq("uri", publication_uri)
18
+
.single();
19
+
if (!publication || publication.identity_did !== identity.atp_did)
20
+
return null;
21
+
await supabaseServerClient
22
+
.from("leaflets_in_publications")
23
+
.update({ title, description })
24
+
.eq("leaflet", leafletID)
25
+
.eq("publication", publication_uri);
26
+
}
+51
-204
actions/publishToPublication.ts
+51
-204
actions/publishToPublication.ts
···
44
44
import { List, parseBlocksToList } from "src/utils/parseBlocksToList";
45
45
import { getBlocksWithTypeLocal } from "src/hooks/queries/useBlocks";
46
46
import { Lock } from "src/utils/lock";
47
-
import type { PubLeafletPublication } from "lexicons/api";
48
-
import {
49
-
ColorToRGB,
50
-
ColorToRGBA,
51
-
} from "components/ThemeManager/colorToLexicons";
52
-
import { parseColor } from "@react-stately/color";
53
47
54
48
export async function publishToPublication({
55
49
root_entity,
···
57
51
leaflet_id,
58
52
title,
59
53
description,
60
-
entitiesToDelete,
61
54
}: {
62
55
root_entity: string;
63
-
publication_uri?: string;
56
+
publication_uri: string;
64
57
leaflet_id: string;
65
58
title?: string;
66
59
description?: string;
67
-
entitiesToDelete?: string[];
68
60
}) {
69
61
const oauthClient = await createOauthClient();
70
62
let identity = await getIdentityData();
···
74
66
let agent = new AtpBaseClient(
75
67
credentialSession.fetchHandler.bind(credentialSession),
76
68
);
77
-
78
-
// Check if we're publishing to a publication or standalone
79
-
let draft: any = null;
80
-
let existingDocUri: string | null = null;
81
-
82
-
if (publication_uri) {
83
-
// Publishing to a publication - use leaflets_in_publications
84
-
let { data, error } = await supabaseServerClient
85
-
.from("publications")
86
-
.select("*, leaflets_in_publications(*, documents(*))")
87
-
.eq("uri", publication_uri)
88
-
.eq("leaflets_in_publications.leaflet", leaflet_id)
89
-
.single();
90
-
console.log(error);
91
-
92
-
if (!data || identity.atp_did !== data?.identity_did)
93
-
throw new Error("No draft or not publisher");
94
-
draft = data.leaflets_in_publications[0];
95
-
existingDocUri = draft?.doc;
96
-
} else {
97
-
// Publishing standalone - use leaflets_to_documents
98
-
let { data } = await supabaseServerClient
99
-
.from("leaflets_to_documents")
100
-
.select("*, documents(*)")
101
-
.eq("leaflet", leaflet_id)
102
-
.single();
103
-
draft = data;
104
-
existingDocUri = draft?.document;
105
-
}
106
-
107
-
// Heuristic: Remove title entities if this is the first time publishing
108
-
// (when coming from a standalone leaflet with entitiesToDelete passed in)
109
-
if (entitiesToDelete && entitiesToDelete.length > 0 && !existingDocUri) {
110
-
await supabaseServerClient
111
-
.from("entities")
112
-
.delete()
113
-
.in("id", entitiesToDelete);
114
-
}
115
-
69
+
let { data: draft } = await supabaseServerClient
70
+
.from("leaflets_in_publications")
71
+
.select("*, publications(*), documents(*)")
72
+
.eq("publication", publication_uri)
73
+
.eq("leaflet", leaflet_id)
74
+
.single();
75
+
if (!draft || identity.atp_did !== draft?.publications?.identity_did)
76
+
throw new Error("No draft or not publisher");
116
77
let { data } = await supabaseServerClient.rpc("get_facts", {
117
78
root: root_entity,
118
79
});
119
80
let facts = (data as unknown as Fact<Attribute>[]) || [];
120
81
121
-
let { pages } = await processBlocksToPages(
82
+
let { firstPageBlocks, pages } = await processBlocksToPages(
122
83
facts,
123
84
agent,
124
85
root_entity,
···
127
88
128
89
let existingRecord =
129
90
(draft?.documents?.data as PubLeafletDocument.Record | undefined) || {};
130
-
131
-
// Extract theme for standalone documents (not for publications)
132
-
let theme: PubLeafletPublication.Theme | undefined;
133
-
if (!publication_uri) {
134
-
theme = await extractThemeFromFacts(facts, root_entity, agent);
135
-
}
136
-
137
91
let record: PubLeafletDocument.Record = {
138
-
publishedAt: new Date().toISOString(),
139
-
...existingRecord,
140
92
$type: "pub.leaflet.document",
141
93
author: credentialSession.did!,
142
-
...(publication_uri && { publication: publication_uri }),
143
-
...(theme && { theme }),
94
+
publication: publication_uri,
95
+
publishedAt: new Date().toISOString(),
96
+
...existingRecord,
144
97
title: title || "Untitled",
145
98
description: description || "",
146
-
pages: pages.map((p) => {
147
-
if (p.type === "canvas") {
148
-
return {
149
-
$type: "pub.leaflet.pages.canvas" as const,
150
-
id: p.id,
151
-
blocks: p.blocks as PubLeafletPagesCanvas.Block[],
152
-
};
153
-
} else {
154
-
return {
155
-
$type: "pub.leaflet.pages.linearDocument" as const,
156
-
id: p.id,
157
-
blocks: p.blocks as PubLeafletPagesLinearDocument.Block[],
158
-
};
159
-
}
160
-
}),
99
+
pages: [
100
+
{
101
+
$type: "pub.leaflet.pages.linearDocument",
102
+
blocks: firstPageBlocks,
103
+
},
104
+
...pages.map((p) => {
105
+
if (p.type === "canvas") {
106
+
return {
107
+
$type: "pub.leaflet.pages.canvas" as const,
108
+
id: p.id,
109
+
blocks: p.blocks as PubLeafletPagesCanvas.Block[],
110
+
};
111
+
} else {
112
+
return {
113
+
$type: "pub.leaflet.pages.linearDocument" as const,
114
+
id: p.id,
115
+
blocks: p.blocks as PubLeafletPagesLinearDocument.Block[],
116
+
};
117
+
}
118
+
}),
119
+
],
161
120
};
162
-
163
-
// Keep the same rkey if updating an existing document
164
-
let rkey = existingDocUri ? new AtUri(existingDocUri).rkey : TID.nextStr();
121
+
let rkey = draft?.doc ? new AtUri(draft.doc).rkey : TID.nextStr();
165
122
let { data: result } = await agent.com.atproto.repo.putRecord({
166
123
rkey,
167
124
repo: credentialSession.did!,
···
170
127
validate: false, //TODO publish the lexicon so we can validate!
171
128
});
172
129
173
-
// Optimistically create database entries
174
130
await supabaseServerClient.from("documents").upsert({
175
131
uri: result.uri,
176
132
data: record as Json,
177
133
});
178
-
179
-
if (publication_uri) {
180
-
// Publishing to a publication - update both tables
181
-
await Promise.all([
182
-
supabaseServerClient.from("documents_in_publications").upsert({
183
-
publication: publication_uri,
184
-
document: result.uri,
185
-
}),
186
-
supabaseServerClient.from("leaflets_in_publications").upsert({
134
+
await Promise.all([
135
+
//Optimistically put these in!
136
+
supabaseServerClient.from("documents_in_publications").upsert({
137
+
publication: record.publication,
138
+
document: result.uri,
139
+
}),
140
+
supabaseServerClient
141
+
.from("leaflets_in_publications")
142
+
.update({
187
143
doc: result.uri,
188
-
leaflet: leaflet_id,
189
-
publication: publication_uri,
190
-
title: title,
191
-
description: description,
192
-
}),
193
-
]);
194
-
} else {
195
-
// Publishing standalone - update leaflets_to_documents
196
-
await supabaseServerClient.from("leaflets_to_documents").upsert({
197
-
leaflet: leaflet_id,
198
-
document: result.uri,
199
-
title: title || "Untitled",
200
-
description: description || "",
201
-
});
202
-
203
-
// Heuristic: Remove title entities if this is the first time publishing standalone
204
-
// (when entitiesToDelete is provided and there's no existing document)
205
-
if (entitiesToDelete && entitiesToDelete.length > 0 && !existingDocUri) {
206
-
await supabaseServerClient
207
-
.from("entities")
208
-
.delete()
209
-
.in("id", entitiesToDelete);
210
-
}
211
-
}
144
+
})
145
+
.eq("leaflet", leaflet_id)
146
+
.eq("publication", publication_uri),
147
+
]);
212
148
213
149
return { rkey, record: JSON.parse(JSON.stringify(record)) };
214
150
}
···
233
169
234
170
let firstEntity = scan.eav(root_entity, "root/page")?.[0];
235
171
if (!firstEntity) throw new Error("No root page");
236
-
237
-
// Check if the first page is a canvas or linear document
238
-
let [pageType] = scan.eav(firstEntity.data.value, "page/type");
239
-
240
-
if (pageType?.data.value === "canvas") {
241
-
// First page is a canvas
242
-
let canvasBlocks = await canvasBlocksToRecord(firstEntity.data.value, did);
243
-
pages.unshift({
244
-
id: firstEntity.data.value,
245
-
blocks: canvasBlocks,
246
-
type: "canvas",
247
-
});
248
-
} else {
249
-
// First page is a linear document
250
-
let blocks = getBlocksWithTypeLocal(facts, firstEntity?.data.value);
251
-
let b = await blocksToRecord(blocks, did);
252
-
pages.unshift({
253
-
id: firstEntity.data.value,
254
-
blocks: b,
255
-
type: "doc",
256
-
});
257
-
}
258
-
259
-
return { pages };
172
+
let blocks = getBlocksWithTypeLocal(facts, firstEntity?.data.value);
173
+
let b = await blocksToRecord(blocks, did);
174
+
return { firstPageBlocks: b, pages };
260
175
261
176
async function uploadImage(src: string) {
262
177
let data = await fetch(src);
···
657
572
? never
658
573
: T /* maybe literal, not the whole `string` */
659
574
: T; /* not a string */
660
-
661
-
async function extractThemeFromFacts(
662
-
facts: Fact<any>[],
663
-
root_entity: string,
664
-
agent: AtpBaseClient,
665
-
): Promise<PubLeafletPublication.Theme | undefined> {
666
-
let scan = scanIndexLocal(facts);
667
-
let pageBackground = scan.eav(root_entity, "theme/page-background")?.[0]?.data
668
-
.value;
669
-
let cardBackground = scan.eav(root_entity, "theme/card-background")?.[0]?.data
670
-
.value;
671
-
let primary = scan.eav(root_entity, "theme/primary")?.[0]?.data.value;
672
-
let accentBackground = scan.eav(root_entity, "theme/accent-background")?.[0]
673
-
?.data.value;
674
-
let accentText = scan.eav(root_entity, "theme/accent-text")?.[0]?.data.value;
675
-
let showPageBackground = !scan.eav(
676
-
root_entity,
677
-
"theme/card-border-hidden",
678
-
)?.[0]?.data.value;
679
-
let backgroundImage = scan.eav(root_entity, "theme/background-image")?.[0];
680
-
let backgroundImageRepeat = scan.eav(
681
-
root_entity,
682
-
"theme/background-image-repeat",
683
-
)?.[0];
684
-
685
-
let theme: PubLeafletPublication.Theme = {
686
-
showPageBackground: showPageBackground ?? true,
687
-
};
688
-
689
-
if (pageBackground)
690
-
theme.backgroundColor = ColorToRGBA(parseColor(`hsba(${pageBackground})`));
691
-
if (cardBackground)
692
-
theme.pageBackground = ColorToRGBA(parseColor(`hsba(${cardBackground})`));
693
-
if (primary) theme.primary = ColorToRGB(parseColor(`hsba(${primary})`));
694
-
if (accentBackground)
695
-
theme.accentBackground = ColorToRGB(
696
-
parseColor(`hsba(${accentBackground})`),
697
-
);
698
-
if (accentText)
699
-
theme.accentText = ColorToRGB(parseColor(`hsba(${accentText})`));
700
-
701
-
// Upload background image if present
702
-
if (backgroundImage?.data) {
703
-
let imageData = await fetch(backgroundImage.data.src);
704
-
if (imageData.status === 200) {
705
-
let binary = await imageData.blob();
706
-
let blob = await agent.com.atproto.repo.uploadBlob(binary, {
707
-
headers: { "Content-Type": binary.type },
708
-
});
709
-
710
-
theme.backgroundImage = {
711
-
$type: "pub.leaflet.theme.backgroundImage",
712
-
image: blob.data.blob,
713
-
repeat: backgroundImageRepeat?.data.value ? true : false,
714
-
...(backgroundImageRepeat?.data.value && {
715
-
width: backgroundImageRepeat.data.value,
716
-
}),
717
-
};
718
-
}
719
-
}
720
-
721
-
// Only return theme if at least one property is set
722
-
if (Object.keys(theme).length > 1 || theme.showPageBackground !== true) {
723
-
return theme;
724
-
}
725
-
726
-
return undefined;
727
-
}
+1
-1
actions/subscriptions/sendPostToSubscribers.ts
+1
-1
actions/subscriptions/sendPostToSubscribers.ts
+1
-1
app/(home-pages)/discover/PubListing.tsx
+1
-1
app/(home-pages)/discover/PubListing.tsx
···
16
16
},
17
17
) => {
18
18
let record = props.record as PubLeafletPublication.Record;
19
-
let theme = usePubTheme(record.theme);
19
+
let theme = usePubTheme(record);
20
20
let backgroundImage = record?.theme?.backgroundImage?.image?.ref
21
21
? blobRefToSrc(
22
22
record?.theme?.backgroundImage?.image?.ref,
+2
-1
app/(home-pages)/home/Actions/Actions.tsx
+2
-1
app/(home-pages)/home/Actions/Actions.tsx
···
1
1
"use client";
2
2
import { ThemePopover } from "components/ThemeManager/ThemeSetter";
3
3
import { CreateNewLeafletButton } from "./CreateNewButton";
4
-
import { HelpButton } from "app/[leaflet_id]/actions/HelpButton";
4
+
import { HelpPopover } from "components/HelpPopover";
5
5
import { AccountSettings } from "./AccountSettings";
6
6
import { useIdentityData } from "components/IdentityProvider";
7
7
import { useReplicache } from "src/replicache";
···
18
18
) : (
19
19
<LoginActionButton />
20
20
)}
21
+
<HelpPopover />
21
22
</>
22
23
);
23
24
};
+48
app/(home-pages)/home/Actions/CreateNewButton.tsx
+48
app/(home-pages)/home/Actions/CreateNewButton.tsx
···
1
1
"use client";
2
2
3
3
import { createNewLeaflet } from "actions/createNewLeaflet";
4
+
import { createNewLeafletFromTemplate } from "actions/createNewLeafletFromTemplate";
4
5
import { ActionButton } from "components/ActionBar/ActionButton";
5
6
import { AddTiny } from "components/Icons/AddTiny";
6
7
import { BlockCanvasPageSmall } from "components/Icons/BlockCanvasPageSmall";
7
8
import { BlockDocPageSmall } from "components/Icons/BlockDocPageSmall";
9
+
import { TemplateSmall } from "components/Icons/TemplateSmall";
8
10
import { Menu, MenuItem } from "components/Layout";
9
11
import { useIsMobile } from "src/hooks/isMobile";
12
+
import { create } from "zustand";
13
+
import { combine, createJSONStorage, persist } from "zustand/middleware";
10
14
15
+
export const useTemplateState = create(
16
+
persist(
17
+
combine(
18
+
{
19
+
templates: [] as { id: string; name: string }[],
20
+
},
21
+
(set) => ({
22
+
removeTemplate: (template: { id: string }) =>
23
+
set((state) => {
24
+
return {
25
+
templates: state.templates.filter((t) => t.id !== template.id),
26
+
};
27
+
}),
28
+
addTemplate: (template: { id: string; name: string }) =>
29
+
set((state) => {
30
+
if (state.templates.find((t) => t.id === template.id)) return state;
31
+
return { templates: [...state.templates, template] };
32
+
}),
33
+
}),
34
+
),
35
+
{
36
+
name: "home-templates",
37
+
storage: createJSONStorage(() => localStorage),
38
+
},
39
+
),
40
+
);
11
41
export const CreateNewLeafletButton = (props: {}) => {
12
42
let isMobile = useIsMobile();
43
+
let templates = useTemplateState((s) => s.templates);
13
44
let openNewLeaflet = (id: string) => {
14
45
if (isMobile) {
15
46
window.location.href = `/${id}?focusFirstBlock`;
···
65
96
</div>
66
97
</div>
67
98
</MenuItem>
99
+
{templates.length > 0 && (
100
+
<hr className="border-border-light mx-2 mb-0.5" />
101
+
)}
102
+
{templates.map((t) => {
103
+
return (
104
+
<MenuItem
105
+
key={t.id}
106
+
onSelect={async () => {
107
+
let id = await createNewLeafletFromTemplate(t.id, false);
108
+
if (!id.error) openNewLeaflet(id.id);
109
+
}}
110
+
>
111
+
<TemplateSmall />
112
+
New {t.name}
113
+
</MenuItem>
114
+
);
115
+
})}
68
116
</Menu>
69
117
);
70
118
};
+42
-47
app/(home-pages)/home/HomeLayout.tsx
+42
-47
app/(home-pages)/home/HomeLayout.tsx
···
21
21
} from "components/PageLayouts/DashboardLayout";
22
22
import { Actions } from "./Actions/Actions";
23
23
import { useCardBorderHidden } from "components/Pages/useCardBorderHidden";
24
+
import { useTemplateState } from "./Actions/CreateNewButton";
24
25
import { GetLeafletDataReturnType } from "app/api/rpc/[command]/get_leaflet_data";
25
26
import { useState } from "react";
26
27
import { useDebouncedEffect } from "src/hooks/useDebouncedEffect";
···
30
31
PublicationBanner,
31
32
} from "./HomeEmpty/HomeEmpty";
32
33
33
-
export type Leaflet = {
34
+
type Leaflet = {
34
35
added_at: string;
35
-
archived?: boolean | null;
36
36
token: PermissionToken & {
37
37
leaflets_in_publications?: Exclude<
38
38
GetLeafletDataReturnType["result"]["data"],
39
39
null
40
40
>["leaflets_in_publications"];
41
-
leaflets_to_documents?: Exclude<
42
-
GetLeafletDataReturnType["result"]["data"],
43
-
null
44
-
>["leaflets_to_documents"];
45
41
};
46
42
};
47
43
···
72
68
let { identity } = useIdentityData();
73
69
74
70
let hasPubs = !identity || identity.publications.length === 0 ? false : true;
75
-
let hasArchived =
76
-
identity &&
77
-
identity.permission_token_on_homepage.filter(
78
-
(leaflet) => leaflet.archived === true,
79
-
).length > 0;
71
+
let hasTemplates =
72
+
useTemplateState((s) => s.templates).length === 0 ? false : true;
80
73
81
74
return (
82
75
<DashboardLayout
···
94
87
setSearchValueAction={setSearchValue}
95
88
hasBackgroundImage={hasBackgroundImage}
96
89
hasPubs={hasPubs}
97
-
hasArchived={!!hasArchived}
90
+
hasTemplates={hasTemplates}
98
91
/>
99
92
),
100
93
content: (
···
134
127
...identity.permission_token_on_homepage.reduce(
135
128
(acc, tok) => {
136
129
let title =
137
-
tok.permission_tokens.leaflets_in_publications[0]?.title ||
138
-
tok.permission_tokens.leaflets_to_documents[0]?.title;
130
+
tok.permission_tokens.leaflets_in_publications[0]?.title;
139
131
if (title) acc[tok.permission_tokens.root_entity] = title;
140
132
return acc;
141
133
},
···
155
147
? identity.permission_token_on_homepage.map((ptoh) => ({
156
148
added_at: ptoh.created_at,
157
149
token: ptoh.permission_tokens as PermissionToken,
158
-
archived: ptoh.archived,
159
150
}))
160
151
: localLeaflets
161
152
.sort((a, b) => (a.added_at > b.added_at ? -1 : 1))
···
213
204
w-full
214
205
${display === "grid" ? "grid auto-rows-max md:grid-cols-4 sm:grid-cols-3 grid-cols-2 gap-y-4 gap-x-4 sm:gap-x-6 sm:gap-y-5 grow" : "flex flex-col gap-2 pt-2"} `}
215
206
>
216
-
{props.leaflets.map(({ token: leaflet, added_at, archived }, index) => (
207
+
{props.leaflets.map(({ token: leaflet, added_at }, index) => (
217
208
<ReplicacheProvider
218
209
disablePull
219
210
initialFactsOnly={!!identity}
···
227
218
value={{
228
219
...leaflet,
229
220
leaflets_in_publications: leaflet.leaflets_in_publications || [],
230
-
leaflets_to_documents: leaflet.leaflets_to_documents || [],
231
221
blocked_by_admin: null,
232
222
custom_domain_routes: [],
233
223
}}
234
224
>
235
225
<LeafletListItem
236
-
title={props?.titles?.[leaflet.root_entity]}
237
-
archived={archived}
226
+
title={props?.titles?.[leaflet.root_entity] || "Untitled"}
227
+
token={leaflet}
228
+
draft={!!leaflet.leaflets_in_publications?.length}
229
+
published={!!leaflet.leaflets_in_publications?.find((l) => l.doc)}
230
+
publishedAt={
231
+
leaflet.leaflets_in_publications?.find((l) => l.doc)?.documents
232
+
?.indexed_at
233
+
}
234
+
leaflet_id={leaflet.root_entity}
238
235
loggedIn={!!identity}
239
236
display={display}
240
237
added_at={added_at}
···
263
260
264
261
let sortedLeaflets = leaflets.sort((a, b) => {
265
262
if (sort === "alphabetical") {
266
-
let titleA = titles[a.token.root_entity] ?? "Untitled";
267
-
let titleB = titles[b.token.root_entity] ?? "Untitled";
268
-
269
-
if (titleA === titleB) {
263
+
if (titles[a.token.root_entity] === titles[b.token.root_entity]) {
270
264
return a.added_at > b.added_at ? -1 : 1;
271
265
} else {
272
-
return titleA.toLocaleLowerCase() > titleB.toLocaleLowerCase() ? 1 : -1;
266
+
return titles[a.token.root_entity].toLocaleLowerCase() >
267
+
titles[b.token.root_entity].toLocaleLowerCase()
268
+
? 1
269
+
: -1;
273
270
}
274
271
} else {
275
272
return a.added_at === b.added_at
···
282
279
}
283
280
});
284
281
285
-
let filteredLeaflets = sortedLeaflets.filter(
286
-
({ token: leaflet, archived: archived }) => {
287
-
let published =
288
-
!!leaflet.leaflets_in_publications?.find((l) => l.doc) ||
289
-
!!leaflet.leaflets_to_documents?.find((l) => l.document);
290
-
let drafts = !!leaflet.leaflets_in_publications?.length && !published;
291
-
let docs = !leaflet.leaflets_in_publications?.length && !archived;
292
-
// If no filters are active, show all
293
-
if (
294
-
!filter.drafts &&
295
-
!filter.published &&
296
-
!filter.docs &&
297
-
!filter.archived
298
-
)
299
-
return archived === false || archived === null || archived == undefined;
282
+
let allTemplates = useTemplateState((s) => s.templates);
283
+
let filteredLeaflets = sortedLeaflets.filter(({ token: leaflet }) => {
284
+
let published = !!leaflet.leaflets_in_publications?.find((l) => l.doc);
285
+
let drafts = !!leaflet.leaflets_in_publications?.length && !published;
286
+
let docs = !leaflet.leaflets_in_publications?.length;
287
+
let templates = !!allTemplates.find((t) => t.id === leaflet.id);
288
+
// If no filters are active, show all
289
+
if (
290
+
!filter.drafts &&
291
+
!filter.published &&
292
+
!filter.docs &&
293
+
!filter.templates
294
+
)
295
+
return true;
300
296
301
-
return (
302
-
(filter.drafts && drafts) ||
303
-
(filter.published && published) ||
304
-
(filter.docs && docs) ||
305
-
(filter.archived && archived)
306
-
);
307
-
},
308
-
);
297
+
return (
298
+
(filter.drafts && drafts) ||
299
+
(filter.published && published) ||
300
+
(filter.docs && docs) ||
301
+
(filter.templates && templates)
302
+
);
303
+
});
309
304
if (searchValue === "") return filteredLeaflets;
310
305
let searchedLeaflets = filteredLeaflets.filter(({ token: leaflet }) => {
311
306
return titles[leaflet.root_entity]
+57
-29
app/(home-pages)/home/LeafletList/LeafletInfo.tsx
+57
-29
app/(home-pages)/home/LeafletList/LeafletInfo.tsx
···
1
1
"use client";
2
-
import { useEntity } from "src/replicache";
2
+
import { PermissionToken } from "src/replicache";
3
3
import { LeafletOptions } from "./LeafletOptions";
4
+
import Link from "next/link";
5
+
import { useState } from "react";
6
+
import { theme } from "tailwind.config";
7
+
import { TemplateSmall } from "components/Icons/TemplateSmall";
4
8
import { timeAgo } from "src/utils/timeAgo";
5
-
import { usePageTitle } from "components/utils/UpdateLeafletTitle";
6
-
import { useLeafletPublicationStatus } from "components/PageSWRDataProvider";
7
9
8
10
export const LeafletInfo = (props: {
9
11
title?: string;
12
+
draft?: boolean;
13
+
published?: boolean;
14
+
token: PermissionToken;
15
+
leaflet_id: string;
16
+
loggedIn: boolean;
17
+
isTemplate: boolean;
10
18
className?: string;
11
19
display: "grid" | "list";
12
20
added_at: string;
13
-
archived?: boolean | null;
14
-
loggedIn: boolean;
21
+
publishedAt?: string;
15
22
}) => {
16
-
const pubStatus = useLeafletPublicationStatus();
23
+
let [prefetch, setPrefetch] = useState(false);
17
24
let prettyCreatedAt = props.added_at ? timeAgo(props.added_at) : "";
18
-
let prettyPublishedAt = pubStatus?.publishedAt
19
-
? timeAgo(pubStatus.publishedAt)
20
-
: "";
21
25
22
-
// Look up root page first, like UpdateLeafletTitle does
23
-
let firstPage = useEntity(pubStatus?.leafletId ?? "", "root/page")[0];
24
-
let entityID = firstPage?.data.value || pubStatus?.leafletId || "";
25
-
let titleFromDb = usePageTitle(entityID);
26
-
27
-
let title = props.title ?? titleFromDb ?? "Untitled";
26
+
let prettyPublishedAt = props.publishedAt ? timeAgo(props.publishedAt) : "";
28
27
29
28
return (
30
29
<div
31
30
className={`leafletInfo w-full min-w-0 flex flex-col ${props.className}`}
32
31
>
33
32
<div className="flex justify-between items-center shrink-0 max-w-full gap-2 leading-tight overflow-hidden">
34
-
<h3 className="sm:text-lg text-base truncate w-full min-w-0">
35
-
{title}
36
-
</h3>
33
+
<Link
34
+
onMouseEnter={() => setPrefetch(true)}
35
+
onPointerDown={() => setPrefetch(true)}
36
+
prefetch={prefetch}
37
+
href={`/${props.token.id}`}
38
+
className="no-underline sm:hover:no-underline text-primary grow min-w-0"
39
+
>
40
+
<h3 className="sm:text-lg text-base truncate w-full min-w-0">
41
+
{props.title}
42
+
</h3>
43
+
</Link>
37
44
<div className="flex gap-1 shrink-0">
38
-
<LeafletOptions archived={props.archived} loggedIn={props.loggedIn} />
45
+
{props.isTemplate && props.display === "list" ? (
46
+
<TemplateSmall
47
+
fill={theme.colors["bg-page"]}
48
+
className="text-tertiary"
49
+
/>
50
+
) : null}
51
+
<LeafletOptions
52
+
leaflet={props.token}
53
+
isTemplate={props.isTemplate}
54
+
loggedIn={props.loggedIn}
55
+
added_at={props.added_at}
56
+
/>
39
57
</div>
40
58
</div>
41
-
<div className="flex gap-2 items-center">
42
-
{props.archived ? (
43
-
<div className="text-xs text-tertiary truncate">Archived</div>
44
-
) : pubStatus?.draftInPublication || pubStatus?.isPublished ? (
59
+
<Link
60
+
onMouseEnter={() => setPrefetch(true)}
61
+
onPointerDown={() => setPrefetch(true)}
62
+
prefetch={prefetch}
63
+
href={`/${props.token.id}`}
64
+
className="no-underline sm:hover:no-underline text-primary w-full"
65
+
>
66
+
{props.draft || props.published ? (
45
67
<div
46
-
className={`text-xs w-max grow truncate ${pubStatus?.isPublished ? "font-bold text-tertiary" : "text-tertiary"}`}
68
+
className={`text-xs ${props.published ? "font-bold text-tertiary" : "text-tertiary"}`}
47
69
>
48
-
{pubStatus?.isPublished
70
+
{props.published
49
71
? `Published ${prettyPublishedAt}`
50
72
: `Draft ${prettyCreatedAt}`}
51
73
</div>
52
74
) : (
53
-
<div className="text-xs text-tertiary grow w-max truncate">
54
-
{prettyCreatedAt}
55
-
</div>
75
+
<div className="text-xs text-tertiary">{prettyCreatedAt}</div>
56
76
)}
57
-
</div>
77
+
</Link>
78
+
{props.isTemplate && props.display === "grid" ? (
79
+
<div className="absolute -top-2 right-1">
80
+
<TemplateSmall
81
+
className="text-tertiary"
82
+
fill={theme.colors["bg-page"]}
83
+
/>
84
+
</div>
85
+
) : null}
58
86
</div>
59
87
);
60
88
};
+26
-45
app/(home-pages)/home/LeafletList/LeafletListItem.tsx
+26
-45
app/(home-pages)/home/LeafletList/LeafletListItem.tsx
···
1
1
"use client";
2
+
import { PermissionToken } from "src/replicache";
3
+
import { useTemplateState } from "../Actions/CreateNewButton";
2
4
import { LeafletListPreview, LeafletGridPreview } from "./LeafletPreview";
3
5
import { LeafletInfo } from "./LeafletInfo";
4
6
import { useState, useRef, useEffect } from "react";
5
-
import { SpeedyLink } from "components/SpeedyLink";
6
-
import { useLeafletPublicationStatus } from "components/PageSWRDataProvider";
7
7
8
8
export const LeafletListItem = (props: {
9
-
archived?: boolean | null;
9
+
token: PermissionToken;
10
+
leaflet_id: string;
10
11
loggedIn: boolean;
11
12
display: "list" | "grid";
12
13
cardBorderHidden: boolean;
13
14
added_at: string;
14
-
title?: string;
15
+
title: string;
16
+
draft?: boolean;
17
+
published?: boolean;
18
+
publishedAt?: string;
15
19
index: number;
16
20
isHidden: boolean;
17
21
showPreview?: boolean;
18
22
}) => {
19
-
const pubStatus = useLeafletPublicationStatus();
23
+
let isTemplate = useTemplateState(
24
+
(s) => !!s.templates.find((t) => t.id === props.token.id),
25
+
);
26
+
20
27
let [isOnScreen, setIsOnScreen] = useState(props.index < 16 ? true : false);
21
28
let previewRef = useRef<HTMLDivElement | null>(null);
22
29
···
38
45
return () => observer.disconnect();
39
46
}, [previewRef]);
40
47
41
-
const tokenId = pubStatus?.shareLink ?? "";
42
-
43
48
if (props.display === "list")
44
49
return (
45
50
<>
46
51
<div
47
52
ref={previewRef}
48
-
className={`relative flex gap-3 w-full
49
-
${props.isHidden ? "hidden" : "flex"}
50
-
${props.cardBorderHidden ? "" : "px-2 py-1 block-border hover:outline-border relative"}`}
53
+
className={`gap-3 w-full ${props.cardBorderHidden ? "" : "px-2 py-1 block-border hover:outline-border"}`}
51
54
style={{
52
55
backgroundColor: props.cardBorderHidden
53
56
? "transparent"
54
57
: "rgba(var(--bg-page), var(--bg-page-alpha))",
58
+
59
+
display: props.isHidden ? "none" : "flex",
55
60
}}
56
61
>
57
-
<SpeedyLink
58
-
href={`/${tokenId}`}
59
-
className={`absolute w-full h-full top-0 left-0 no-underline hover:no-underline! text-primary`}
60
-
/>
61
-
{props.showPreview && <LeafletListPreview isVisible={isOnScreen} />}
62
-
<LeafletInfo
63
-
title={props.title}
64
-
display={props.display}
65
-
added_at={props.added_at}
66
-
archived={props.archived}
67
-
loggedIn={props.loggedIn}
68
-
/>
62
+
{props.showPreview && (
63
+
<LeafletListPreview isVisible={isOnScreen} {...props} />
64
+
)}
65
+
<LeafletInfo isTemplate={isTemplate} {...props} />
69
66
</div>
70
67
{props.cardBorderHidden && (
71
68
<hr
···
80
77
return (
81
78
<div
82
79
ref={previewRef}
83
-
className={`
84
-
relative
85
-
flex flex-col gap-1 p-1 h-52 w-full
80
+
className={`leafletGridListItem relative
81
+
flex flex-col gap-1 p-1 h-52
86
82
block-border border-border! hover:outline-border
87
-
${props.isHidden ? "hidden" : "flex"}
88
83
`}
89
84
style={{
90
85
backgroundColor: props.cardBorderHidden
91
86
? "transparent"
92
87
: "rgba(var(--bg-page), var(--bg-page-alpha))",
88
+
89
+
display: props.isHidden ? "none" : "flex",
93
90
}}
94
91
>
95
-
<SpeedyLink
96
-
href={`/${tokenId}`}
97
-
className={`absolute w-full h-full top-0 left-0 no-underline hover:no-underline! text-primary`}
98
-
/>
99
92
<div className="grow">
100
-
<LeafletGridPreview isVisible={isOnScreen} />
93
+
<LeafletGridPreview {...props} isVisible={isOnScreen} />
101
94
</div>
102
95
<LeafletInfo
96
+
isTemplate={isTemplate}
103
97
className="px-1 pb-0.5 shrink-0"
104
-
title={props.title}
105
-
display={props.display}
106
-
added_at={props.added_at}
107
-
archived={props.archived}
108
-
loggedIn={props.loggedIn}
98
+
{...props}
109
99
/>
110
100
</div>
111
101
);
112
102
};
113
-
114
-
const LeafletLink = (props: { id: string; className: string }) => {
115
-
return (
116
-
<SpeedyLink
117
-
href={`/${props.id}`}
118
-
className={`no-underline hover:no-underline! text-primary ${props.className}`}
119
-
/>
120
-
);
121
-
};
+170
-293
app/(home-pages)/home/LeafletList/LeafletOptions.tsx
+170
-293
app/(home-pages)/home/LeafletList/LeafletOptions.tsx
···
1
1
"use client";
2
2
3
3
import { Menu, MenuItem } from "components/Layout";
4
+
import { useReplicache, type PermissionToken } from "src/replicache";
5
+
import { hideDoc } from "../storage";
4
6
import { useState } from "react";
5
-
import { ButtonPrimary, ButtonTertiary } from "components/Buttons";
6
-
import { useToaster } from "components/Toast";
7
-
import { MoreOptionsVerticalTiny } from "components/Icons/MoreOptionsVerticalTiny";
8
-
import { DeleteSmall } from "components/Icons/DeleteSmall";
9
-
import {
10
-
archivePost,
11
-
deleteLeaflet,
12
-
unarchivePost,
13
-
} from "actions/deleteLeaflet";
14
-
import { ArchiveSmall } from "components/Icons/ArchiveSmall";
15
-
import { UnpublishSmall } from "components/Icons/UnpublishSmall";
16
-
import {
17
-
deletePost,
18
-
unpublishPost,
19
-
} from "app/lish/[did]/[publication]/dashboard/deletePost";
20
-
import { ShareSmall } from "components/Icons/ShareSmall";
7
+
import { ButtonPrimary } from "components/Buttons";
8
+
import { useTemplateState } from "../Actions/CreateNewButton";
9
+
import { useSmoker, useToaster } from "components/Toast";
10
+
import { removeLeafletFromHome } from "actions/removeLeafletFromHome";
11
+
import { useIdentityData } from "components/IdentityProvider";
21
12
import { HideSmall } from "components/Icons/HideSmall";
22
-
import { hideDoc } from "../storage";
23
-
24
-
import {
25
-
useIdentityData,
26
-
mutateIdentityData,
27
-
} from "components/IdentityProvider";
28
-
import {
29
-
usePublicationData,
30
-
mutatePublicationData,
31
-
} from "app/lish/[did]/[publication]/dashboard/PublicationSWRProvider";
32
-
import { ShareButton } from "app/[leaflet_id]/actions/ShareOptions";
33
-
import { useLeafletPublicationStatus } from "components/PageSWRDataProvider";
13
+
import { MoreOptionsTiny } from "components/Icons/MoreOptionsTiny";
14
+
import { TemplateRemoveSmall } from "components/Icons/TemplateRemoveSmall";
15
+
import { TemplateSmall } from "components/Icons/TemplateSmall";
16
+
import { MoreOptionsVerticalTiny } from "components/Icons/MoreOptionsVerticalTiny";
17
+
import { addLeafletToHome } from "actions/addLeafletToHome";
34
18
35
19
export const LeafletOptions = (props: {
36
-
archived?: boolean | null;
37
-
loggedIn?: boolean;
20
+
leaflet: PermissionToken;
21
+
isTemplate: boolean;
22
+
loggedIn: boolean;
23
+
added_at: string;
38
24
}) => {
39
-
const pubStatus = useLeafletPublicationStatus();
40
-
let [state, setState] = useState<"normal" | "areYouSure">("normal");
25
+
let { mutate: mutateIdentity } = useIdentityData();
26
+
let [state, setState] = useState<"normal" | "template">("normal");
41
27
let [open, setOpen] = useState(false);
42
-
let { identity } = useIdentityData();
43
-
let isPublicationOwner =
44
-
!!identity?.atp_did && !!pubStatus?.documentUri?.includes(identity.atp_did);
28
+
let smoker = useSmoker();
29
+
let toaster = useToaster();
45
30
return (
46
31
<>
47
32
<Menu
···
53
38
}}
54
39
trigger={
55
40
<div
56
-
className="text-secondary shrink-0 relative"
41
+
className="text-secondary shrink-0"
57
42
onClick={(e) => {
58
43
e.preventDefault;
59
44
e.stopPropagation;
···
64
49
}
65
50
>
66
51
{state === "normal" ? (
67
-
!props.loggedIn ? (
68
-
<LoggedOutOptions setState={setState} />
69
-
) : pubStatus?.documentUri && isPublicationOwner ? (
70
-
<PublishedPostOptions setState={setState} />
71
-
) : (
72
-
<DefaultOptions setState={setState} archived={props.archived} />
73
-
)
74
-
) : state === "areYouSure" ? (
75
-
<DeleteAreYouSureForm backToMenu={() => setState("normal")} />
52
+
<>
53
+
{!props.isTemplate ? (
54
+
<MenuItem
55
+
onSelect={(e) => {
56
+
e.preventDefault();
57
+
setState("template");
58
+
}}
59
+
>
60
+
<TemplateSmall /> Add as Template
61
+
</MenuItem>
62
+
) : (
63
+
<MenuItem
64
+
onSelect={(e) => {
65
+
useTemplateState.getState().removeTemplate(props.leaflet);
66
+
let newLeafletButton =
67
+
document.getElementById("new-leaflet-button");
68
+
if (!newLeafletButton) return;
69
+
let rect = newLeafletButton.getBoundingClientRect();
70
+
smoker({
71
+
static: true,
72
+
text: <strong>Removed template!</strong>,
73
+
position: {
74
+
y: rect.top,
75
+
x: rect.right + 5,
76
+
},
77
+
});
78
+
}}
79
+
>
80
+
<TemplateRemoveSmall /> Remove from Templates
81
+
</MenuItem>
82
+
)}
83
+
<MenuItem
84
+
onSelect={async () => {
85
+
if (props.loggedIn) {
86
+
mutateIdentity(
87
+
(s) => {
88
+
if (!s) return s;
89
+
return {
90
+
...s,
91
+
permission_token_on_homepage:
92
+
s.permission_token_on_homepage.filter(
93
+
(ptrh) =>
94
+
ptrh.permission_tokens.id !== props.leaflet.id,
95
+
),
96
+
};
97
+
},
98
+
{ revalidate: false },
99
+
);
100
+
await removeLeafletFromHome([props.leaflet.id]);
101
+
mutateIdentity();
102
+
} else {
103
+
hideDoc(props.leaflet);
104
+
}
105
+
toaster({
106
+
content: (
107
+
<div className="font-bold">
108
+
Doc removed!{" "}
109
+
<UndoRemoveFromHomeButton
110
+
leaflet={props.leaflet}
111
+
added_at={props.added_at}
112
+
/>
113
+
</div>
114
+
),
115
+
type: "success",
116
+
});
117
+
}}
118
+
>
119
+
<HideSmall />
120
+
Remove from Home
121
+
</MenuItem>
122
+
</>
123
+
) : state === "template" ? (
124
+
<AddTemplateForm
125
+
leaflet={props.leaflet}
126
+
close={() => setOpen(false)}
127
+
/>
76
128
) : null}
77
129
</Menu>
78
130
</>
79
131
);
80
132
};
81
133
82
-
const DefaultOptions = (props: {
83
-
setState: (s: "areYouSure") => void;
84
-
archived?: boolean | null;
134
+
const UndoRemoveFromHomeButton = (props: {
135
+
leaflet: PermissionToken;
136
+
added_at: string | undefined;
85
137
}) => {
86
-
const pubStatus = useLeafletPublicationStatus();
87
-
const toaster = useToaster();
88
-
const { setArchived } = useArchiveMutations();
89
-
const tokenId = pubStatus?.token.id;
90
-
const itemType = pubStatus?.draftInPublication ? "Draft" : "Leaflet";
91
-
138
+
let toaster = useToaster();
139
+
let { mutate } = useIdentityData();
92
140
return (
93
-
<>
94
-
<EditLinkShareButton link={pubStatus?.shareLink ?? ""} />
95
-
<hr className="border-border-light" />
96
-
<MenuItem
97
-
onSelect={async () => {
98
-
if (!tokenId) return;
99
-
setArchived(tokenId, !props.archived);
141
+
<button
142
+
onClick={async (e) => {
143
+
await mutate(
144
+
(identity) => {
145
+
if (!identity) return;
146
+
return {
147
+
...identity,
148
+
permission_token_on_homepage: [
149
+
...identity.permission_token_on_homepage,
150
+
{
151
+
created_at: props.added_at || new Date().toISOString(),
152
+
permission_tokens: {
153
+
...props.leaflet,
154
+
leaflets_in_publications: [],
155
+
},
156
+
},
157
+
],
158
+
};
159
+
},
160
+
{ revalidate: false },
161
+
);
162
+
await addLeafletToHome(props.leaflet.id);
163
+
await mutate();
100
164
101
-
if (!props.archived) {
102
-
await archivePost(tokenId);
103
-
toaster({
104
-
content: (
105
-
<div className="font-bold flex gap-2">
106
-
Archived {itemType}!
107
-
<ButtonTertiary
108
-
className="underline text-accent-2!"
109
-
onClick={async () => {
110
-
setArchived(tokenId, false);
111
-
await unarchivePost(tokenId);
112
-
toaster({
113
-
content: <div className="font-bold">Unarchived!</div>,
114
-
type: "success",
115
-
});
116
-
}}
117
-
>
118
-
Undo?
119
-
</ButtonTertiary>
120
-
</div>
121
-
),
122
-
type: "success",
123
-
});
124
-
} else {
125
-
await unarchivePost(tokenId);
126
-
toaster({
127
-
content: <div className="font-bold">Unarchived!</div>,
128
-
type: "success",
129
-
});
130
-
}
131
-
}}
132
-
>
133
-
<ArchiveSmall />
134
-
{!props.archived ? " Archive" : "Unarchive"} {itemType}
135
-
</MenuItem>
136
-
<DeleteForeverMenuItem
137
-
onSelect={(e) => {
138
-
e.preventDefault();
139
-
props.setState("areYouSure");
140
-
}}
141
-
/>
142
-
</>
165
+
toaster({
166
+
content: <div className="font-bold">Recovered Doc!</div>,
167
+
type: "success",
168
+
});
169
+
}}
170
+
className="underline"
171
+
>
172
+
Undo?
173
+
</button>
143
174
);
144
175
};
145
176
146
-
const LoggedOutOptions = (props: { setState: (s: "areYouSure") => void }) => {
147
-
const pubStatus = useLeafletPublicationStatus();
148
-
const toaster = useToaster();
149
-
150
-
return (
151
-
<>
152
-
<EditLinkShareButton link={`/${pubStatus?.shareLink ?? ""}`} />
153
-
<hr className="border-border-light" />
154
-
<MenuItem
155
-
onSelect={() => {
156
-
if (pubStatus?.token) hideDoc(pubStatus.token);
157
-
toaster({
158
-
content: <div className="font-bold">Removed from Home!</div>,
159
-
type: "success",
160
-
});
161
-
}}
162
-
>
163
-
<HideSmall />
164
-
Remove from Home
165
-
</MenuItem>
166
-
<DeleteForeverMenuItem
167
-
onSelect={(e) => {
168
-
e.preventDefault();
169
-
props.setState("areYouSure");
170
-
}}
171
-
/>
172
-
</>
173
-
);
174
-
};
175
-
176
-
const PublishedPostOptions = (props: {
177
-
setState: (s: "areYouSure") => void;
177
+
const AddTemplateForm = (props: {
178
+
leaflet: PermissionToken;
179
+
close: () => void;
178
180
}) => {
179
-
const pubStatus = useLeafletPublicationStatus();
180
-
const toaster = useToaster();
181
-
const postLink = pubStatus?.postShareLink ?? "";
182
-
const isFullUrl = postLink.includes("http");
183
-
181
+
let [name, setName] = useState("");
182
+
let smoker = useSmoker();
184
183
return (
185
-
<>
186
-
<ShareButton
187
-
text={
188
-
<div className="flex gap-2">
189
-
<ShareSmall />
190
-
Copy Post Link
191
-
</div>
192
-
}
193
-
smokerText="Link copied!"
194
-
id="get-link"
195
-
link={postLink}
196
-
fullLink={isFullUrl ? postLink : undefined}
197
-
/>
198
-
<hr className="border-border-light" />
199
-
<MenuItem
200
-
onSelect={async () => {
201
-
if (pubStatus?.documentUri) {
202
-
await unpublishPost(pubStatus.documentUri);
203
-
}
204
-
toaster({
205
-
content: <div className="font-bold">Unpublished Post!</div>,
206
-
type: "success",
184
+
<div className="flex flex-col gap-2 px-3 py-1">
185
+
<label className="font-bold flex flex-col gap-1 text-secondary">
186
+
Template Name
187
+
<input
188
+
value={name}
189
+
onChange={(e) => setName(e.target.value)}
190
+
type="text"
191
+
className=" text-primary font-normal border border-border rounded-md outline-hidden px-2 py-1 w-64"
192
+
/>
193
+
</label>
194
+
195
+
<ButtonPrimary
196
+
onClick={() => {
197
+
useTemplateState.getState().addTemplate({
198
+
name,
199
+
id: props.leaflet.id,
207
200
});
201
+
let newLeafletButton = document.getElementById("new-leaflet-button");
202
+
if (!newLeafletButton) return;
203
+
let rect = newLeafletButton.getBoundingClientRect();
204
+
smoker({
205
+
static: true,
206
+
text: <strong>Added {name}!</strong>,
207
+
position: {
208
+
y: rect.top,
209
+
x: rect.right + 5,
210
+
},
211
+
});
212
+
props.close();
208
213
}}
214
+
className="place-self-end"
209
215
>
210
-
<UnpublishSmall />
211
-
<div className="flex flex-col">
212
-
Unpublish Post
213
-
<div className="text-tertiary text-sm font-normal!">
214
-
Move this post back into drafts
215
-
</div>
216
-
</div>
217
-
</MenuItem>
218
-
<DeleteForeverMenuItem
219
-
onSelect={(e) => {
220
-
e.preventDefault();
221
-
props.setState("areYouSure");
222
-
}}
223
-
subtext="Post"
224
-
/>
225
-
</>
226
-
);
227
-
};
228
-
229
-
const DeleteAreYouSureForm = (props: { backToMenu: () => void }) => {
230
-
const pubStatus = useLeafletPublicationStatus();
231
-
const toaster = useToaster();
232
-
const { removeFromLists } = useArchiveMutations();
233
-
const tokenId = pubStatus?.token.id;
234
-
235
-
const itemType = pubStatus?.documentUri
236
-
? "Post"
237
-
: pubStatus?.draftInPublication
238
-
? "Draft"
239
-
: "Leaflet";
240
-
241
-
return (
242
-
<div className="flex flex-col justify-center p-2 text-center">
243
-
<div className="text-primary font-bold"> Are you sure?</div>
244
-
<div className="text-sm text-secondary">
245
-
This will delete it forever for everyone!
246
-
</div>
247
-
<div className="flex gap-2 mx-auto items-center mt-2">
248
-
<ButtonTertiary onClick={() => props.backToMenu()}>
249
-
Nevermind
250
-
</ButtonTertiary>
251
-
<ButtonPrimary
252
-
onClick={async () => {
253
-
if (tokenId) removeFromLists(tokenId);
254
-
if (pubStatus?.documentUri) {
255
-
await deletePost(pubStatus.documentUri);
256
-
}
257
-
if (pubStatus?.token) deleteLeaflet(pubStatus.token);
258
-
259
-
toaster({
260
-
content: <div className="font-bold">Deleted {itemType}!</div>,
261
-
type: "success",
262
-
});
263
-
}}
264
-
>
265
-
Delete it!
266
-
</ButtonPrimary>
267
-
</div>
216
+
Add Template
217
+
</ButtonPrimary>
268
218
</div>
269
219
);
270
220
};
271
-
272
-
// Shared menu items
273
-
const EditLinkShareButton = (props: { link: string }) => (
274
-
<ShareButton
275
-
text={
276
-
<div className="flex gap-2">
277
-
<ShareSmall />
278
-
Copy Edit Link
279
-
</div>
280
-
}
281
-
subtext=""
282
-
smokerText="Link copied!"
283
-
id="get-link"
284
-
link={props.link}
285
-
/>
286
-
);
287
-
288
-
const DeleteForeverMenuItem = (props: {
289
-
onSelect: (e: Event) => void;
290
-
subtext?: string;
291
-
}) => (
292
-
<MenuItem onSelect={props.onSelect}>
293
-
<DeleteSmall />
294
-
{props.subtext ? (
295
-
<div className="flex flex-col">
296
-
Delete {props.subtext}
297
-
<div className="text-tertiary text-sm font-normal!">
298
-
Unpublish AND delete
299
-
</div>
300
-
</div>
301
-
) : (
302
-
"Delete Forever"
303
-
)}
304
-
</MenuItem>
305
-
);
306
-
307
-
// Helper to update archived state in both identity and publication data
308
-
function useArchiveMutations() {
309
-
const { mutate: mutatePub } = usePublicationData();
310
-
const { mutate: mutateIdentity } = useIdentityData();
311
-
312
-
return {
313
-
setArchived: (tokenId: string, archived: boolean) => {
314
-
mutateIdentityData(mutateIdentity, (data) => {
315
-
const item = data.permission_token_on_homepage.find(
316
-
(p) => p.permission_tokens?.id === tokenId,
317
-
);
318
-
if (item) item.archived = archived;
319
-
});
320
-
mutatePublicationData(mutatePub, (data) => {
321
-
const item = data.publication?.leaflets_in_publications.find(
322
-
(l) => l.permission_tokens?.id === tokenId,
323
-
);
324
-
if (item) item.archived = archived;
325
-
});
326
-
},
327
-
removeFromLists: (tokenId: string) => {
328
-
mutateIdentityData(mutateIdentity, (data) => {
329
-
data.permission_token_on_homepage =
330
-
data.permission_token_on_homepage.filter(
331
-
(p) => p.permission_tokens?.id !== tokenId,
332
-
);
333
-
});
334
-
mutatePublicationData(mutatePub, (data) => {
335
-
if (!data.publication) return;
336
-
data.publication.leaflets_in_publications =
337
-
data.publication.leaflets_in_publications.filter(
338
-
(l) => l.permission_tokens?.id !== tokenId,
339
-
);
340
-
});
341
-
},
342
-
};
343
-
}
+112
-49
app/(home-pages)/home/LeafletList/LeafletPreview.tsx
+112
-49
app/(home-pages)/home/LeafletList/LeafletPreview.tsx
···
3
3
ThemeBackgroundProvider,
4
4
ThemeProvider,
5
5
} from "components/ThemeManager/ThemeProvider";
6
-
import { useEntity, useReferenceToEntity } from "src/replicache";
6
+
import {
7
+
PermissionToken,
8
+
useEntity,
9
+
useReferenceToEntity,
10
+
} from "src/replicache";
11
+
import { useTemplateState } from "../Actions/CreateNewButton";
7
12
import { useCardBorderHidden } from "components/Pages/useCardBorderHidden";
8
13
import { LeafletContent } from "./LeafletContent";
9
14
import { Tooltip } from "components/Tooltip";
10
-
import { useLeafletPublicationStatus } from "components/PageSWRDataProvider";
11
-
import { CSSProperties } from "react";
15
+
import { useState } from "react";
16
+
import Link from "next/link";
17
+
import { SpeedyLink } from "components/SpeedyLink";
12
18
13
-
function useLeafletPreviewData() {
14
-
const pubStatus = useLeafletPublicationStatus();
15
-
const leafletId = pubStatus?.leafletId ?? "";
16
-
const root =
17
-
useReferenceToEntity("root/page", leafletId)[0]?.entity || leafletId;
18
-
const firstPage = useEntity(root, "root/page")[0];
19
-
const page = firstPage?.data.value || root;
19
+
export const LeafletListPreview = (props: {
20
+
draft?: boolean;
21
+
published?: boolean;
22
+
isVisible: boolean;
23
+
token: PermissionToken;
24
+
leaflet_id: string;
25
+
loggedIn: boolean;
26
+
}) => {
27
+
let root =
28
+
useReferenceToEntity("root/page", props.leaflet_id)[0]?.entity ||
29
+
props.leaflet_id;
30
+
let firstPage = useEntity(root, "root/page")[0];
31
+
let page = firstPage?.data.value || root;
20
32
21
-
const cardBorderHidden = useCardBorderHidden(root);
22
-
const rootBackgroundImage = useEntity(root, "theme/card-background-image");
23
-
const rootBackgroundRepeat = useEntity(
33
+
let cardBorderHidden = useCardBorderHidden(root);
34
+
let rootBackgroundImage = useEntity(root, "theme/card-background-image");
35
+
let rootBackgroundRepeat = useEntity(
24
36
root,
25
37
"theme/card-background-image-repeat",
26
38
);
27
-
const rootBackgroundOpacity = useEntity(
39
+
let rootBackgroundOpacity = useEntity(
28
40
root,
29
41
"theme/card-background-image-opacity",
30
42
);
31
43
32
-
const contentWrapperStyle: CSSProperties = cardBorderHidden
33
-
? {}
34
-
: {
35
-
backgroundImage: rootBackgroundImage
36
-
? `url(${rootBackgroundImage.data.src}), url(${rootBackgroundImage.data.fallback})`
37
-
: undefined,
38
-
backgroundRepeat: rootBackgroundRepeat ? "repeat" : "no-repeat",
39
-
backgroundPosition: "center",
40
-
backgroundSize: !rootBackgroundRepeat
41
-
? "cover"
42
-
: rootBackgroundRepeat?.data.value / 3,
43
-
opacity:
44
-
rootBackgroundImage?.data.src && rootBackgroundOpacity
45
-
? rootBackgroundOpacity.data.value
46
-
: 1,
47
-
backgroundColor: "rgba(var(--bg-page), var(--bg-page-alpha))",
48
-
};
49
-
50
-
const contentWrapperClass = `leafletContentWrapper h-full sm:w-48 w-40 mx-auto overflow-clip ${!cardBorderHidden && "border border-border-light border-b-0 rounded-t-md"}`;
51
-
52
-
return { root, page, cardBorderHidden, contentWrapperStyle, contentWrapperClass };
53
-
}
54
-
55
-
export const LeafletListPreview = (props: { isVisible: boolean }) => {
56
-
const { root, page, cardBorderHidden, contentWrapperStyle, contentWrapperClass } =
57
-
useLeafletPreviewData();
58
-
59
44
return (
60
45
<Tooltip
61
46
open={true}
···
92
77
<ThemeProvider local entityID={root} className="rounded-sm">
93
78
<ThemeBackgroundProvider entityID={root}>
94
79
<div className="leafletPreview grow shrink-0 h-44 w-64 px-2 pt-2 sm:px-3 sm:pt-3 flex items-end pointer-events-none rounded-[2px] ">
95
-
<div className={contentWrapperClass} style={contentWrapperStyle}>
80
+
<div
81
+
className={`leafletContentWrapper h-full sm:w-48 w-40 mx-auto overflow-clip ${!cardBorderHidden && "border border-border-light border-b-0 rounded-t-md"}`}
82
+
style={
83
+
cardBorderHidden
84
+
? {}
85
+
: {
86
+
backgroundImage: rootBackgroundImage
87
+
? `url(${rootBackgroundImage.data.src}), url(${rootBackgroundImage.data.fallback})`
88
+
: undefined,
89
+
backgroundRepeat: rootBackgroundRepeat
90
+
? "repeat"
91
+
: "no-repeat",
92
+
backgroundPosition: "center",
93
+
backgroundSize: !rootBackgroundRepeat
94
+
? "cover"
95
+
: rootBackgroundRepeat?.data.value / 3,
96
+
opacity:
97
+
rootBackgroundImage?.data.src && rootBackgroundOpacity
98
+
? rootBackgroundOpacity.data.value
99
+
: 1,
100
+
backgroundColor:
101
+
"rgba(var(--bg-page), var(--bg-page-alpha))",
102
+
}
103
+
}
104
+
>
96
105
<LeafletContent entityID={page} isOnScreen={props.isVisible} />
97
106
</div>
98
107
</div>
···
102
111
);
103
112
};
104
113
105
-
export const LeafletGridPreview = (props: { isVisible: boolean }) => {
106
-
const { root, page, contentWrapperStyle, contentWrapperClass } =
107
-
useLeafletPreviewData();
114
+
export const LeafletGridPreview = (props: {
115
+
draft?: boolean;
116
+
published?: boolean;
117
+
token: PermissionToken;
118
+
leaflet_id: string;
119
+
loggedIn: boolean;
120
+
isVisible: boolean;
121
+
}) => {
122
+
let root =
123
+
useReferenceToEntity("root/page", props.leaflet_id)[0]?.entity ||
124
+
props.leaflet_id;
125
+
let firstPage = useEntity(root, "root/page")[0];
126
+
let page = firstPage?.data.value || root;
108
127
128
+
let cardBorderHidden = useCardBorderHidden(root);
129
+
let rootBackgroundImage = useEntity(root, "theme/card-background-image");
130
+
let rootBackgroundRepeat = useEntity(
131
+
root,
132
+
"theme/card-background-image-repeat",
133
+
);
134
+
let rootBackgroundOpacity = useEntity(
135
+
root,
136
+
"theme/card-background-image-opacity",
137
+
);
109
138
return (
110
139
<ThemeProvider local entityID={root} className="w-full!">
111
-
<div className="border border-border-light rounded-md w-full h-full overflow-hidden ">
112
-
<div className="w-full h-full">
140
+
<div className="border border-border-light rounded-md w-full h-full overflow-hidden relative">
141
+
<div className="relative w-full h-full">
113
142
<ThemeBackgroundProvider entityID={root}>
114
143
<div
115
144
inert
116
-
className="leafletPreview grow shrink-0 h-full w-full px-2 pt-2 sm:px-3 sm:pt-3 flex items-end pointer-events-none"
145
+
className="leafletPreview relative grow shrink-0 h-full w-full px-2 pt-2 sm:px-3 sm:pt-3 flex items-end pointer-events-none"
117
146
>
118
-
<div className={contentWrapperClass} style={contentWrapperStyle}>
147
+
<div
148
+
className={`leafletContentWrapper h-full sm:w-48 w-40 mx-auto overflow-clip ${!cardBorderHidden && "border border-border-light border-b-0 rounded-t-md"}`}
149
+
style={
150
+
cardBorderHidden
151
+
? {}
152
+
: {
153
+
backgroundImage: rootBackgroundImage
154
+
? `url(${rootBackgroundImage.data.src}), url(${rootBackgroundImage.data.fallback})`
155
+
: undefined,
156
+
backgroundRepeat: rootBackgroundRepeat
157
+
? "repeat"
158
+
: "no-repeat",
159
+
backgroundPosition: "center",
160
+
backgroundSize: !rootBackgroundRepeat
161
+
? "cover"
162
+
: rootBackgroundRepeat?.data.value / 3,
163
+
opacity:
164
+
rootBackgroundImage?.data.src && rootBackgroundOpacity
165
+
? rootBackgroundOpacity.data.value
166
+
: 1,
167
+
backgroundColor:
168
+
"rgba(var(--bg-page), var(--bg-page-alpha))",
169
+
}
170
+
}
171
+
>
119
172
<LeafletContent entityID={page} isOnScreen={props.isVisible} />
120
173
</div>
121
174
</div>
122
175
</ThemeBackgroundProvider>
123
176
</div>
177
+
<LeafletPreviewLink id={props.token.id} />
124
178
</div>
125
179
</ThemeProvider>
126
180
);
127
181
};
182
+
183
+
const LeafletPreviewLink = (props: { id: string }) => {
184
+
return (
185
+
<SpeedyLink
186
+
href={`/${props.id}`}
187
+
className={`hello no-underline sm:hover:no-underline text-primary absolute inset-0 w-full h-full bg-bg-test`}
188
+
/>
189
+
);
190
+
};
+1
-2
app/(home-pages)/home/page.tsx
+1
-2
app/(home-pages)/home/page.tsx
···
29
29
...auth_res?.permission_token_on_homepage.reduce(
30
30
(acc, tok) => {
31
31
let title =
32
-
tok.permission_tokens.leaflets_in_publications[0]?.title ||
33
-
tok.permission_tokens.leaflets_to_documents[0]?.title;
32
+
tok.permission_tokens.leaflets_in_publications[0]?.title;
34
33
if (title) acc[tok.permission_tokens.root_entity] = title;
35
34
return acc;
36
35
},
-116
app/(home-pages)/looseleafs/LooseleafsLayout.tsx
-116
app/(home-pages)/looseleafs/LooseleafsLayout.tsx
···
1
-
"use client";
2
-
import { DashboardLayout } from "components/PageLayouts/DashboardLayout";
3
-
import { useCardBorderHidden } from "components/Pages/useCardBorderHidden";
4
-
import { useState } from "react";
5
-
import { useDebouncedEffect } from "src/hooks/useDebouncedEffect";
6
-
import { Fact, PermissionToken } from "src/replicache";
7
-
import { Attribute } from "src/replicache/attributes";
8
-
import { Actions } from "../home/Actions/Actions";
9
-
import { callRPC } from "app/api/rpc/client";
10
-
import { useIdentityData } from "components/IdentityProvider";
11
-
import useSWR from "swr";
12
-
import { getHomeDocs } from "../home/storage";
13
-
import { Leaflet, LeafletList } from "../home/HomeLayout";
14
-
15
-
export const LooseleafsLayout = (props: {
16
-
entityID: string | null;
17
-
titles: { [root_entity: string]: string };
18
-
initialFacts: {
19
-
[root_entity: string]: Fact<Attribute>[];
20
-
};
21
-
}) => {
22
-
let [searchValue, setSearchValue] = useState("");
23
-
let [debouncedSearchValue, setDebouncedSearchValue] = useState("");
24
-
25
-
useDebouncedEffect(
26
-
() => {
27
-
setDebouncedSearchValue(searchValue);
28
-
},
29
-
200,
30
-
[searchValue],
31
-
);
32
-
33
-
let cardBorderHidden = !!useCardBorderHidden(props.entityID);
34
-
return (
35
-
<DashboardLayout
36
-
id="looseleafs"
37
-
cardBorderHidden={cardBorderHidden}
38
-
currentPage="looseleafs"
39
-
defaultTab="home"
40
-
actions={<Actions />}
41
-
tabs={{
42
-
home: {
43
-
controls: null,
44
-
content: (
45
-
<LooseleafList
46
-
titles={props.titles}
47
-
initialFacts={props.initialFacts}
48
-
cardBorderHidden={cardBorderHidden}
49
-
searchValue={debouncedSearchValue}
50
-
/>
51
-
),
52
-
},
53
-
}}
54
-
/>
55
-
);
56
-
};
57
-
58
-
export const LooseleafList = (props: {
59
-
titles: { [root_entity: string]: string };
60
-
initialFacts: {
61
-
[root_entity: string]: Fact<Attribute>[];
62
-
};
63
-
searchValue: string;
64
-
cardBorderHidden: boolean;
65
-
}) => {
66
-
let { identity } = useIdentityData();
67
-
let { data: initialFacts } = useSWR(
68
-
"home-leaflet-data",
69
-
async () => {
70
-
if (identity) {
71
-
let { result } = await callRPC("getFactsFromHomeLeaflets", {
72
-
tokens: identity.permission_token_on_homepage.map(
73
-
(ptrh) => ptrh.permission_tokens.root_entity,
74
-
),
75
-
});
76
-
let titles = {
77
-
...result.titles,
78
-
...identity.permission_token_on_homepage.reduce(
79
-
(acc, tok) => {
80
-
let title =
81
-
tok.permission_tokens.leaflets_in_publications[0]?.title ||
82
-
tok.permission_tokens.leaflets_to_documents[0]?.title;
83
-
if (title) acc[tok.permission_tokens.root_entity] = title;
84
-
return acc;
85
-
},
86
-
{} as { [k: string]: string },
87
-
),
88
-
};
89
-
return { ...result, titles };
90
-
}
91
-
},
92
-
{ fallbackData: { facts: props.initialFacts, titles: props.titles } },
93
-
);
94
-
95
-
let leaflets: Leaflet[] = identity
96
-
? identity.permission_token_on_homepage
97
-
.filter(
98
-
(ptoh) => ptoh.permission_tokens.leaflets_to_documents.length > 0,
99
-
)
100
-
.map((ptoh) => ({
101
-
added_at: ptoh.created_at,
102
-
token: ptoh.permission_tokens as PermissionToken,
103
-
}))
104
-
: [];
105
-
return (
106
-
<LeafletList
107
-
defaultDisplay="list"
108
-
searchValue={props.searchValue}
109
-
leaflets={leaflets}
110
-
titles={initialFacts?.titles || {}}
111
-
cardBorderHidden={props.cardBorderHidden}
112
-
initialFacts={initialFacts?.facts || {}}
113
-
showPreview
114
-
/>
115
-
);
116
-
};
-47
app/(home-pages)/looseleafs/page.tsx
-47
app/(home-pages)/looseleafs/page.tsx
···
1
-
import { getIdentityData } from "actions/getIdentityData";
2
-
import { DashboardLayout } from "components/PageLayouts/DashboardLayout";
3
-
import { Actions } from "../home/Actions/Actions";
4
-
import { Fact } from "src/replicache";
5
-
import { Attribute } from "src/replicache/attributes";
6
-
import { getFactsFromHomeLeaflets } from "app/api/rpc/[command]/getFactsFromHomeLeaflets";
7
-
import { supabaseServerClient } from "supabase/serverClient";
8
-
import { LooseleafsLayout } from "./LooseleafsLayout";
9
-
10
-
export default async function Home() {
11
-
let auth_res = await getIdentityData();
12
-
13
-
let [allLeafletFacts] = await Promise.all([
14
-
auth_res
15
-
? getFactsFromHomeLeaflets.handler(
16
-
{
17
-
tokens: auth_res.permission_token_on_homepage.map(
18
-
(r) => r.permission_tokens.root_entity,
19
-
),
20
-
},
21
-
{ supabase: supabaseServerClient },
22
-
)
23
-
: undefined,
24
-
]);
25
-
26
-
let home_docs_initialFacts = allLeafletFacts?.result || {};
27
-
28
-
return (
29
-
<LooseleafsLayout
30
-
entityID={auth_res?.home_leaflet?.root_entity || null}
31
-
titles={{
32
-
...home_docs_initialFacts.titles,
33
-
...auth_res?.permission_token_on_homepage.reduce(
34
-
(acc, tok) => {
35
-
let title =
36
-
tok.permission_tokens.leaflets_in_publications[0]?.title ||
37
-
tok.permission_tokens.leaflets_to_documents[0]?.title;
38
-
if (title) acc[tok.permission_tokens.root_entity] = title;
39
-
return acc;
40
-
},
41
-
{} as { [k: string]: string },
42
-
),
43
-
}}
44
-
initialFacts={home_docs_initialFacts.facts || {}}
45
-
/>
46
-
);
47
-
}
+3
-9
app/(home-pages)/notifications/CommentNotication.tsx
+3
-9
app/(home-pages)/notifications/CommentNotication.tsx
···
27
27
props.commentData.bsky_profiles?.handle ||
28
28
"Someone";
29
29
const pubRecord = props.commentData.documents?.documents_in_publications[0]
30
-
?.publications?.record as PubLeafletPublication.Record | undefined;
31
-
let docUri = new AtUri(props.commentData.documents?.uri!);
32
-
let rkey = docUri.rkey;
33
-
let did = docUri.host;
34
-
35
-
const href = pubRecord
36
-
? `https://${pubRecord.base_path}/${rkey}?interactionDrawer=comments`
37
-
: `/p/${did}/${rkey}?interactionDrawer=comments`;
30
+
?.publications?.record as PubLeafletPublication.Record;
31
+
let rkey = new AtUri(props.commentData.documents?.uri!).rkey;
38
32
39
33
return (
40
34
<Notification
41
35
timestamp={props.commentData.indexed_at}
42
-
href={href}
36
+
href={`https://${pubRecord.base_path}/${rkey}?interactionDrawer=comments`}
43
37
icon={<CommentTiny />}
44
38
actionText={<>{displayName} commented on your post</>}
45
39
content={
+41
-36
app/(home-pages)/notifications/MentionNotification.tsx
+41
-36
app/(home-pages)/notifications/MentionNotification.tsx
···
1
-
import { QuoteTiny } from "components/Icons/QuoteTiny";
1
+
import { MentionTiny } from "components/Icons/MentionTiny";
2
2
import { ContentLayout, Notification } from "./Notification";
3
-
import { HydratedQuoteNotification } from "src/notifications";
4
-
import { PubLeafletDocument, PubLeafletPublication } from "lexicons/api";
5
-
import { AtUri } from "@atproto/api";
6
-
import { Avatar } from "components/Avatar";
7
3
8
-
export const QuoteNotification = (props: HydratedQuoteNotification) => {
9
-
const postView = props.bskyPost.post_view as any;
10
-
const author = postView.author;
11
-
const displayName = author.displayName || author.handle || "Someone";
12
-
const docRecord = props.document.data as PubLeafletDocument.Record;
13
-
const pubRecord = props.document.documents_in_publications[0]?.publications
14
-
?.record as PubLeafletPublication.Record | undefined;
15
-
const docUri = new AtUri(props.document.uri);
16
-
const rkey = docUri.rkey;
17
-
const did = docUri.host;
18
-
const postText = postView.record?.text || "";
4
+
export const DummyPostMentionNotification = (props: {}) => {
5
+
return (
6
+
<Notification
7
+
timestamp={""}
8
+
href="/"
9
+
icon={<MentionTiny />}
10
+
actionText={<>celine mentioned your post</>}
11
+
content={
12
+
<ContentLayout
13
+
postTitle={"Post Title Here"}
14
+
pubRecord={{ name: "My Publication" } as any}
15
+
>
16
+
I'm just gonna put the description here. The surrounding context is
17
+
just sort of a pain to figure out
18
+
<div className="border border-border-light rounded-md p-1 my-1 text-xs text-secondary">
19
+
<div className="font-bold">Title of the Mentioned Post</div>
20
+
<div className="text-tertiary">
21
+
And here is the description that follows it
22
+
</div>
23
+
</div>
24
+
</ContentLayout>
25
+
}
26
+
/>
27
+
);
28
+
};
19
29
20
-
const href = pubRecord
21
-
? `https://${pubRecord.base_path}/${rkey}`
22
-
: `/p/${did}/${rkey}`;
23
-
30
+
export const DummyUserMentionNotification = (props: {
31
+
cardBorderHidden: boolean;
32
+
}) => {
24
33
return (
25
34
<Notification
26
-
timestamp={props.created_at}
27
-
href={href}
28
-
icon={<QuoteTiny />}
29
-
actionText={<>{displayName} quoted your post</>}
35
+
timestamp={""}
36
+
href="/"
37
+
icon={<MentionTiny />}
38
+
actionText={<>celine mentioned you</>}
30
39
content={
31
-
<ContentLayout postTitle={docRecord.title} pubRecord={pubRecord}>
32
-
<div className="flex gap-2 text-sm w-full">
33
-
<Avatar
34
-
src={author.avatar}
35
-
displayName={displayName}
36
-
/>
37
-
<pre
38
-
style={{ wordBreak: "break-word" }}
39
-
className="whitespace-pre-wrap text-secondary line-clamp-3 sm:line-clamp-6"
40
-
>
41
-
{postText}
42
-
</pre>
40
+
<ContentLayout
41
+
postTitle={"Post Title Here"}
42
+
pubRecord={{ name: "My Publication" } as any}
43
+
>
44
+
<div>
45
+
...llo this is the content of a post or whatever here it comes{" "}
46
+
<span className="text-accent-contrast">@celine </span> and here it
47
+
was! ooooh heck yeah the high is unre...
43
48
</div>
44
49
</ContentLayout>
45
50
}
-4
app/(home-pages)/notifications/NotificationList.tsx
-4
app/(home-pages)/notifications/NotificationList.tsx
···
7
7
import { ReplyNotification } from "./ReplyNotification";
8
8
import { useIdentityData } from "components/IdentityProvider";
9
9
import { FollowNotification } from "./FollowNotification";
10
-
import { QuoteNotification } from "./MentionNotification";
11
10
12
11
export function NotificationList({
13
12
notifications,
···
42
41
}
43
42
if (n.type === "subscribe") {
44
43
return <FollowNotification key={n.id} {...n} />;
45
-
}
46
-
if (n.type === "quote") {
47
-
return <QuoteNotification key={n.id} {...n} />;
48
44
}
49
45
})}
50
46
</div>
+3
-9
app/(home-pages)/notifications/ReplyNotification.tsx
+3
-9
app/(home-pages)/notifications/ReplyNotification.tsx
···
34
34
props.parentData?.bsky_profiles?.handle ||
35
35
"Someone";
36
36
37
-
let docUri = new AtUri(props.commentData.documents?.uri!);
38
-
let rkey = docUri.rkey;
39
-
let did = docUri.host;
37
+
let rkey = new AtUri(props.commentData.documents?.uri!).rkey;
40
38
const pubRecord = props.commentData.documents?.documents_in_publications[0]
41
-
?.publications?.record as PubLeafletPublication.Record | undefined;
42
-
43
-
const href = pubRecord
44
-
? `https://${pubRecord.base_path}/${rkey}?interactionDrawer=comments`
45
-
: `/p/${did}/${rkey}?interactionDrawer=comments`;
39
+
?.publications?.record as PubLeafletPublication.Record;
46
40
47
41
return (
48
42
<Notification
49
43
timestamp={props.commentData.indexed_at}
50
-
href={href}
44
+
href={`https://${pubRecord.base_path}/${rkey}?interactionDrawer=comments`}
51
45
icon={<ReplyTiny />}
52
46
actionText={`${displayName} replied to your comment`}
53
47
content={
+1
-1
app/(home-pages)/reader/ReaderContent.tsx
+1
-1
app/(home-pages)/reader/ReaderContent.tsx
···
102
102
let postRecord = props.documents.data as PubLeafletDocument.Record;
103
103
let postUri = new AtUri(props.documents.uri);
104
104
105
-
let theme = usePubTheme(pubRecord?.theme);
105
+
let theme = usePubTheme(pubRecord);
106
106
let backgroundImage = pubRecord?.theme?.backgroundImage?.image?.ref
107
107
? blobRefToSrc(
108
108
pubRecord?.theme?.backgroundImage?.image?.ref,
+98
app/[leaflet_id]/Actions.tsx
+98
app/[leaflet_id]/Actions.tsx
···
1
+
import { publishToPublication } from "actions/publishToPublication";
2
+
import {
3
+
getBasePublicationURL,
4
+
getPublicationURL,
5
+
} from "app/lish/createPub/getPublicationURL";
6
+
import { ActionButton } from "components/ActionBar/ActionButton";
7
+
import { GoBackSmall } from "components/Icons/GoBackSmall";
8
+
import { PublishSmall } from "components/Icons/PublishSmall";
9
+
import { useLeafletPublicationData } from "components/PageSWRDataProvider";
10
+
import { SpeedyLink } from "components/SpeedyLink";
11
+
import { useToaster } from "components/Toast";
12
+
import { DotLoader } from "components/utils/DotLoader";
13
+
import { useParams, useRouter } from "next/navigation";
14
+
import { useState } from "react";
15
+
import { useReplicache } from "src/replicache";
16
+
import { Json } from "supabase/database.types";
17
+
18
+
export const BackToPubButton = (props: {
19
+
publication: {
20
+
identity_did: string;
21
+
indexed_at: string;
22
+
name: string;
23
+
record: Json;
24
+
uri: string;
25
+
};
26
+
}) => {
27
+
return (
28
+
<SpeedyLink
29
+
href={`${getBasePublicationURL(props.publication)}/dashboard`}
30
+
className="hover:no-underline!"
31
+
>
32
+
<ActionButton
33
+
icon={<GoBackSmall className="shrink-0" />}
34
+
label="To Pub"
35
+
/>
36
+
</SpeedyLink>
37
+
);
38
+
};
39
+
40
+
export const PublishButton = () => {
41
+
let { data: pub } = useLeafletPublicationData();
42
+
let params = useParams();
43
+
let router = useRouter();
44
+
if (!pub?.doc)
45
+
return (
46
+
<ActionButton
47
+
primary
48
+
icon={<PublishSmall className="shrink-0" />}
49
+
label={"Publish!"}
50
+
onClick={() => {
51
+
router.push(`/${params.leaflet_id}/publish`);
52
+
}}
53
+
/>
54
+
);
55
+
56
+
return <UpdateButton />;
57
+
};
58
+
59
+
const UpdateButton = () => {
60
+
let [isLoading, setIsLoading] = useState(false);
61
+
let { data: pub, mutate } = useLeafletPublicationData();
62
+
let { permission_token, rootEntity } = useReplicache();
63
+
let toaster = useToaster();
64
+
65
+
return (
66
+
<ActionButton
67
+
primary
68
+
icon={<PublishSmall className="shrink-0" />}
69
+
label={isLoading ? <DotLoader /> : "Update!"}
70
+
onClick={async () => {
71
+
if (!pub || !pub.publications) return;
72
+
setIsLoading(true);
73
+
let doc = await publishToPublication({
74
+
root_entity: rootEntity,
75
+
publication_uri: pub.publications.uri,
76
+
leaflet_id: permission_token.id,
77
+
title: pub.title,
78
+
description: pub.description,
79
+
});
80
+
setIsLoading(false);
81
+
mutate();
82
+
toaster({
83
+
content: (
84
+
<div>
85
+
{pub.doc ? "Updated! " : "Published! "}
86
+
<SpeedyLink
87
+
href={`${getPublicationURL(pub.publications)}/${doc?.rkey}`}
88
+
>
89
+
link
90
+
</SpeedyLink>
91
+
</div>
92
+
),
93
+
type: "success",
94
+
});
95
+
}}
96
+
/>
97
+
);
98
+
};
+28
-12
app/[leaflet_id]/Sidebar.tsx
+28
-12
app/[leaflet_id]/Sidebar.tsx
···
1
1
"use client";
2
+
import { ActionButton } from "components/ActionBar/ActionButton";
2
3
import { Sidebar } from "components/ActionBar/Sidebar";
3
4
import { useEntitySetContext } from "components/EntitySetProvider";
4
-
import { HelpButton } from "app/[leaflet_id]/actions/HelpButton";
5
-
import { HomeButton } from "app/[leaflet_id]/actions/HomeButton";
5
+
import { HelpPopover } from "components/HelpPopover";
6
+
import { HomeButton } from "components/HomeButton";
6
7
import { Media } from "components/Media";
7
8
import { useLeafletPublicationData } from "components/PageSWRDataProvider";
8
-
import { ShareOptions } from "app/[leaflet_id]/actions/ShareOptions";
9
+
import { ShareOptions } from "components/ShareOptions";
9
10
import { ThemePopover } from "components/ThemeManager/ThemeSetter";
10
-
import { PublishButton } from "./actions/PublishButton";
11
11
import { Watermark } from "components/Watermark";
12
-
import { BackToPubButton } from "./actions/BackToPubButton";
12
+
import { useUIState } from "src/useUIState";
13
+
import { BackToPubButton, PublishButton } from "./Actions";
13
14
import { useIdentityData } from "components/IdentityProvider";
14
15
import { useReplicache } from "src/replicache";
15
16
···
28
29
<div className="sidebarContainer flex flex-col justify-end h-full w-16 relative">
29
30
{entity_set.permissions.write && (
30
31
<Sidebar>
31
-
<PublishButton entityID={rootEntity} />
32
-
<ShareOptions />
33
-
<ThemePopover entityID={rootEntity} />
34
-
<HelpButton />
35
-
<hr className="text-border" />
36
32
{pub?.publications &&
37
33
identity?.atp_did &&
38
34
pub.publications.identity_did === identity.atp_did ? (
39
-
<BackToPubButton publication={pub.publications} />
35
+
<>
36
+
<PublishButton />
37
+
<ShareOptions />
38
+
<ThemePopover entityID={rootEntity} />
39
+
<HelpPopover />
40
+
<hr className="text-border" />
41
+
<BackToPubButton publication={pub.publications} />
42
+
</>
40
43
) : (
41
-
<HomeButton />
44
+
<>
45
+
<ShareOptions />
46
+
<ThemePopover entityID={rootEntity} />
47
+
<HelpPopover />
48
+
<hr className="text-border" />
49
+
<HomeButton />
50
+
</>
42
51
)}
43
52
</Sidebar>
44
53
)}
···
50
59
</Media>
51
60
);
52
61
}
62
+
63
+
const blurPage = () => {
64
+
useUIState.setState(() => ({
65
+
focusedEntity: null,
66
+
selectedBlocks: [],
67
+
}));
68
+
};
-27
app/[leaflet_id]/actions/BackToPubButton.tsx
-27
app/[leaflet_id]/actions/BackToPubButton.tsx
···
1
-
import { getBasePublicationURL } from "app/lish/createPub/getPublicationURL";
2
-
import { ActionButton } from "components/ActionBar/ActionButton";
3
-
import { GoBackSmall } from "components/Icons/GoBackSmall";
4
-
import { SpeedyLink } from "components/SpeedyLink";
5
-
import { Json } from "supabase/database.types";
6
-
7
-
export const BackToPubButton = (props: {
8
-
publication: {
9
-
identity_did: string;
10
-
indexed_at: string;
11
-
name: string;
12
-
record: Json;
13
-
uri: string;
14
-
};
15
-
}) => {
16
-
return (
17
-
<SpeedyLink
18
-
href={`${getBasePublicationURL(props.publication)}/dashboard`}
19
-
className="hover:no-underline!"
20
-
>
21
-
<ActionButton
22
-
icon={<GoBackSmall className="shrink-0" />}
23
-
label="To Pub"
24
-
/>
25
-
</SpeedyLink>
26
-
);
27
-
};
-173
app/[leaflet_id]/actions/HelpButton.tsx
-173
app/[leaflet_id]/actions/HelpButton.tsx
···
1
-
"use client";
2
-
import { ShortcutKey } from "../../../components/Layout";
3
-
import { Media } from "../../../components/Media";
4
-
import { Popover } from "../../../components/Popover";
5
-
import { metaKey } from "src/utils/metaKey";
6
-
import { useEntitySetContext } from "../../../components/EntitySetProvider";
7
-
import { useState } from "react";
8
-
import { ActionButton } from "components/ActionBar/ActionButton";
9
-
import { HelpSmall } from "../../../components/Icons/HelpSmall";
10
-
import { isMac } from "src/utils/isDevice";
11
-
import { useIsMobile } from "src/hooks/isMobile";
12
-
13
-
export const HelpButton = (props: { noShortcuts?: boolean }) => {
14
-
let entity_set = useEntitySetContext();
15
-
let isMobile = useIsMobile();
16
-
17
-
return entity_set.permissions.write ? (
18
-
<Popover
19
-
side={isMobile ? "top" : "right"}
20
-
align={isMobile ? "center" : "start"}
21
-
asChild
22
-
className="max-w-xs w-full"
23
-
trigger={<ActionButton icon={<HelpSmall />} label="About" />}
24
-
>
25
-
<div className="flex flex-col text-sm gap-2 text-secondary">
26
-
{/* about links */}
27
-
<HelpLink text="📖 Leaflet Manual" url="https://about.leaflet.pub" />
28
-
<HelpLink text="💡 Make with Leaflet" url="https://make.leaflet.pub" />
29
-
<HelpLink
30
-
text="✨ Explore Publications"
31
-
url="https://leaflet.pub/discover"
32
-
/>
33
-
<HelpLink text="📣 Newsletter" url="https://buttondown.com/leaflet" />
34
-
{/* contact links */}
35
-
<div className="columns-2 gap-2">
36
-
<HelpLink
37
-
text="🦋 Bluesky"
38
-
url="https://bsky.app/profile/leaflet.pub"
39
-
/>
40
-
<HelpLink text="💌 Email" url="mailto:contact@leaflet.pub" />
41
-
</div>
42
-
{/* keyboard shortcuts: desktop only */}
43
-
<Media mobile={false}>
44
-
{!props.noShortcuts && (
45
-
<>
46
-
<hr className="text-border my-1" />
47
-
<div className="flex flex-col gap-1">
48
-
<Label>Text Shortcuts</Label>
49
-
<KeyboardShortcut name="Bold" keys={[metaKey(), "B"]} />
50
-
<KeyboardShortcut name="Italic" keys={[metaKey(), "I"]} />
51
-
<KeyboardShortcut name="Underline" keys={[metaKey(), "U"]} />
52
-
<KeyboardShortcut
53
-
name="Highlight"
54
-
keys={[metaKey(), isMac() ? "Ctrl" : "Meta", "H"]}
55
-
/>
56
-
<KeyboardShortcut
57
-
name="Strikethrough"
58
-
keys={[metaKey(), isMac() ? "Ctrl" : "Meta", "X"]}
59
-
/>
60
-
<KeyboardShortcut name="Inline Link" keys={[metaKey(), "K"]} />
61
-
62
-
<Label>Block Shortcuts</Label>
63
-
{/* shift + up/down arrows (or click + drag): select multiple blocks */}
64
-
<KeyboardShortcut
65
-
name="Move Block Up"
66
-
keys={["Shift", metaKey(), "↑"]}
67
-
/>
68
-
<KeyboardShortcut
69
-
name="Move Block Down"
70
-
keys={["Shift", metaKey(), "↓"]}
71
-
/>
72
-
{/* cmd/ctrl-a: first selects all text in a block; again selects all blocks on page */}
73
-
{/* cmd/ctrl + up/down arrows: go to beginning / end of doc */}
74
-
75
-
<Label>Canvas Shortcuts</Label>
76
-
<OtherShortcut name="Add Block" description="Double click" />
77
-
<OtherShortcut name="Select Block" description="Long press" />
78
-
79
-
<Label>Outliner Shortcuts</Label>
80
-
<KeyboardShortcut
81
-
name="Make List"
82
-
keys={[metaKey(), isMac() ? "Opt" : "Alt", "L"]}
83
-
/>
84
-
{/* tab / shift + tab: indent / outdent */}
85
-
<KeyboardShortcut
86
-
name="Toggle Checkbox"
87
-
keys={[metaKey(), "Enter"]}
88
-
/>
89
-
<KeyboardShortcut
90
-
name="Toggle Fold"
91
-
keys={[metaKey(), "Shift", "Enter"]}
92
-
/>
93
-
<KeyboardShortcut
94
-
name="Fold All"
95
-
keys={[metaKey(), isMac() ? "Opt" : "Alt", "Shift", "↑"]}
96
-
/>
97
-
<KeyboardShortcut
98
-
name="Unfold All"
99
-
keys={[metaKey(), isMac() ? "Opt" : "Alt", "Shift", "↓"]}
100
-
/>
101
-
</div>
102
-
</>
103
-
)}
104
-
</Media>
105
-
{/* links: terms and privacy */}
106
-
<hr className="text-border my-1" />
107
-
{/* <HelpLink
108
-
text="Terms and Privacy Policy"
109
-
url="https://leaflet.pub/legal"
110
-
/> */}
111
-
<div>
112
-
<a href="https://leaflet.pub/legal" target="_blank">
113
-
Terms and Privacy Policy
114
-
</a>
115
-
</div>
116
-
</div>
117
-
</Popover>
118
-
) : null;
119
-
};
120
-
121
-
const KeyboardShortcut = (props: { name: string; keys: string[] }) => {
122
-
return (
123
-
<div className="flex gap-2 justify-between items-center">
124
-
{props.name}
125
-
<div className="flex gap-1 items-center font-bold">
126
-
{props.keys.map((key, index) => {
127
-
return <ShortcutKey key={index}>{key}</ShortcutKey>;
128
-
})}
129
-
</div>
130
-
</div>
131
-
);
132
-
};
133
-
134
-
const OtherShortcut = (props: { name: string; description: string }) => {
135
-
return (
136
-
<div className="flex justify-between items-center">
137
-
<span>{props.name}</span>
138
-
<span>
139
-
<strong>{props.description}</strong>
140
-
</span>
141
-
</div>
142
-
);
143
-
};
144
-
145
-
const Label = (props: { children: React.ReactNode }) => {
146
-
return <div className="text-tertiary font-bold pt-2 ">{props.children}</div>;
147
-
};
148
-
149
-
const HelpLink = (props: { url: string; text: string }) => {
150
-
const [isHovered, setIsHovered] = useState(false);
151
-
const handleMouseEnter = () => {
152
-
setIsHovered(true);
153
-
};
154
-
const handleMouseLeave = () => {
155
-
setIsHovered(false);
156
-
};
157
-
return (
158
-
<a
159
-
href={props.url}
160
-
target="_blank"
161
-
className="py-2 px-2 rounded-md flex flex-col gap-1 bg-border-light hover:bg-border hover:no-underline"
162
-
style={{
163
-
backgroundColor: isHovered
164
-
? "color-mix(in oklab, rgb(var(--accent-contrast)), rgb(var(--bg-page)) 85%)"
165
-
: "color-mix(in oklab, rgb(var(--accent-contrast)), rgb(var(--bg-page)) 75%)",
166
-
}}
167
-
onMouseEnter={handleMouseEnter}
168
-
onMouseLeave={handleMouseLeave}
169
-
>
170
-
<strong>{props.text}</strong>
171
-
</a>
172
-
);
173
-
};
-74
app/[leaflet_id]/actions/HomeButton.tsx
-74
app/[leaflet_id]/actions/HomeButton.tsx
···
1
-
"use client";
2
-
import Link from "next/link";
3
-
import { useEntitySetContext } from "../../../components/EntitySetProvider";
4
-
import { ActionButton } from "components/ActionBar/ActionButton";
5
-
import { useSearchParams } from "next/navigation";
6
-
import { useIdentityData } from "../../../components/IdentityProvider";
7
-
import { useReplicache } from "src/replicache";
8
-
import { addLeafletToHome } from "actions/addLeafletToHome";
9
-
import { useSmoker } from "../../../components/Toast";
10
-
import { AddToHomeSmall } from "../../../components/Icons/AddToHomeSmall";
11
-
import { HomeSmall } from "../../../components/Icons/HomeSmall";
12
-
import { produce } from "immer";
13
-
14
-
export function HomeButton() {
15
-
let { permissions } = useEntitySetContext();
16
-
let searchParams = useSearchParams();
17
-
18
-
return (
19
-
<>
20
-
<Link
21
-
href="/home"
22
-
prefetch
23
-
className="hover:no-underline"
24
-
style={{ textDecorationLine: "none !important" }}
25
-
>
26
-
<ActionButton icon={<HomeSmall />} label="Go Home" />
27
-
</Link>
28
-
{<AddToHomeButton />}
29
-
</>
30
-
);
31
-
}
32
-
33
-
const AddToHomeButton = (props: {}) => {
34
-
let { permission_token } = useReplicache();
35
-
let { identity, mutate } = useIdentityData();
36
-
let smoker = useSmoker();
37
-
if (
38
-
identity?.permission_token_on_homepage.find(
39
-
(pth) => pth.permission_tokens.id === permission_token.id,
40
-
) ||
41
-
!identity
42
-
)
43
-
return null;
44
-
return (
45
-
<ActionButton
46
-
onClick={async (e) => {
47
-
await addLeafletToHome(permission_token.id);
48
-
mutate((identity) => {
49
-
if (!identity) return;
50
-
return produce<typeof identity>((draft) => {
51
-
draft.permission_token_on_homepage.push({
52
-
created_at: new Date().toISOString(),
53
-
archived: null,
54
-
permission_tokens: {
55
-
...permission_token,
56
-
leaflets_to_documents: [],
57
-
leaflets_in_publications: [],
58
-
},
59
-
});
60
-
})(identity);
61
-
});
62
-
smoker({
63
-
position: {
64
-
x: e.clientX + 64,
65
-
y: e.clientY,
66
-
},
67
-
text: "Leaflet added to your home!",
68
-
});
69
-
}}
70
-
icon={<AddToHomeSmall />}
71
-
label="Add to Home"
72
-
/>
73
-
);
74
-
};
-427
app/[leaflet_id]/actions/PublishButton.tsx
-427
app/[leaflet_id]/actions/PublishButton.tsx
···
1
-
"use client";
2
-
import { publishToPublication } from "actions/publishToPublication";
3
-
import { getPublicationURL } from "app/lish/createPub/getPublicationURL";
4
-
import { ActionButton } from "components/ActionBar/ActionButton";
5
-
import {
6
-
PubIcon,
7
-
PubListEmptyContent,
8
-
PubListEmptyIllo,
9
-
} from "components/ActionBar/Publications";
10
-
import { ButtonPrimary, ButtonTertiary } from "components/Buttons";
11
-
import { AddSmall } from "components/Icons/AddSmall";
12
-
import { LooseLeafSmall } from "components/Icons/LooseleafSmall";
13
-
import { PublishSmall } from "components/Icons/PublishSmall";
14
-
import { useIdentityData } from "components/IdentityProvider";
15
-
import { InputWithLabel } from "components/Input";
16
-
import { Menu, MenuItem } from "components/Layout";
17
-
import {
18
-
useLeafletDomains,
19
-
useLeafletPublicationData,
20
-
} from "components/PageSWRDataProvider";
21
-
import { Popover } from "components/Popover";
22
-
import { SpeedyLink } from "components/SpeedyLink";
23
-
import { useToaster } from "components/Toast";
24
-
import { DotLoader } from "components/utils/DotLoader";
25
-
import { PubLeafletPublication } from "lexicons/api";
26
-
import { useParams, useRouter, useSearchParams } from "next/navigation";
27
-
import { useState, useMemo } from "react";
28
-
import { useIsMobile } from "src/hooks/isMobile";
29
-
import { useReplicache, useEntity } from "src/replicache";
30
-
import { Json } from "supabase/database.types";
31
-
import {
32
-
useBlocks,
33
-
useCanvasBlocksWithType,
34
-
} from "src/hooks/queries/useBlocks";
35
-
import * as Y from "yjs";
36
-
import * as base64 from "base64-js";
37
-
import { YJSFragmentToString } from "components/Blocks/TextBlock/RenderYJSFragment";
38
-
import { BlueskyLogin } from "app/login/LoginForm";
39
-
import { moveLeafletToPublication } from "actions/publications/moveLeafletToPublication";
40
-
import { AddTiny } from "components/Icons/AddTiny";
41
-
42
-
export const PublishButton = (props: { entityID: string }) => {
43
-
let { data: pub } = useLeafletPublicationData();
44
-
let params = useParams();
45
-
let router = useRouter();
46
-
47
-
if (!pub) return <PublishToPublicationButton entityID={props.entityID} />;
48
-
if (!pub?.doc)
49
-
return (
50
-
<ActionButton
51
-
primary
52
-
icon={<PublishSmall className="shrink-0" />}
53
-
label={"Publish!"}
54
-
onClick={() => {
55
-
router.push(`/${params.leaflet_id}/publish`);
56
-
}}
57
-
/>
58
-
);
59
-
60
-
return <UpdateButton />;
61
-
};
62
-
63
-
const UpdateButton = () => {
64
-
let [isLoading, setIsLoading] = useState(false);
65
-
let { data: pub, mutate } = useLeafletPublicationData();
66
-
let { permission_token, rootEntity } = useReplicache();
67
-
let { identity } = useIdentityData();
68
-
let toaster = useToaster();
69
-
70
-
return (
71
-
<ActionButton
72
-
primary
73
-
icon={<PublishSmall className="shrink-0" />}
74
-
label={isLoading ? <DotLoader /> : "Update!"}
75
-
onClick={async () => {
76
-
if (!pub) return;
77
-
setIsLoading(true);
78
-
let doc = await publishToPublication({
79
-
root_entity: rootEntity,
80
-
publication_uri: pub.publications?.uri,
81
-
leaflet_id: permission_token.id,
82
-
title: pub.title,
83
-
description: pub.description,
84
-
});
85
-
setIsLoading(false);
86
-
mutate();
87
-
88
-
// Generate URL based on whether it's in a publication or standalone
89
-
let docUrl = pub.publications
90
-
? `${getPublicationURL(pub.publications)}/${doc?.rkey}`
91
-
: `https://leaflet.pub/p/${identity?.atp_did}/${doc?.rkey}`;
92
-
93
-
toaster({
94
-
content: (
95
-
<div>
96
-
{pub.doc ? "Updated! " : "Published! "}
97
-
<SpeedyLink href={docUrl}>link</SpeedyLink>
98
-
</div>
99
-
),
100
-
type: "success",
101
-
});
102
-
}}
103
-
/>
104
-
);
105
-
};
106
-
107
-
const PublishToPublicationButton = (props: { entityID: string }) => {
108
-
let { identity } = useIdentityData();
109
-
let { permission_token } = useReplicache();
110
-
let query = useSearchParams();
111
-
console.log(query.get("publish"));
112
-
let [open, setOpen] = useState(query.get("publish") !== null);
113
-
114
-
let isMobile = useIsMobile();
115
-
identity && identity.atp_did && identity.publications.length > 0;
116
-
let [selectedPub, setSelectedPub] = useState<string | undefined>(undefined);
117
-
let router = useRouter();
118
-
let { title, entitiesToDelete } = useTitle(props.entityID);
119
-
let [description, setDescription] = useState("");
120
-
121
-
return (
122
-
<Popover
123
-
asChild
124
-
open={open}
125
-
onOpenChange={(o) => setOpen(o)}
126
-
side={isMobile ? "top" : "right"}
127
-
align={isMobile ? "center" : "start"}
128
-
className="sm:max-w-sm w-[1000px]"
129
-
trigger={
130
-
<ActionButton
131
-
primary
132
-
icon={<PublishSmall className="shrink-0" />}
133
-
label={"Publish on ATP"}
134
-
/>
135
-
}
136
-
>
137
-
{!identity || !identity.atp_did ? (
138
-
<div className="-mx-2 -my-1">
139
-
<div
140
-
className={`bg-[var(--accent-light)] w-full rounded-md flex flex-col text-center justify-center p-2 pb-4 text-sm`}
141
-
>
142
-
<div className="mx-auto pt-2 scale-90">
143
-
<PubListEmptyIllo />
144
-
</div>
145
-
<div className="pt-1 font-bold">Publish on AT Proto</div>
146
-
{
147
-
<>
148
-
<div className="pb-2 text-secondary text-xs">
149
-
Link a Bluesky account to start <br /> a publishing on AT
150
-
Proto
151
-
</div>
152
-
153
-
<BlueskyLogin
154
-
compact
155
-
redirectRoute={`/${permission_token.id}?publish`}
156
-
/>
157
-
</>
158
-
}
159
-
</div>
160
-
</div>
161
-
) : (
162
-
<div className="flex flex-col">
163
-
<PostDetailsForm
164
-
title={title}
165
-
description={description}
166
-
setDescription={setDescription}
167
-
/>
168
-
<hr className="border-border-light my-3" />
169
-
<div>
170
-
<PubSelector
171
-
publications={identity.publications}
172
-
selectedPub={selectedPub}
173
-
setSelectedPub={setSelectedPub}
174
-
/>
175
-
</div>
176
-
<hr className="border-border-light mt-3 mb-2" />
177
-
178
-
<div className="flex gap-2 items-center place-self-end">
179
-
{selectedPub !== "looseleaf" && selectedPub && (
180
-
<SaveAsDraftButton
181
-
selectedPub={selectedPub}
182
-
leafletId={permission_token.id}
183
-
metadata={{ title: title, description }}
184
-
entitiesToDelete={entitiesToDelete}
185
-
/>
186
-
)}
187
-
<ButtonPrimary
188
-
disabled={selectedPub === undefined}
189
-
onClick={async (e) => {
190
-
if (!selectedPub) return;
191
-
e.preventDefault();
192
-
if (selectedPub === "create") return;
193
-
194
-
// For looseleaf, navigate without publication_uri
195
-
if (selectedPub === "looseleaf") {
196
-
router.push(
197
-
`${permission_token.id}/publish?title=${encodeURIComponent(title)}&description=${encodeURIComponent(description)}&entitiesToDelete=${encodeURIComponent(JSON.stringify(entitiesToDelete))}`,
198
-
);
199
-
} else {
200
-
router.push(
201
-
`${permission_token.id}/publish?publication_uri=${encodeURIComponent(selectedPub)}&title=${encodeURIComponent(title)}&description=${encodeURIComponent(description)}&entitiesToDelete=${encodeURIComponent(JSON.stringify(entitiesToDelete))}`,
202
-
);
203
-
}
204
-
}}
205
-
>
206
-
Next{selectedPub === "create" && ": Create Pub!"}
207
-
</ButtonPrimary>
208
-
</div>
209
-
</div>
210
-
)}
211
-
</Popover>
212
-
);
213
-
};
214
-
215
-
const SaveAsDraftButton = (props: {
216
-
selectedPub: string | undefined;
217
-
leafletId: string;
218
-
metadata: { title: string; description: string };
219
-
entitiesToDelete: string[];
220
-
}) => {
221
-
let { mutate } = useLeafletPublicationData();
222
-
let { rep } = useReplicache();
223
-
let [isLoading, setIsLoading] = useState(false);
224
-
225
-
return (
226
-
<ButtonTertiary
227
-
onClick={async (e) => {
228
-
if (!props.selectedPub) return;
229
-
if (props.selectedPub === "create") return;
230
-
e.preventDefault();
231
-
setIsLoading(true);
232
-
await moveLeafletToPublication(
233
-
props.leafletId,
234
-
props.selectedPub,
235
-
props.metadata,
236
-
props.entitiesToDelete,
237
-
);
238
-
await Promise.all([rep?.pull(), mutate()]);
239
-
setIsLoading(false);
240
-
}}
241
-
>
242
-
{isLoading ? <DotLoader /> : "Save as Draft"}
243
-
</ButtonTertiary>
244
-
);
245
-
};
246
-
247
-
const PostDetailsForm = (props: {
248
-
title: string;
249
-
description: string;
250
-
setDescription: (d: string) => void;
251
-
}) => {
252
-
return (
253
-
<div className=" flex flex-col gap-1">
254
-
<div className="text-sm text-tertiary">Post Details</div>
255
-
<div className="flex flex-col gap-2">
256
-
<InputWithLabel label="Title" value={props.title} disabled />
257
-
<InputWithLabel
258
-
label="Description (optional)"
259
-
textarea
260
-
value={props.description}
261
-
className="h-[4lh]"
262
-
onChange={(e) => props.setDescription(e.currentTarget.value)}
263
-
/>
264
-
</div>
265
-
</div>
266
-
);
267
-
};
268
-
269
-
const PubSelector = (props: {
270
-
selectedPub: string | undefined;
271
-
setSelectedPub: (s: string) => void;
272
-
publications: {
273
-
identity_did: string;
274
-
indexed_at: string;
275
-
name: string;
276
-
record: Json | null;
277
-
uri: string;
278
-
}[];
279
-
}) => {
280
-
// HEY STILL TO DO
281
-
// test out logged out, logged in but no pubs, and pubbed up flows
282
-
283
-
return (
284
-
<div className="flex flex-col gap-1">
285
-
<div className="text-sm text-tertiary">Publish to…</div>
286
-
{props.publications.length === 0 || props.publications === undefined ? (
287
-
<div className="flex flex-col gap-1">
288
-
<div className="flex gap-2 menuItem">
289
-
<LooseLeafSmall className="shrink-0" />
290
-
<div className="flex flex-col leading-snug">
291
-
<div className="text-secondary font-bold">
292
-
Publish as Looseleaf
293
-
</div>
294
-
<div className="text-tertiary text-sm font-normal">
295
-
Publish this as a one off doc to AT Proto
296
-
</div>
297
-
</div>
298
-
</div>
299
-
<div className="flex gap-2 px-2 py-1 ">
300
-
<PublishSmall className="shrink-0 text-border" />
301
-
<div className="flex flex-col leading-snug">
302
-
<div className="text-border font-bold">
303
-
Publish to Publication
304
-
</div>
305
-
<div className="text-border text-sm font-normal">
306
-
Publish your writing to a blog on AT Proto
307
-
</div>
308
-
<hr className="my-2 drashed border-border-light border-dashed" />
309
-
<div className="text-tertiary text-sm font-normal ">
310
-
You don't have any Publications yet.{" "}
311
-
<a target="_blank" href="/lish/createPub">
312
-
Create one
313
-
</a>{" "}
314
-
to get started!
315
-
</div>
316
-
</div>
317
-
</div>
318
-
</div>
319
-
) : (
320
-
<div className="flex flex-col gap-1">
321
-
<PubOption
322
-
selected={props.selectedPub === "looseleaf"}
323
-
onSelect={() => props.setSelectedPub("looseleaf")}
324
-
>
325
-
<LooseLeafSmall />
326
-
Publish as Looseleaf
327
-
</PubOption>
328
-
<hr className="border-border-light border-dashed " />
329
-
{props.publications.map((p) => {
330
-
let pubRecord = p.record as PubLeafletPublication.Record;
331
-
return (
332
-
<PubOption
333
-
key={p.uri}
334
-
selected={props.selectedPub === p.uri}
335
-
onSelect={() => props.setSelectedPub(p.uri)}
336
-
>
337
-
<>
338
-
<PubIcon record={pubRecord} uri={p.uri} />
339
-
{p.name}
340
-
</>
341
-
</PubOption>
342
-
);
343
-
})}
344
-
<div className="flex items-center px-2 py-1 text-accent-contrast gap-2">
345
-
<AddTiny className="m-1 shrink-0" />
346
-
347
-
<a target="_blank" href="/lish/createPub">
348
-
Start a new Publication
349
-
</a>
350
-
</div>
351
-
</div>
352
-
)}
353
-
</div>
354
-
);
355
-
};
356
-
357
-
const PubOption = (props: {
358
-
selected: boolean;
359
-
onSelect: () => void;
360
-
children: React.ReactNode;
361
-
}) => {
362
-
return (
363
-
<button
364
-
className={`flex gap-2 menuItem font-bold text-secondary ${props.selected && "bg-[var(--accent-light)]! outline! outline-offset-1! outline-accent-contrast!"}`}
365
-
onClick={() => {
366
-
props.onSelect();
367
-
}}
368
-
>
369
-
{props.children}
370
-
</button>
371
-
);
372
-
};
373
-
374
-
let useTitle = (entityID: string) => {
375
-
let rootPage = useEntity(entityID, "root/page")[0].data.value;
376
-
let canvasBlocks = useCanvasBlocksWithType(rootPage).filter(
377
-
(b) => b.type === "text" || b.type === "heading",
378
-
);
379
-
let blocks = useBlocks(rootPage).filter(
380
-
(b) => b.type === "text" || b.type === "heading",
381
-
);
382
-
let firstBlock = canvasBlocks[0] || blocks[0];
383
-
384
-
let firstBlockText = useEntity(firstBlock?.value, "block/text")?.data.value;
385
-
386
-
const leafletTitle = useMemo(() => {
387
-
if (!firstBlockText) return "Untitled";
388
-
let doc = new Y.Doc();
389
-
const update = base64.toByteArray(firstBlockText);
390
-
Y.applyUpdate(doc, update);
391
-
let nodes = doc.getXmlElement("prosemirror").toArray();
392
-
return YJSFragmentToString(nodes[0]) || "Untitled";
393
-
}, [firstBlockText]);
394
-
395
-
// Only handle second block logic for linear documents, not canvas
396
-
let isCanvas = canvasBlocks.length > 0;
397
-
let secondBlock = !isCanvas ? blocks[1] : undefined;
398
-
let secondBlockTextValue = useEntity(secondBlock?.value || null, "block/text")
399
-
?.data.value;
400
-
const secondBlockText = useMemo(() => {
401
-
if (!secondBlockTextValue) return "";
402
-
let doc = new Y.Doc();
403
-
const update = base64.toByteArray(secondBlockTextValue);
404
-
Y.applyUpdate(doc, update);
405
-
let nodes = doc.getXmlElement("prosemirror").toArray();
406
-
return YJSFragmentToString(nodes[0]) || "";
407
-
}, [secondBlockTextValue]);
408
-
409
-
let entitiesToDelete = useMemo(() => {
410
-
let etod: string[] = [];
411
-
// Only delete first block if it's a heading type
412
-
if (firstBlock?.type === "heading") {
413
-
etod.push(firstBlock.value);
414
-
}
415
-
// Delete second block if it's empty text (only for linear documents)
416
-
if (
417
-
!isCanvas &&
418
-
secondBlockText.trim() === "" &&
419
-
secondBlock?.type === "text"
420
-
) {
421
-
etod.push(secondBlock.value);
422
-
}
423
-
return etod;
424
-
}, [firstBlock, secondBlockText, secondBlock, isCanvas]);
425
-
426
-
return { title: leafletTitle, entitiesToDelete };
427
-
};
+2
-4
app/[leaflet_id]/icon.tsx
+2
-4
app/[leaflet_id]/icon.tsx
···
24
24
process.env.SUPABASE_SERVICE_ROLE_KEY as string,
25
25
{ cookies: {} },
26
26
);
27
-
export default async function Icon(props: {
28
-
params: Promise<{ leaflet_id: string }>;
29
-
}) {
27
+
export default async function Icon(props: { params: { leaflet_id: string } }) {
30
28
let res = await supabase
31
29
.from("permission_tokens")
32
30
.select("*, permission_token_rights(*)")
33
-
.eq("id", (await props.params).leaflet_id)
31
+
.eq("id", props.params.leaflet_id)
34
32
.single();
35
33
let rootEntity = res.data?.root_entity;
36
34
let outlineColor, fillColor;
+2
-3
app/[leaflet_id]/opengraph-image.tsx
+2
-3
app/[leaflet_id]/opengraph-image.tsx
···
4
4
export const revalidate = 60;
5
5
6
6
export default async function OpenGraphImage(props: {
7
-
params: Promise<{ leaflet_id: string }>;
7
+
params: { leaflet_id: string };
8
8
}) {
9
-
let params = await props.params;
10
-
return getMicroLinkOgImage(`/${params.leaflet_id}`);
9
+
return getMicroLinkOgImage(`/${props.params.leaflet_id}`);
11
10
}
+5
-2
app/[leaflet_id]/page.tsx
+5
-2
app/[leaflet_id]/page.tsx
···
13
13
import { supabaseServerClient } from "supabase/serverClient";
14
14
import { get_leaflet_data } from "app/api/rpc/[command]/get_leaflet_data";
15
15
import { NotFoundLayout } from "components/PageLayouts/NotFoundLayout";
16
-
import { getPublicationMetadataFromLeafletData } from "src/utils/getPublicationMetadataFromLeafletData";
17
16
18
17
export const preferredRegion = ["sfo1"];
19
18
export const dynamic = "force-dynamic";
···
71
70
);
72
71
let rootEntity = res.data?.root_entity;
73
72
if (!rootEntity || !res.data) return { title: "Leaflet not found" };
74
-
let publication_data = getPublicationMetadataFromLeafletData(res.data);
73
+
let publication_data =
74
+
res.data?.leaflets_in_publications?.[0] ||
75
+
res.data?.permission_token_rights[0].entity_sets?.permission_tokens?.find(
76
+
(p) => p.leaflets_in_publications.length,
77
+
)?.leaflets_in_publications?.[0];
75
78
if (publication_data) {
76
79
return {
77
80
title: publication_data.title || "Untitled",
+1
-3
app/[leaflet_id]/publish/BskyPostEditorProsemirror.tsx
+1
-3
app/[leaflet_id]/publish/BskyPostEditorProsemirror.tsx
···
245
245
view.updateState(newState);
246
246
setEditorState(newState);
247
247
props.editorStateRef.current = newState;
248
-
props.onCharCountChange?.(
249
-
newState.doc.textContent.length + newState.doc.children.length - 1,
250
-
);
248
+
props.onCharCountChange?.(newState.doc.textContent.length);
251
249
},
252
250
},
253
251
);
+17
-68
app/[leaflet_id]/publish/PublishPost.tsx
+17
-68
app/[leaflet_id]/publish/PublishPost.tsx
···
18
18
editorStateToFacetedText,
19
19
} from "./BskyPostEditorProsemirror";
20
20
import { EditorState } from "prosemirror-state";
21
-
import { LooseLeafSmall } from "components/Icons/LooseleafSmall";
22
-
import { PubIcon } from "components/ActionBar/Publications";
23
21
24
22
type Props = {
25
23
title: string;
···
27
25
root_entity: string;
28
26
profile: ProfileViewDetailed;
29
27
description: string;
30
-
publication_uri?: string;
28
+
publication_uri: string;
31
29
record?: PubLeafletPublication.Record;
32
30
posts_in_pub?: number;
33
-
entitiesToDelete?: string[];
34
31
};
35
32
36
33
export function PublishPost(props: Props) {
···
75
72
leaflet_id: props.leaflet_id,
76
73
title: props.title,
77
74
description: props.description,
78
-
entitiesToDelete: props.entitiesToDelete,
79
75
});
80
76
if (!doc) return;
81
77
82
-
// Generate post URL based on whether it's in a publication or standalone
83
-
let post_url = props.record?.base_path
84
-
? `https://${props.record.base_path}/${doc.rkey}`
85
-
: `https://leaflet.pub/p/${props.profile.did}/${doc.rkey}`;
86
-
78
+
let post_url = `https://${props.record?.base_path}/${doc.rkey}`;
87
79
let [text, facets] = editorStateRef.current
88
80
? editorStateToFacetedText(editorStateRef.current)
89
81
: [];
···
102
94
}
103
95
104
96
return (
105
-
<div className="flex flex-col gap-4 w-[640px] max-w-full sm:px-4 px-3 text-primary">
97
+
<div className="flex flex-col gap-4 w-[640px] max-w-full sm:px-4 px-3">
98
+
<h3>Publish Options</h3>
106
99
<form
107
100
onSubmit={(e) => {
108
101
e.preventDefault();
···
110
103
}}
111
104
>
112
105
<div className="container flex flex-col gap-2 sm:p-3 p-4">
113
-
<PublishingTo
114
-
publication_uri={props.publication_uri}
115
-
record={props.record}
116
-
/>
117
-
<hr className="border-border-light my-1" />
118
106
<Radio
119
107
checked={shareOption === "quiet"}
120
108
onChange={(e) => {
···
173
161
/>
174
162
</div>
175
163
<div className="opaque-container overflow-hidden flex flex-col mt-4 w-full">
164
+
{/* <div className="h-[260px] w-full bg-test" /> */}
176
165
<div className="flex flex-col p-2">
177
166
<div className="font-bold">{props.title}</div>
178
167
<div className="text-tertiary">{props.description}</div>
179
-
{props.record && (
180
-
<>
181
-
<hr className="border-border-light mt-2 mb-1" />
182
-
<p className="text-xs text-tertiary">
183
-
{props.record?.base_path}
184
-
</p>
185
-
</>
186
-
)}
168
+
<hr className="border-border-light mt-2 mb-1" />
169
+
<p className="text-xs text-tertiary">
170
+
{props.record?.base_path}
171
+
</p>
187
172
</div>
188
173
</div>
189
174
<div className="text-xs text-secondary italic place-self-end pt-2">
···
214
199
);
215
200
};
216
201
217
-
const PublishingTo = (props: {
218
-
publication_uri?: string;
219
-
record?: PubLeafletPublication.Record;
220
-
}) => {
221
-
if (props.publication_uri && props.record) {
222
-
return (
223
-
<div className="flex flex-col gap-1">
224
-
<h3>Publishing to</h3>
225
-
<div className="flex gap-2 items-center p-2 rounded-md bg-[var(--accent-light)]">
226
-
<PubIcon record={props.record} uri={props.publication_uri} />
227
-
<div className="font-bold text-secondary">{props.record.name}</div>
228
-
</div>
229
-
</div>
230
-
);
231
-
}
232
-
233
-
return (
234
-
<div className="flex flex-col gap-1">
235
-
<h3>Publishing as</h3>
236
-
<div className="flex gap-2 items-center p-2 rounded-md bg-[var(--accent-light)]">
237
-
<LooseLeafSmall className="shrink-0" />
238
-
<div className="font-bold text-secondary">Looseleaf</div>
239
-
</div>
240
-
</div>
241
-
);
242
-
};
243
-
244
202
const PublishPostSuccess = (props: {
245
203
post_url: string;
246
-
publication_uri?: string;
204
+
publication_uri: string;
247
205
record: Props["record"];
248
206
posts_in_pub: number;
249
207
}) => {
250
-
let uri = props.publication_uri ? new AtUri(props.publication_uri) : null;
208
+
let uri = new AtUri(props.publication_uri);
251
209
return (
252
210
<div className="container p-4 m-3 sm:m-4 flex flex-col gap-1 justify-center text-center w-fit h-fit mx-auto">
253
211
<PublishIllustration posts_in_pub={props.posts_in_pub} />
254
212
<h2 className="pt-2">Published!</h2>
255
-
{uri && props.record ? (
256
-
<Link
257
-
className="hover:no-underline! font-bold place-self-center pt-2"
258
-
href={`/lish/${uri.host}/${encodeURIComponent(props.record.name || "")}/dashboard`}
259
-
>
260
-
<ButtonPrimary>Back to Dashboard</ButtonPrimary>
261
-
</Link>
262
-
) : (
263
-
<Link
264
-
className="hover:no-underline! font-bold place-self-center pt-2"
265
-
href="/"
266
-
>
267
-
<ButtonPrimary>Back to Home</ButtonPrimary>
268
-
</Link>
269
-
)}
213
+
<Link
214
+
className="hover:no-underline! font-bold place-self-center pt-2"
215
+
href={`/lish/${uri.host}/${encodeURIComponent(props.record?.name || "")}/dashboard`}
216
+
>
217
+
<ButtonPrimary>Back to Dashboard</ButtonPrimary>
218
+
</Link>
270
219
<a href={props.post_url}>See published post</a>
271
220
</div>
272
221
);
+9
-63
app/[leaflet_id]/publish/page.tsx
+9
-63
app/[leaflet_id]/publish/page.tsx
···
13
13
type Props = {
14
14
// this is now a token id not leaflet! Should probs rename
15
15
params: Promise<{ leaflet_id: string }>;
16
-
searchParams: Promise<{
17
-
publication_uri: string;
18
-
title: string;
19
-
description: string;
20
-
entitiesToDelete: string;
21
-
}>;
22
16
};
23
17
export default async function PublishLeafletPage(props: Props) {
24
18
let leaflet_id = (await props.params).leaflet_id;
···
33
27
*,
34
28
documents_in_publications(count)
35
29
),
36
-
documents(*)),
37
-
leaflets_to_documents(
38
-
*,
39
-
documents(*)
40
-
)`,
30
+
documents(*))`,
41
31
)
42
32
.eq("id", leaflet_id)
43
33
.single();
44
34
let rootEntity = data?.root_entity;
45
-
46
-
// Try to find publication from leaflets_in_publications first
47
-
let publication = data?.leaflets_in_publications[0]?.publications;
48
-
49
-
// If not found, check if publication_uri is in searchParams
50
-
if (!publication) {
51
-
let pub_uri = (await props.searchParams).publication_uri;
52
-
if (pub_uri) {
53
-
console.log(decodeURIComponent(pub_uri));
54
-
let { data: pubData, error } = await supabaseServerClient
55
-
.from("publications")
56
-
.select("*, documents_in_publications(count)")
57
-
.eq("uri", decodeURIComponent(pub_uri))
58
-
.single();
59
-
console.log(error);
60
-
publication = pubData;
61
-
}
62
-
}
63
-
64
-
// Check basic data requirements
65
-
if (!data || !rootEntity)
35
+
if (!data || !rootEntity || !data.leaflets_in_publications[0])
66
36
return (
67
37
<div>
68
38
missin something
···
72
42
73
43
let identity = await getIdentityData();
74
44
if (!identity || !identity.atp_did) return null;
75
-
76
-
// Get title and description from either source
77
-
let title =
78
-
data.leaflets_in_publications[0]?.title ||
79
-
data.leaflets_to_documents[0]?.title ||
80
-
decodeURIComponent((await props.searchParams).title || "");
81
-
let description =
82
-
data.leaflets_in_publications[0]?.description ||
83
-
data.leaflets_to_documents[0]?.description ||
84
-
decodeURIComponent((await props.searchParams).description || "");
85
-
45
+
let pub = data.leaflets_in_publications[0];
86
46
let agent = new AtpAgent({ service: "https://public.api.bsky.app" });
87
-
let profile = await agent.getProfile({ actor: identity.atp_did });
88
47
89
-
// Parse entitiesToDelete from URL params
90
-
let searchParams = await props.searchParams;
91
-
let entitiesToDelete: string[] = [];
92
-
try {
93
-
if (searchParams.entitiesToDelete) {
94
-
entitiesToDelete = JSON.parse(
95
-
decodeURIComponent(searchParams.entitiesToDelete),
96
-
);
97
-
}
98
-
} catch (e) {
99
-
// If parsing fails, just use empty array
100
-
}
101
-
48
+
let profile = await agent.getProfile({ actor: identity.atp_did });
102
49
return (
103
50
<ReplicacheProvider
104
51
rootEntity={rootEntity}
···
110
57
leaflet_id={leaflet_id}
111
58
root_entity={rootEntity}
112
59
profile={profile.data}
113
-
title={title}
114
-
description={description}
115
-
publication_uri={publication?.uri}
116
-
record={publication?.record as PubLeafletPublication.Record | undefined}
117
-
posts_in_pub={publication?.documents_in_publications[0]?.count}
118
-
entitiesToDelete={entitiesToDelete}
60
+
title={pub.title}
61
+
publication_uri={pub.publication}
62
+
description={pub.description}
63
+
record={pub.publications?.record as PubLeafletPublication.Record}
64
+
posts_in_pub={pub.publications?.documents_in_publications[0].count}
119
65
/>
120
66
</ReplicacheProvider>
121
67
);
+17
-80
app/api/inngest/functions/index_post_mention.ts
+17
-80
app/api/inngest/functions/index_post_mention.ts
···
3
3
import { AtpAgent, AtUri } from "@atproto/api";
4
4
import { Json } from "supabase/database.types";
5
5
import { ids } from "lexicons/api/lexicons";
6
-
import { Notification, pingIdentityToUpdateNotification } from "src/notifications";
7
-
import { v7 } from "uuid";
8
-
import { idResolver } from "app/(home-pages)/reader/idResolver";
9
6
10
7
export const index_post_mention = inngest.createFunction(
11
8
{ id: "index_post_mention" },
···
14
11
let url = new URL(event.data.document_link);
15
12
let path = url.pathname.split("/").filter(Boolean);
16
13
17
-
// Check if this is a standalone document URL (/p/didOrHandle/rkey/...)
18
-
const isStandaloneDoc = path[0] === "p" && path.length >= 3;
14
+
let { data: pub, error } = await supabaseServerClient
15
+
.from("publications")
16
+
.select("*")
17
+
.eq("record->>base_path", url.host)
18
+
.single();
19
19
20
-
let documentUri: string;
21
-
let authorDid: string;
22
-
23
-
if (isStandaloneDoc) {
24
-
// Standalone doc: /p/didOrHandle/rkey/l-quote/...
25
-
const didOrHandle = decodeURIComponent(path[1]);
26
-
const rkey = path[2];
27
-
28
-
// Resolve handle to DID if necessary
29
-
let did = didOrHandle;
30
-
if (!didOrHandle.startsWith("did:")) {
31
-
const resolved = await step.run("resolve-handle", async () => {
32
-
return idResolver.handle.resolve(didOrHandle);
33
-
});
34
-
if (!resolved) {
35
-
return { message: `Could not resolve handle: ${didOrHandle}` };
36
-
}
37
-
did = resolved;
38
-
}
39
-
40
-
documentUri = AtUri.make(did, ids.PubLeafletDocument, rkey).toString();
41
-
authorDid = did;
42
-
} else {
43
-
// Publication post: look up by custom domain
44
-
let { data: pub, error } = await supabaseServerClient
45
-
.from("publications")
46
-
.select("*")
47
-
.eq("record->>base_path", url.host)
48
-
.single();
49
-
50
-
if (!pub) {
51
-
return {
52
-
message: `No publication found for ${url.host}/${path[0]}`,
53
-
error,
54
-
};
55
-
}
56
-
57
-
documentUri = AtUri.make(
58
-
pub.identity_did,
59
-
ids.PubLeafletDocument,
60
-
path[0],
61
-
).toString();
62
-
authorDid = pub.identity_did;
20
+
if (!pub) {
21
+
return {
22
+
message: `No publication found for ${url.host}/${path[0]}`,
23
+
error,
24
+
};
63
25
}
64
26
65
27
let bsky_post = await step.run("get-bsky-post-data", async () => {
···
76
38
}
77
39
78
40
await step.run("index-bsky-post", async () => {
79
-
await supabaseServerClient.from("bsky_posts").upsert({
41
+
await supabaseServerClient.from("bsky_posts").insert({
80
42
uri: bsky_post.uri,
81
43
cid: bsky_post.cid,
82
44
post_view: bsky_post as Json,
83
45
});
84
-
await supabaseServerClient.from("document_mentions_in_bsky").upsert({
46
+
await supabaseServerClient.from("document_mentions_in_bsky").insert({
85
47
uri: bsky_post.uri,
86
-
document: documentUri,
48
+
document: AtUri.make(
49
+
pub.identity_did,
50
+
ids.PubLeafletDocument,
51
+
path[0],
52
+
).toString(),
87
53
link: event.data.document_link,
88
54
});
89
-
});
90
-
91
-
await step.run("create-notification", async () => {
92
-
// Only create notification if the quote is from someone other than the author
93
-
if (bsky_post.author.did !== authorDid) {
94
-
// Check if a notification already exists for this post and recipient
95
-
const { data: existingNotification } = await supabaseServerClient
96
-
.from("notifications")
97
-
.select("id")
98
-
.eq("recipient", authorDid)
99
-
.eq("data->>type", "quote")
100
-
.eq("data->>bsky_post_uri", bsky_post.uri)
101
-
.eq("data->>document_uri", documentUri)
102
-
.single();
103
-
104
-
if (!existingNotification) {
105
-
const notification: Notification = {
106
-
id: v7(),
107
-
recipient: authorDid,
108
-
data: {
109
-
type: "quote",
110
-
bsky_post_uri: bsky_post.uri,
111
-
document_uri: documentUri,
112
-
},
113
-
};
114
-
await supabaseServerClient.from("notifications").insert(notification);
115
-
await pingIdentityToUpdateNotification(authorDid);
116
-
}
117
-
}
118
55
});
119
56
},
120
57
);
+4
-33
app/api/rpc/[command]/getFactsFromHomeLeaflets.ts
+4
-33
app/api/rpc/[command]/getFactsFromHomeLeaflets.ts
···
4
4
import { makeRoute } from "../lib";
5
5
import type { Env } from "./route";
6
6
import { scanIndexLocal } from "src/replicache/utils";
7
+
import { getBlocksWithTypeLocal } from "src/hooks/queries/useBlocks";
7
8
import * as base64 from "base64-js";
8
9
import { YJSFragmentToString } from "components/Blocks/TextBlock/RenderYJSFragment";
9
10
import { applyUpdate, Doc } from "yjs";
···
34
35
let scan = scanIndexLocal(facts[token]);
35
36
let [root] = scan.eav(token, "root/page");
36
37
let rootEntity = root?.data.value || token;
37
-
38
-
// Check page type to determine which blocks to look up
39
-
let [pageType] = scan.eav(rootEntity, "page/type");
40
-
let isCanvas = pageType?.data.value === "canvas";
41
-
42
-
// Get blocks and sort by position
43
-
let rawBlocks = isCanvas
44
-
? scan.eav(rootEntity, "canvas/block").sort((a, b) => {
45
-
if (a.data.position.y === b.data.position.y)
46
-
return a.data.position.x - b.data.position.x;
47
-
return a.data.position.y - b.data.position.y;
48
-
})
49
-
: scan.eav(rootEntity, "card/block").sort((a, b) => {
50
-
if (a.data.position === b.data.position)
51
-
return a.id > b.id ? 1 : -1;
52
-
return a.data.position > b.data.position ? 1 : -1;
53
-
});
54
-
55
-
// Map to get type and filter for text/heading
56
-
let blocks = rawBlocks
57
-
.map((b) => {
58
-
let type = scan.eav(b.data.value, "block/type")[0];
59
-
if (
60
-
!type ||
61
-
(type.data.value !== "text" && type.data.value !== "heading")
62
-
)
63
-
return null;
64
-
return b.data;
65
-
})
66
-
.filter((b): b is NonNullable<typeof b> => b !== null);
67
-
68
-
let title = blocks[0];
69
-
38
+
let [title] = getBlocksWithTypeLocal(facts[token], rootEntity).filter(
39
+
(b) => b.type === "text" || b.type === "heading",
40
+
);
70
41
if (!title) titles[token] = "Untitled";
71
42
else {
72
43
let [content] = scan.eav(title.value, "block/text");
+2
-4
app/api/rpc/[command]/get_leaflet_data.ts
+2
-4
app/api/rpc/[command]/get_leaflet_data.ts
···
7
7
>;
8
8
9
9
const leaflets_in_publications_query = `leaflets_in_publications(*, publications(*), documents(*))`;
10
-
const leaflets_to_documents_query = `leaflets_to_documents(*, documents(*))`;
11
10
export const get_leaflet_data = makeRoute({
12
11
route: "get_leaflet_data",
13
12
input: z.object({
···
19
18
.from("permission_tokens")
20
19
.select(
21
20
`*,
22
-
permission_token_rights(*, entity_sets(permission_tokens(${leaflets_in_publications_query}, ${leaflets_to_documents_query}))),
21
+
permission_token_rights(*, entity_sets(permission_tokens(${leaflets_in_publications_query}))),
23
22
custom_domain_routes!custom_domain_routes_edit_permission_token_fkey(*),
24
-
${leaflets_in_publications_query},
25
-
${leaflets_to_documents_query}`,
23
+
${leaflets_in_publications_query}`,
26
24
)
27
25
.eq("id", token_id)
28
26
.single();
-1
app/globals.css
-1
app/globals.css
+10
app/lish/Subscribe.tsx
+10
app/lish/Subscribe.tsx
···
186
186
pub_uri: string;
187
187
base_url: string;
188
188
subscribers: { identity: string }[];
189
+
pub_creator?: string;
189
190
}) => {
190
191
let { identity } = useIdentityData();
191
192
let searchParams = useSearchParams();
···
195
196
let subscribed =
196
197
identity?.atp_did &&
197
198
props.subscribers.find((s) => s.identity === identity.atp_did);
199
+
200
+
// Check if the logged-in user is the publication owner
201
+
let isOwner = identity?.atp_did && props.pub_creator === identity.atp_did;
198
202
199
203
if (successModalOpen)
200
204
return (
···
203
207
setOpen={setSuccessModalOpen}
204
208
/>
205
209
);
210
+
211
+
// Don't allow users to subscribe to their own publication
212
+
if (isOwner) {
213
+
return null;
214
+
}
215
+
206
216
if (subscribed) {
207
217
return <ManageSubscription {...props} />;
208
218
}
-24
app/lish/[did]/[publication]/PublicationHomeLayout.tsx
-24
app/lish/[did]/[publication]/PublicationHomeLayout.tsx
···
1
-
"use client";
2
-
3
-
import { usePreserveScroll } from "src/hooks/usePreserveScroll";
4
-
5
-
export function PublicationHomeLayout(props: {
6
-
uri: string;
7
-
showPageBackground: boolean;
8
-
children: React.ReactNode;
9
-
}) {
10
-
let { ref } = usePreserveScroll<HTMLDivElement>(props.uri);
11
-
return (
12
-
<div
13
-
ref={props.showPageBackground ? null : ref}
14
-
className={`pubWrapper flex flex-col sm:py-6 h-full ${props.showPageBackground ? "max-w-prose mx-auto sm:px-0 px-[6px] py-2" : "w-full overflow-y-scroll"}`}
15
-
>
16
-
<div
17
-
ref={!props.showPageBackground ? null : ref}
18
-
className={`pub sm:max-w-prose max-w-(--page-width-units) w-[1000px] mx-auto px-3 sm:px-4 py-5 ${props.showPageBackground ? "overflow-auto h-full bg-[rgba(var(--bg-page),var(--bg-page-alpha))] border border-border rounded-lg" : "h-fit"}`}
19
-
>
20
-
{props.children}
21
-
</div>
22
-
</div>
23
-
);
24
-
}
+3
-9
app/lish/[did]/[publication]/[rkey]/BaseTextBlock.tsx
+3
-9
app/lish/[did]/[publication]/[rkey]/BaseTextBlock.tsx
···
36
36
${isStrikethrough ? "line-through decoration-tertiary" : ""}
37
37
${isHighlighted ? "highlight bg-highlight-1" : ""}`.replaceAll("\n", " ");
38
38
39
-
// Split text by newlines and insert <br> tags
40
-
const textParts = segment.text.split('\n');
41
-
const renderedText = textParts.flatMap((part, i) =>
42
-
i < textParts.length - 1 ? [part, <br key={`br-${counter}-${i}`} />] : [part]
43
-
);
44
-
45
39
if (isCode) {
46
40
children.push(
47
41
<code key={counter} className={className} id={id?.id}>
48
-
{renderedText}
42
+
{segment.text}
49
43
</code>,
50
44
);
51
45
} else if (link) {
···
56
50
className={`text-accent-contrast hover:underline ${className}`}
57
51
target="_blank"
58
52
>
59
-
{renderedText}
53
+
{segment.text}
60
54
</a>,
61
55
);
62
56
} else {
63
57
children.push(
64
58
<span key={counter} className={className} id={id?.id}>
65
-
{renderedText}
59
+
{segment.text}
66
60
</span>,
67
61
);
68
62
}
+26
-19
app/lish/[did]/[publication]/[rkey]/CanvasPage.tsx
+26
-19
app/lish/[did]/[publication]/[rkey]/CanvasPage.tsx
···
21
21
import { PostHeader } from "./PostHeader/PostHeader";
22
22
import { useDrawerOpen } from "./Interactions/InteractionDrawer";
23
23
import { PollData } from "./fetchPollData";
24
-
import { SharedPageProps } from "./PostPages";
25
24
26
25
export function CanvasPage({
26
+
document,
27
27
blocks,
28
+
did,
29
+
profile,
30
+
preferences,
31
+
pubRecord,
32
+
prerenderedCodeBlocks,
33
+
bskyPostData,
34
+
pollData,
35
+
document_uri,
36
+
pageId,
37
+
pageOptions,
38
+
fullPageScroll,
28
39
pages,
29
-
...props
30
-
}: Omit<SharedPageProps, "allPages"> & {
40
+
}: {
41
+
document_uri: string;
42
+
document: PostPageData;
31
43
blocks: PubLeafletPagesCanvas.Block[];
44
+
profile: ProfileViewDetailed;
45
+
pubRecord: PubLeafletPublication.Record;
46
+
did: string;
47
+
prerenderedCodeBlocks?: Map<string, string>;
48
+
bskyPostData: AppBskyFeedDefs.PostView[];
49
+
pollData: PollData[];
50
+
preferences: { showComments?: boolean };
51
+
pageId?: string;
52
+
pageOptions?: React.ReactNode;
53
+
fullPageScroll: boolean;
32
54
pages: (PubLeafletPagesLinearDocument.Main | PubLeafletPagesCanvas.Main)[];
33
55
}) {
34
-
const {
35
-
document,
36
-
did,
37
-
profile,
38
-
preferences,
39
-
pubRecord,
40
-
theme,
41
-
prerenderedCodeBlocks,
42
-
bskyPostData,
43
-
pollData,
44
-
document_uri,
45
-
pageId,
46
-
pageOptions,
47
-
fullPageScroll,
48
-
hasPageBackground,
49
-
} = props;
50
56
if (!document) return null;
51
57
58
+
let hasPageBackground = !!pubRecord.theme?.showPageBackground;
52
59
let isSubpage = !!pageId;
53
60
let drawer = useDrawerOpen(document_uri);
54
61
-146
app/lish/[did]/[publication]/[rkey]/DocumentPageRenderer.tsx
-146
app/lish/[did]/[publication]/[rkey]/DocumentPageRenderer.tsx
···
1
-
import { AtpAgent } from "@atproto/api";
2
-
import { AtUri } from "@atproto/syntax";
3
-
import { ids } from "lexicons/api/lexicons";
4
-
import {
5
-
PubLeafletBlocksBskyPost,
6
-
PubLeafletDocument,
7
-
PubLeafletPagesLinearDocument,
8
-
PubLeafletPagesCanvas,
9
-
PubLeafletPublication,
10
-
} from "lexicons/api";
11
-
import { QuoteHandler } from "./QuoteHandler";
12
-
import {
13
-
PublicationBackgroundProvider,
14
-
PublicationThemeProvider,
15
-
} from "components/ThemeManager/PublicationThemeProvider";
16
-
import { getPostPageData } from "./getPostPageData";
17
-
import { PostPageContextProvider } from "./PostPageContext";
18
-
import { PostPages } from "./PostPages";
19
-
import { extractCodeBlocks } from "./extractCodeBlocks";
20
-
import { LeafletLayout } from "components/LeafletLayout";
21
-
import { fetchPollData } from "./fetchPollData";
22
-
23
-
export async function DocumentPageRenderer({
24
-
did,
25
-
rkey,
26
-
}: {
27
-
did: string;
28
-
rkey: string;
29
-
}) {
30
-
let agent = new AtpAgent({
31
-
service: "https://public.api.bsky.app",
32
-
fetch: (...args) =>
33
-
fetch(args[0], {
34
-
...args[1],
35
-
next: { revalidate: 3600 },
36
-
}),
37
-
});
38
-
39
-
let [document, profile] = await Promise.all([
40
-
getPostPageData(AtUri.make(did, ids.PubLeafletDocument, rkey).toString()),
41
-
agent.getProfile({ actor: did }),
42
-
]);
43
-
44
-
if (!document?.data)
45
-
return (
46
-
<div className="bg-bg-leaflet h-full p-3 text-center relative">
47
-
<div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 max-w-md w-full">
48
-
<div className=" px-3 py-4 opaque-container flex flex-col gap-1 mx-2 ">
49
-
<h3>Sorry, post not found!</h3>
50
-
<p>
51
-
This may be a glitch on our end. If the issue persists please{" "}
52
-
<a href="mailto:contact@leaflet.pub">send us a note</a>.
53
-
</p>
54
-
</div>
55
-
</div>
56
-
</div>
57
-
);
58
-
59
-
let record = document.data as PubLeafletDocument.Record;
60
-
let bskyPosts =
61
-
record.pages.flatMap((p) => {
62
-
let page = p as PubLeafletPagesLinearDocument.Main;
63
-
return page.blocks?.filter(
64
-
(b) => b.block.$type === ids.PubLeafletBlocksBskyPost,
65
-
);
66
-
}) || [];
67
-
68
-
// Batch bsky posts into groups of 25 and fetch in parallel
69
-
let bskyPostBatches = [];
70
-
for (let i = 0; i < bskyPosts.length; i += 25) {
71
-
bskyPostBatches.push(bskyPosts.slice(i, i + 25));
72
-
}
73
-
74
-
let bskyPostResponses = await Promise.all(
75
-
bskyPostBatches.map((batch) =>
76
-
agent.getPosts(
77
-
{
78
-
uris: batch.map((p) => {
79
-
let block = p?.block as PubLeafletBlocksBskyPost.Main;
80
-
return block.postRef.uri;
81
-
}),
82
-
},
83
-
{ headers: {} },
84
-
),
85
-
),
86
-
);
87
-
88
-
let bskyPostData =
89
-
bskyPostResponses.length > 0
90
-
? bskyPostResponses.flatMap((response) => response.data.posts)
91
-
: [];
92
-
93
-
// Extract poll blocks and fetch vote data
94
-
let pollBlocks = record.pages.flatMap((p) => {
95
-
let page = p as PubLeafletPagesLinearDocument.Main;
96
-
return (
97
-
page.blocks?.filter((b) => b.block.$type === ids.PubLeafletBlocksPoll) ||
98
-
[]
99
-
);
100
-
});
101
-
let pollData = await fetchPollData(
102
-
pollBlocks.map((b) => (b.block as any).pollRef.uri),
103
-
);
104
-
105
-
// Get theme from publication or document (for standalone docs)
106
-
let pubRecord = document.documents_in_publications[0]?.publications
107
-
?.record as PubLeafletPublication.Record | undefined;
108
-
let theme = pubRecord?.theme || record.theme || null;
109
-
let pub_creator =
110
-
document.documents_in_publications[0]?.publications?.identity_did || did;
111
-
let isStandalone = !pubRecord;
112
-
113
-
let firstPage = record.pages[0];
114
-
115
-
let firstPageBlocks =
116
-
(
117
-
firstPage as
118
-
| PubLeafletPagesLinearDocument.Main
119
-
| PubLeafletPagesCanvas.Main
120
-
).blocks || [];
121
-
let prerenderedCodeBlocks = await extractCodeBlocks(firstPageBlocks);
122
-
123
-
return (
124
-
<PostPageContextProvider value={document}>
125
-
<PublicationThemeProvider theme={theme} pub_creator={pub_creator} isStandalone={isStandalone}>
126
-
<PublicationBackgroundProvider theme={theme} pub_creator={pub_creator}>
127
-
<LeafletLayout>
128
-
<PostPages
129
-
document_uri={document.uri}
130
-
preferences={pubRecord?.preferences || {}}
131
-
pubRecord={pubRecord}
132
-
profile={JSON.parse(JSON.stringify(profile.data))}
133
-
document={document}
134
-
bskyPostData={bskyPostData}
135
-
did={did}
136
-
prerenderedCodeBlocks={prerenderedCodeBlocks}
137
-
pollData={pollData}
138
-
/>
139
-
</LeafletLayout>
140
-
141
-
<QuoteHandler />
142
-
</PublicationBackgroundProvider>
143
-
</PublicationThemeProvider>
144
-
</PostPageContextProvider>
145
-
);
146
-
}
+4
app/lish/[did]/[publication]/[rkey]/LinearDocumentPage.tsx
+4
app/lish/[did]/[publication]/[rkey]/LinearDocumentPage.tsx
+21
-14
app/lish/[did]/[publication]/[rkey]/PostHeader/PostHeader.tsx
+21
-14
app/lish/[did]/[publication]/[rkey]/PostHeader/PostHeader.tsx
···
27
27
28
28
let record = document?.data as PubLeafletDocument.Record;
29
29
let profile = props.profile;
30
-
let pub = props.data?.documents_in_publications[0]?.publications;
30
+
let pub = props.data?.documents_in_publications[0].publications;
31
+
let pubRecord = pub?.record as PubLeafletPublication.Record;
31
32
32
33
const formattedDate = useLocalizedDate(
33
34
record.publishedAt || new Date().toISOString(),
···
35
36
year: "numeric",
36
37
month: "long",
37
38
day: "2-digit",
38
-
},
39
+
}
39
40
);
40
41
41
-
if (!document?.data) return;
42
+
if (!document?.data || !document.documents_in_publications[0].publications)
43
+
return;
42
44
return (
43
45
<div
44
46
className="max-w-prose w-full mx-auto px-3 sm:px-4 sm:pt-3 pt-2"
···
46
48
>
47
49
<div className="pubHeader flex flex-col pb-5">
48
50
<div className="flex justify-between w-full">
49
-
{pub && (
50
-
<SpeedyLink
51
-
className="font-bold hover:no-underline text-accent-contrast"
52
-
href={document && getPublicationURL(pub)}
53
-
>
54
-
{pub?.name}
55
-
</SpeedyLink>
56
-
)}
51
+
<SpeedyLink
52
+
className="font-bold hover:no-underline text-accent-contrast"
53
+
href={
54
+
document &&
55
+
getPublicationURL(
56
+
document.documents_in_publications[0].publications,
57
+
)
58
+
}
59
+
>
60
+
{pub?.name}
61
+
</SpeedyLink>
57
62
{identity &&
58
-
pub &&
59
-
identity.atp_did === pub.identity_did &&
63
+
identity.atp_did ===
64
+
document.documents_in_publications[0]?.publications
65
+
.identity_did &&
60
66
document.leaflets_in_publications[0] && (
61
67
<a
62
68
className=" rounded-full flex place-items-center"
···
84
90
) : null}
85
91
{record.publishedAt ? (
86
92
<>
87
-
|<p>{formattedDate}</p>
93
+
|
94
+
<p>{formattedDate}</p>
88
95
</>
89
96
) : null}
90
97
|{" "}
+75
-110
app/lish/[did]/[publication]/[rkey]/PostPages.tsx
+75
-110
app/lish/[did]/[publication]/[rkey]/PostPages.tsx
···
98
98
};
99
99
});
100
100
101
-
// Shared props type for both page components
102
-
export type SharedPageProps = {
103
-
document: PostPageData;
104
-
did: string;
105
-
profile: ProfileViewDetailed;
106
-
preferences: { showComments?: boolean };
107
-
pubRecord?: PubLeafletPublication.Record;
108
-
theme?: PubLeafletPublication.Theme | null;
109
-
prerenderedCodeBlocks?: Map<string, string>;
110
-
bskyPostData: AppBskyFeedDefs.PostView[];
111
-
pollData: PollData[];
112
-
document_uri: string;
113
-
fullPageScroll: boolean;
114
-
hasPageBackground: boolean;
115
-
pageId?: string;
116
-
pageOptions?: React.ReactNode;
117
-
allPages: (PubLeafletPagesLinearDocument.Main | PubLeafletPagesCanvas.Main)[];
118
-
};
119
-
120
-
// Component that renders either Canvas or Linear page based on page type
121
-
function PageRenderer({
122
-
page,
123
-
...sharedProps
124
-
}: {
125
-
page: PubLeafletPagesLinearDocument.Main | PubLeafletPagesCanvas.Main;
126
-
} & SharedPageProps) {
127
-
const isCanvas = PubLeafletPagesCanvas.isMain(page);
128
-
129
-
if (isCanvas) {
130
-
return (
131
-
<CanvasPage
132
-
{...sharedProps}
133
-
blocks={(page as PubLeafletPagesCanvas.Main).blocks || []}
134
-
pages={sharedProps.allPages}
135
-
/>
136
-
);
137
-
}
138
-
139
-
return (
140
-
<LinearDocumentPage
141
-
{...sharedProps}
142
-
blocks={(page as PubLeafletPagesLinearDocument.Main).blocks || []}
143
-
/>
144
-
);
145
-
}
146
-
147
101
export function PostPages({
148
102
document,
103
+
blocks,
149
104
did,
150
105
profile,
151
106
preferences,
···
157
112
}: {
158
113
document_uri: string;
159
114
document: PostPageData;
115
+
blocks: PubLeafletPagesLinearDocument.Block[];
160
116
profile: ProfileViewDetailed;
161
-
pubRecord?: PubLeafletPublication.Record;
117
+
pubRecord: PubLeafletPublication.Record;
162
118
did: string;
163
119
prerenderedCodeBlocks?: Map<string, string>;
164
120
bskyPostData: AppBskyFeedDefs.PostView[];
···
167
123
}) {
168
124
let drawer = useDrawerOpen(document_uri);
169
125
useInitializeOpenPages();
170
-
let openPageIds = useOpenPages();
171
-
if (!document) return null;
126
+
let pages = useOpenPages();
127
+
if (!document || !document.documents_in_publications[0].publications)
128
+
return null;
172
129
130
+
let hasPageBackground = !!pubRecord.theme?.showPageBackground;
173
131
let record = document.data as PubLeafletDocument.Record;
174
-
let theme = pubRecord?.theme || record.theme || null;
175
-
// For publication posts, respect the publication's showPageBackground setting
176
-
// For standalone documents, default to showing page background
177
-
let isInPublication = !!pubRecord;
178
-
let hasPageBackground = isInPublication ? !!theme?.showPageBackground : true;
179
132
let quotesAndMentions = document.quotesAndMentions;
180
133
181
-
let firstPage = record.pages[0] as
182
-
| PubLeafletPagesLinearDocument.Main
183
-
| PubLeafletPagesCanvas.Main;
184
-
185
-
// Canvas pages don't support fullPageScroll well due to fixed 1272px width
186
-
let firstPageIsCanvas = PubLeafletPagesCanvas.isMain(firstPage);
187
-
188
-
// Shared props used for all pages
189
-
const sharedProps: SharedPageProps = {
190
-
document,
191
-
did,
192
-
profile,
193
-
preferences,
194
-
pubRecord,
195
-
theme,
196
-
prerenderedCodeBlocks,
197
-
bskyPostData,
198
-
pollData,
199
-
document_uri,
200
-
hasPageBackground,
201
-
allPages: record.pages as (
202
-
| PubLeafletPagesLinearDocument.Main
203
-
| PubLeafletPagesCanvas.Main
204
-
)[],
205
-
fullPageScroll:
206
-
!hasPageBackground &&
207
-
!drawer &&
208
-
openPageIds.length === 0 &&
209
-
!firstPageIsCanvas,
210
-
};
211
-
134
+
let fullPageScroll = !hasPageBackground && !drawer && pages.length === 0;
212
135
return (
213
136
<>
214
-
{!sharedProps.fullPageScroll && <BookendSpacer />}
215
-
216
-
<PageRenderer page={firstPage} {...sharedProps} />
137
+
{!fullPageScroll && <BookendSpacer />}
138
+
<LinearDocumentPage
139
+
document={document}
140
+
blocks={blocks}
141
+
did={did}
142
+
profile={profile}
143
+
fullPageScroll={fullPageScroll}
144
+
pollData={pollData}
145
+
preferences={preferences}
146
+
pubRecord={pubRecord}
147
+
prerenderedCodeBlocks={prerenderedCodeBlocks}
148
+
bskyPostData={bskyPostData}
149
+
document_uri={document_uri}
150
+
/>
217
151
218
152
{drawer && !drawer.pageId && (
219
153
<InteractionDrawer
220
154
document_uri={document.uri}
221
155
comments={
222
-
pubRecord?.preferences?.showComments === false
156
+
pubRecord.preferences?.showComments === false
223
157
? []
224
158
: document.comments_on_documents
225
159
}
···
228
162
/>
229
163
)}
230
164
231
-
{openPageIds.map((pageId) => {
165
+
{pages.map((p) => {
232
166
let page = record.pages.find(
233
-
(p) =>
167
+
(page) =>
234
168
(
235
-
p as
169
+
page as
236
170
| PubLeafletPagesLinearDocument.Main
237
171
| PubLeafletPagesCanvas.Main
238
-
).id === pageId,
172
+
).id === p,
239
173
) as
240
174
| PubLeafletPagesLinearDocument.Main
241
175
| PubLeafletPagesCanvas.Main
242
176
| undefined;
243
-
244
177
if (!page) return null;
245
178
179
+
const isCanvas = PubLeafletPagesCanvas.isMain(page);
180
+
246
181
return (
247
-
<Fragment key={pageId}>
182
+
<Fragment key={p}>
248
183
<SandwichSpacer />
249
-
<PageRenderer
250
-
page={page}
251
-
{...sharedProps}
252
-
fullPageScroll={false}
253
-
pageId={page.id}
254
-
pageOptions={
255
-
<PageOptions
256
-
onClick={() => closePage(page.id!)}
257
-
hasPageBackground={hasPageBackground}
258
-
/>
259
-
}
260
-
/>
184
+
{isCanvas ? (
185
+
<CanvasPage
186
+
fullPageScroll={false}
187
+
document={document}
188
+
blocks={(page as PubLeafletPagesCanvas.Main).blocks}
189
+
did={did}
190
+
preferences={preferences}
191
+
profile={profile}
192
+
pubRecord={pubRecord}
193
+
prerenderedCodeBlocks={prerenderedCodeBlocks}
194
+
pollData={pollData}
195
+
bskyPostData={bskyPostData}
196
+
document_uri={document_uri}
197
+
pageId={page.id}
198
+
pages={record.pages as PubLeafletPagesLinearDocument.Main[]}
199
+
pageOptions={
200
+
<PageOptions
201
+
onClick={() => closePage(page?.id!)}
202
+
hasPageBackground={hasPageBackground}
203
+
/>
204
+
}
205
+
/>
206
+
) : (
207
+
<LinearDocumentPage
208
+
fullPageScroll={false}
209
+
document={document}
210
+
blocks={(page as PubLeafletPagesLinearDocument.Main).blocks}
211
+
did={did}
212
+
preferences={preferences}
213
+
pubRecord={pubRecord}
214
+
pollData={pollData}
215
+
prerenderedCodeBlocks={prerenderedCodeBlocks}
216
+
bskyPostData={bskyPostData}
217
+
document_uri={document_uri}
218
+
pageId={page.id}
219
+
pageOptions={
220
+
<PageOptions
221
+
onClick={() => closePage(page?.id!)}
222
+
hasPageBackground={hasPageBackground}
223
+
/>
224
+
}
225
+
/>
226
+
)}
261
227
{drawer && drawer.pageId === page.id && (
262
228
<InteractionDrawer
263
229
pageId={page.id}
264
230
document_uri={document.uri}
265
231
comments={
266
-
pubRecord?.preferences?.showComments === false
232
+
pubRecord.preferences?.showComments === false
267
233
? []
268
234
: document.comments_on_documents
269
235
}
···
274
240
</Fragment>
275
241
);
276
242
})}
277
-
278
-
{!sharedProps.fullPageScroll && <BookendSpacer />}
243
+
{!fullPageScroll && <BookendSpacer />}
279
244
</>
280
245
);
281
246
}
+5
-4
app/lish/[did]/[publication]/[rkey]/PublishedPageBlock.tsx
+5
-4
app/lish/[did]/[publication]/[rkey]/PublishedPageBlock.tsx
···
106
106
<div className="grow">
107
107
{title && (
108
108
<div
109
-
className={`pageBlockOne outline-none resize-none align-top gap-2 ${title.$type === "pub.leaflet.blocks.header" ? "font-bold text-base" : ""}`}
109
+
className={`pageBlockOne outline-none resize-none align-top flex gap-2 ${title.$type === "pub.leaflet.blocks.header" ? "font-bold text-base" : ""}`}
110
110
>
111
111
<TextBlock
112
112
facets={title.facets}
···
118
118
)}
119
119
{description && (
120
120
<div
121
-
className={`pageBlockLineTwo outline-none resize-none align-top gap-2 ${description.$type === "pub.leaflet.blocks.header" ? "font-bold" : ""}`}
121
+
className={`pageBlockLineTwo outline-none resize-none align-top flex gap-2 ${description.$type === "pub.leaflet.blocks.header" ? "font-bold" : ""}`}
122
122
>
123
123
<TextBlock
124
124
facets={description.facets}
···
151
151
let previewRef = useRef<HTMLDivElement | null>(null);
152
152
let { rootEntity } = useReplicache();
153
153
let data = useContext(PostPageContext);
154
-
let theme = data?.theme;
154
+
let theme = data?.documents_in_publications[0]?.publications
155
+
?.record as PubLeafletPublication.Record;
155
156
let pageWidth = `var(--page-width-unitless)`;
156
-
let cardBorderHidden = !theme?.showPageBackground;
157
+
let cardBorderHidden = !theme.theme?.showPageBackground;
157
158
return (
158
159
<div
159
160
ref={previewRef}
+2
-3
app/lish/[did]/[publication]/[rkey]/extractCodeBlocks.ts
+2
-3
app/lish/[did]/[publication]/[rkey]/extractCodeBlocks.ts
···
1
1
import {
2
2
PubLeafletDocument,
3
3
PubLeafletPagesLinearDocument,
4
-
PubLeafletPagesCanvas,
5
4
PubLeafletBlocksCode,
6
5
} from "lexicons/api";
7
6
import { codeToHtml, bundledLanguagesInfo, bundledThemesInfo } from "shiki";
8
7
9
8
export async function extractCodeBlocks(
10
-
blocks: PubLeafletPagesLinearDocument.Block[] | PubLeafletPagesCanvas.Block[],
9
+
blocks: PubLeafletPagesLinearDocument.Block[],
11
10
): Promise<Map<string, string>> {
12
11
const codeBlocks = new Map<string, string>();
13
12
14
-
// Process all blocks (works for both linear and canvas)
13
+
// Process all pages in the document
15
14
for (let i = 0; i < blocks.length; i++) {
16
15
const block = blocks[i];
17
16
const currentIndex = [i];
+3
-12
app/lish/[did]/[publication]/[rkey]/getPostPageData.ts
+3
-12
app/lish/[did]/[publication]/[rkey]/getPostPageData.ts
···
1
1
import { supabaseServerClient } from "supabase/serverClient";
2
2
import { AtUri } from "@atproto/syntax";
3
-
import { PubLeafletDocument, PubLeafletPublication } from "lexicons/api";
3
+
import { PubLeafletPublication } from "lexicons/api";
4
4
5
5
export async function getPostPageData(uri: string) {
6
6
let { data: document } = await supabaseServerClient
···
23
23
// Fetch constellation backlinks for mentions
24
24
const pubRecord = document.documents_in_publications[0]?.publications
25
25
?.record as PubLeafletPublication.Record;
26
-
let aturi = new AtUri(uri);
27
-
const postUrl = pubRecord
28
-
? `https://${pubRecord?.base_path}/${aturi.rkey}`
29
-
: `https://leaflet.pub/p/${aturi.host}/${aturi.rkey}`;
26
+
const rkey = new AtUri(uri).rkey;
27
+
const postUrl = `https://${pubRecord?.base_path}/${rkey}`;
30
28
const constellationBacklinks = await getConstellationBacklinks(postUrl);
31
29
32
30
// Deduplicate constellation backlinks (same post could appear in both links and embeds)
···
45
43
...uniqueBacklinks,
46
44
];
47
45
48
-
let theme =
49
-
(
50
-
document?.documents_in_publications[0]?.publications
51
-
?.record as PubLeafletPublication.Record
52
-
)?.theme || (document?.data as PubLeafletDocument.Record)?.theme;
53
-
54
46
return {
55
47
...document,
56
48
quotesAndMentions,
57
-
theme,
58
49
};
59
50
}
60
51
+3
-4
app/lish/[did]/[publication]/[rkey]/l-quote/[quote]/opengraph-image.ts
+3
-4
app/lish/[did]/[publication]/[rkey]/l-quote/[quote]/opengraph-image.ts
···
5
5
export const revalidate = 60;
6
6
7
7
export default async function OpenGraphImage(props: {
8
-
params: Promise<{ publication: string; did: string; rkey: string; quote: string }>;
8
+
params: { publication: string; did: string; rkey: string; quote: string };
9
9
}) {
10
-
let params = await props.params;
11
-
let quotePosition = decodeQuotePosition(params.quote);
10
+
let quotePosition = decodeQuotePosition(props.params.quote);
12
11
return getMicroLinkOgImage(
13
-
`/lish/${decodeURIComponent(params.did)}/${decodeURIComponent(params.publication)}/${params.rkey}/l-quote/${params.quote}#${quotePosition?.pageId ? `${quotePosition.pageId}~` : ""}${quotePosition?.start.block.join(".")}_${quotePosition?.start.offset}`,
12
+
`/lish/${decodeURIComponent(props.params.did)}/${decodeURIComponent(props.params.publication)}/${props.params.rkey}/l-quote/${props.params.quote}#${quotePosition?.pageId ? `${quotePosition.pageId}~` : ""}${quotePosition?.start.block.join(".")}_${quotePosition?.start.offset}`,
14
13
{
15
14
width: 620,
16
15
height: 324,
+2
-3
app/lish/[did]/[publication]/[rkey]/opengraph-image.ts
+2
-3
app/lish/[did]/[publication]/[rkey]/opengraph-image.ts
···
4
4
export const revalidate = 60;
5
5
6
6
export default async function OpenGraphImage(props: {
7
-
params: Promise<{ publication: string; did: string; rkey: string }>;
7
+
params: { publication: string; did: string; rkey: string };
8
8
}) {
9
-
let params = await props.params;
10
9
return getMicroLinkOgImage(
11
-
`/lish/${decodeURIComponent(params.did)}/${decodeURIComponent(params.publication)}/${params.rkey}/`,
10
+
`/lish/${decodeURIComponent(props.params.did)}/${decodeURIComponent(props.params.publication)}/${props.params.rkey}/`,
12
11
);
13
12
}
+156
-6
app/lish/[did]/[publication]/[rkey]/page.tsx
+156
-6
app/lish/[did]/[publication]/[rkey]/page.tsx
···
1
1
import { supabaseServerClient } from "supabase/serverClient";
2
2
import { AtUri } from "@atproto/syntax";
3
3
import { ids } from "lexicons/api/lexicons";
4
-
import { PubLeafletDocument } from "lexicons/api";
4
+
import {
5
+
PubLeafletBlocksBskyPost,
6
+
PubLeafletDocument,
7
+
PubLeafletPagesLinearDocument,
8
+
PubLeafletPublication,
9
+
} from "lexicons/api";
5
10
import { Metadata } from "next";
6
-
import { DocumentPageRenderer } from "./DocumentPageRenderer";
11
+
import { AtpAgent } from "@atproto/api";
12
+
import { QuoteHandler } from "./QuoteHandler";
13
+
import { InteractionDrawer } from "./Interactions/InteractionDrawer";
14
+
import {
15
+
PublicationBackgroundProvider,
16
+
PublicationThemeProvider,
17
+
} from "components/ThemeManager/PublicationThemeProvider";
18
+
import { getPostPageData } from "./getPostPageData";
19
+
import { PostPageContextProvider } from "./PostPageContext";
20
+
import { PostPages } from "./PostPages";
21
+
import { extractCodeBlocks } from "./extractCodeBlocks";
22
+
import { LeafletLayout } from "components/LeafletLayout";
23
+
import { fetchPollData } from "./fetchPollData";
7
24
8
25
export async function generateMetadata(props: {
9
26
params: Promise<{ publication: string; did: string; rkey: string }>;
···
40
57
export default async function Post(props: {
41
58
params: Promise<{ publication: string; did: string; rkey: string }>;
42
59
}) {
43
-
let params = await props.params;
44
-
let did = decodeURIComponent(params.did);
45
-
60
+
let did = decodeURIComponent((await props.params).did);
46
61
if (!did)
47
62
return (
48
63
<div className="p-4 text-lg text-center flex flex-col gap-4">
···
53
68
</p>
54
69
</div>
55
70
);
71
+
let agent = new AtpAgent({
72
+
service: "https://public.api.bsky.app",
73
+
fetch: (...args) =>
74
+
fetch(args[0], {
75
+
...args[1],
76
+
next: { revalidate: 3600 },
77
+
}),
78
+
});
79
+
let [document, profile] = await Promise.all([
80
+
getPostPageData(
81
+
AtUri.make(
82
+
did,
83
+
ids.PubLeafletDocument,
84
+
(await props.params).rkey,
85
+
).toString(),
86
+
),
87
+
agent.getProfile({ actor: did }),
88
+
]);
89
+
if (!document?.data || !document.documents_in_publications[0].publications)
90
+
return (
91
+
<div className="bg-bg-leaflet h-full p-3 text-center relative">
92
+
<div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 max-w-md w-full">
93
+
<div className=" px-3 py-4 opaque-container flex flex-col gap-1 mx-2 ">
94
+
<h3>Sorry, post not found!</h3>
95
+
<p>
96
+
This may be a glitch on our end. If the issue persists please{" "}
97
+
<a href="mailto:contact@leaflet.pub">send us a note</a>.
98
+
</p>
99
+
</div>
100
+
</div>
101
+
</div>
102
+
);
103
+
let record = document.data as PubLeafletDocument.Record;
104
+
let bskyPosts =
105
+
record.pages.flatMap((p) => {
106
+
let page = p as PubLeafletPagesLinearDocument.Main;
107
+
return page.blocks?.filter(
108
+
(b) => b.block.$type === ids.PubLeafletBlocksBskyPost,
109
+
);
110
+
}) || [];
56
111
57
-
return <DocumentPageRenderer did={did} rkey={params.rkey} />;
112
+
// Batch bsky posts into groups of 25 and fetch in parallel
113
+
let bskyPostBatches = [];
114
+
for (let i = 0; i < bskyPosts.length; i += 25) {
115
+
bskyPostBatches.push(bskyPosts.slice(i, i + 25));
116
+
}
117
+
118
+
let bskyPostResponses = await Promise.all(
119
+
bskyPostBatches.map((batch) =>
120
+
agent.getPosts(
121
+
{
122
+
uris: batch.map((p) => {
123
+
let block = p?.block as PubLeafletBlocksBskyPost.Main;
124
+
return block.postRef.uri;
125
+
}),
126
+
},
127
+
{ headers: {} },
128
+
),
129
+
),
130
+
);
131
+
132
+
let bskyPostData =
133
+
bskyPostResponses.length > 0
134
+
? bskyPostResponses.flatMap((response) => response.data.posts)
135
+
: [];
136
+
137
+
// Extract poll blocks and fetch vote data
138
+
let pollBlocks = record.pages.flatMap((p) => {
139
+
let page = p as PubLeafletPagesLinearDocument.Main;
140
+
return (
141
+
page.blocks?.filter((b) => b.block.$type === ids.PubLeafletBlocksPoll) ||
142
+
[]
143
+
);
144
+
});
145
+
let pollData = await fetchPollData(
146
+
pollBlocks.map((b) => (b.block as any).pollRef.uri),
147
+
);
148
+
149
+
let pubRecord = document.documents_in_publications[0]?.publications
150
+
.record as PubLeafletPublication.Record;
151
+
152
+
let firstPage = record.pages[0];
153
+
let blocks: PubLeafletPagesLinearDocument.Block[] = [];
154
+
if (PubLeafletPagesLinearDocument.isMain(firstPage)) {
155
+
blocks = firstPage.blocks || [];
156
+
}
157
+
158
+
let prerenderedCodeBlocks = await extractCodeBlocks(blocks);
159
+
160
+
return (
161
+
<PostPageContextProvider value={document}>
162
+
<PublicationThemeProvider
163
+
record={pubRecord}
164
+
pub_creator={
165
+
document.documents_in_publications[0].publications.identity_did
166
+
}
167
+
>
168
+
<PublicationBackgroundProvider
169
+
record={pubRecord}
170
+
pub_creator={
171
+
document.documents_in_publications[0].publications.identity_did
172
+
}
173
+
>
174
+
{/*
175
+
TODO: SCROLL PAGE TO FIT DRAWER
176
+
If the drawer fits without scrolling, dont scroll
177
+
If both drawer and page fit if you scrolled it, scroll it all into the center
178
+
If the drawer and pafe doesn't all fit, scroll to drawer
179
+
180
+
TODO: SROLL BAR
181
+
If there is no drawer && there is no page bg, scroll the entire page
182
+
If there is either a drawer open OR a page background, scroll just the post content
183
+
184
+
TODO: HIGHLIGHTING BORKED
185
+
on chrome, if you scroll backward, things stop working
186
+
seems like if you use an older browser, sel direction is not a thing yet
187
+
*/}
188
+
<LeafletLayout>
189
+
<PostPages
190
+
document_uri={document.uri}
191
+
preferences={pubRecord.preferences || {}}
192
+
pubRecord={pubRecord}
193
+
profile={JSON.parse(JSON.stringify(profile.data))}
194
+
document={document}
195
+
bskyPostData={bskyPostData}
196
+
did={did}
197
+
blocks={blocks}
198
+
prerenderedCodeBlocks={prerenderedCodeBlocks}
199
+
pollData={pollData}
200
+
/>
201
+
</LeafletLayout>
202
+
203
+
<QuoteHandler />
204
+
</PublicationBackgroundProvider>
205
+
</PublicationThemeProvider>
206
+
</PostPageContextProvider>
207
+
);
58
208
}
+1
-3
app/lish/[did]/[publication]/dashboard/DraftList.tsx
+1
-3
app/lish/[did]/[publication]/dashboard/DraftList.tsx
···
26
26
cardBorderHidden={!props.showPageBackground}
27
27
leaflets={leaflets_in_publications
28
28
.filter((l) => !l.documents)
29
-
.filter((l) => !l.archived)
30
29
.map((l) => {
31
30
return {
32
-
archived: l.archived,
33
-
added_at: "",
34
31
token: {
35
32
...l.permission_tokens!,
36
33
leaflets_in_publications: [
···
42
39
},
43
40
],
44
41
},
42
+
added_at: "",
45
43
};
46
44
})}
47
45
initialFacts={pub_data.leaflet_data.facts || {}}
+2
-22
app/lish/[did]/[publication]/dashboard/PublicationSWRProvider.tsx
+2
-22
app/lish/[did]/[publication]/dashboard/PublicationSWRProvider.tsx
···
2
2
3
3
import type { GetPublicationDataReturnType } from "app/api/rpc/[command]/get_publication_data";
4
4
import { callRPC } from "app/api/rpc/client";
5
-
import { createContext, useContext, useEffect } from "react";
6
-
import useSWR, { SWRConfig, KeyedMutator, mutate } from "swr";
7
-
import { produce, Draft } from "immer";
8
-
9
-
export type PublicationData = GetPublicationDataReturnType["result"];
5
+
import { createContext, useContext } from "react";
6
+
import useSWR, { SWRConfig } from "swr";
10
7
11
8
const PublicationContext = createContext({ name: "", did: "" });
12
9
export function PublicationSWRDataProvider(props: {
···
16
13
children: React.ReactNode;
17
14
}) {
18
15
let key = `publication-data-${props.publication_did}-${props.publication_rkey}`;
19
-
useEffect(() => {
20
-
console.log("UPDATING");
21
-
mutate(key, props.publication_data);
22
-
}, [props.publication_data]);
23
16
return (
24
17
<PublicationContext
25
18
value={{ name: props.publication_rkey, did: props.publication_did }}
···
48
41
);
49
42
return { data, mutate };
50
43
}
51
-
52
-
export function mutatePublicationData(
53
-
mutate: KeyedMutator<PublicationData>,
54
-
recipe: (draft: Draft<NonNullable<PublicationData>>) => void,
55
-
) {
56
-
mutate(
57
-
(data) => {
58
-
if (!data) return data;
59
-
return produce(data, recipe);
60
-
},
61
-
{ revalidate: false },
62
-
);
63
-
}
+108
-112
app/lish/[did]/[publication]/dashboard/PublishedPostsLists.tsx
+108
-112
app/lish/[did]/[publication]/dashboard/PublishedPostsLists.tsx
···
13
13
import { MoreOptionsVerticalTiny } from "components/Icons/MoreOptionsVerticalTiny";
14
14
import { DeleteSmall } from "components/Icons/DeleteSmall";
15
15
import { ShareSmall } from "components/Icons/ShareSmall";
16
-
import { ShareButton } from "app/[leaflet_id]/actions/ShareOptions";
16
+
import { ShareButton } from "components/ShareOptions";
17
17
import { SpeedyLink } from "components/SpeedyLink";
18
18
import { QuoteTiny } from "components/Icons/QuoteTiny";
19
19
import { CommentTiny } from "components/Icons/CommentTiny";
20
20
import { useLocalizedDate } from "src/hooks/useLocalizedDate";
21
-
import { LeafletOptions } from "app/(home-pages)/home/LeafletList/LeafletOptions";
22
-
import { StaticLeafletDataContext } from "components/PageSWRDataProvider";
23
21
24
22
export function PublishedPostsList(props: {
25
23
searchValue: string;
···
59
57
let quotes = doc.documents.document_mentions_in_bsky[0]?.count || 0;
60
58
let comments = doc.documents.comments_on_documents[0]?.count || 0;
61
59
62
-
let postLink = data?.publication
63
-
? `${getPublicationURL(data?.publication)}/${new AtUri(doc.documents.uri).rkey}`
64
-
: "";
65
-
66
60
return (
67
61
<Fragment key={doc.documents?.uri}>
68
62
<div className="flex gap-2 w-full ">
···
85
79
</h3>
86
80
</a>
87
81
<div className="flex justify-start align-top flex-row gap-1">
88
-
{leaflet && leaflet.permission_tokens && (
89
-
<>
90
-
<SpeedyLink
91
-
className="pt-[6px]"
92
-
href={`/${leaflet.leaflet}`}
93
-
>
94
-
<EditTiny />
95
-
</SpeedyLink>
96
-
97
-
<StaticLeafletDataContext
98
-
value={{
99
-
...leaflet.permission_tokens,
100
-
leaflets_in_publications: [
101
-
{
102
-
...leaflet,
103
-
publications: publication,
104
-
documents: doc.documents
105
-
? {
106
-
uri: doc.documents.uri,
107
-
indexed_at: doc.documents.indexed_at,
108
-
data: doc.documents.data,
109
-
}
110
-
: null,
111
-
},
112
-
],
113
-
leaflets_to_documents: [],
114
-
blocked_by_admin: null,
115
-
custom_domain_routes: [],
116
-
}}
117
-
>
118
-
<LeafletOptions loggedIn={true} />
119
-
</StaticLeafletDataContext>
120
-
</>
82
+
{leaflet && (
83
+
<SpeedyLink
84
+
className="pt-[6px]"
85
+
href={`/${leaflet.leaflet}`}
86
+
>
87
+
<EditTiny />
88
+
</SpeedyLink>
121
89
)}
90
+
<Options document_uri={doc.documents.uri} />
122
91
</div>
123
92
</div>
124
93
···
164
133
);
165
134
}
166
135
167
-
// function OptionsMenu(props: { document_uri: string }) {
168
-
// let { mutate, data } = usePublicationData();
169
-
// let [state, setState] = useState<"normal" | "confirm">("normal");
136
+
let Options = (props: { document_uri: string }) => {
137
+
return (
138
+
<Menu
139
+
align="end"
140
+
alignOffset={20}
141
+
asChild
142
+
trigger={
143
+
<button className="text-secondary rounded-md selected-outline border-transparent! hover:border-border! h-min">
144
+
<MoreOptionsVerticalTiny />
145
+
</button>
146
+
}
147
+
>
148
+
<>
149
+
<OptionsMenu document_uri={props.document_uri} />
150
+
</>
151
+
</Menu>
152
+
);
153
+
};
170
154
171
-
// if (state === "normal") {
172
-
// return (
173
-
// <>
174
-
// <ShareButton
175
-
// className="justify-end"
176
-
// text={
177
-
// <div className="flex gap-2">
178
-
// Share Post Link
179
-
// <ShareSmall />
180
-
// </div>
181
-
// }
182
-
// subtext=""
183
-
// smokerText="Post link copied!"
184
-
// id="get-post-link"
185
-
// fullLink={postLink?.includes("https") ? postLink : undefined}
186
-
// link={postLink}
187
-
// />
155
+
function OptionsMenu(props: { document_uri: string }) {
156
+
let { mutate, data } = usePublicationData();
157
+
let [state, setState] = useState<"normal" | "confirm">("normal");
188
158
189
-
// <hr className="border-border-light" />
190
-
// <MenuItem
191
-
// className="justify-end"
192
-
// onSelect={async (e) => {
193
-
// e.preventDefault();
194
-
// setState("confirm");
195
-
// return;
196
-
// }}
197
-
// >
198
-
// Delete Post
199
-
// <DeleteSmall />
200
-
// </MenuItem>
201
-
// </>
202
-
// );
203
-
// }
204
-
// if (state === "confirm") {
205
-
// return (
206
-
// <div className="flex flex-col items-center font-bold text-secondary px-2 py-1">
207
-
// Are you sure?
208
-
// <div className="text-sm text-tertiary font-normal">
209
-
// This action cannot be undone!
210
-
// </div>
211
-
// <ButtonPrimary
212
-
// className="mt-2"
213
-
// onClick={async () => {
214
-
// await mutate((data) => {
215
-
// if (!data) return data;
216
-
// return {
217
-
// ...data,
218
-
// publication: {
219
-
// ...data.publication!,
220
-
// leaflets_in_publications:
221
-
// data.publication?.leaflets_in_publications.filter(
222
-
// (l) => l.doc !== props.document_uri,
223
-
// ) || [],
224
-
// documents_in_publications:
225
-
// data.publication?.documents_in_publications.filter(
226
-
// (d) => d.documents?.uri !== props.document_uri,
227
-
// ) || [],
228
-
// },
229
-
// };
230
-
// }, false);
231
-
// await deletePost(props.document_uri);
232
-
// }}
233
-
// >
234
-
// Delete
235
-
// </ButtonPrimary>
236
-
// </div>
237
-
// );
238
-
// }
239
-
//}
159
+
let postLink = data?.publication
160
+
? `${getPublicationURL(data?.publication)}/${new AtUri(props.document_uri).rkey}`
161
+
: null;
162
+
163
+
if (state === "normal") {
164
+
return (
165
+
<>
166
+
<ShareButton
167
+
className="justify-end"
168
+
text={
169
+
<div className="flex gap-2">
170
+
Share Post Link
171
+
<ShareSmall />
172
+
</div>
173
+
}
174
+
subtext=""
175
+
smokerText="Post link copied!"
176
+
id="get-post-link"
177
+
fullLink={postLink?.includes("https") ? postLink : undefined}
178
+
link={postLink}
179
+
/>
180
+
181
+
<hr className="border-border-light" />
182
+
<MenuItem
183
+
className="justify-end"
184
+
onSelect={async (e) => {
185
+
e.preventDefault();
186
+
setState("confirm");
187
+
return;
188
+
}}
189
+
>
190
+
Delete Post
191
+
<DeleteSmall />
192
+
</MenuItem>
193
+
</>
194
+
);
195
+
}
196
+
if (state === "confirm") {
197
+
return (
198
+
<div className="flex flex-col items-center font-bold text-secondary px-2 py-1">
199
+
Are you sure?
200
+
<div className="text-sm text-tertiary font-normal">
201
+
This action cannot be undone!
202
+
</div>
203
+
<ButtonPrimary
204
+
className="mt-2"
205
+
onClick={async () => {
206
+
await mutate((data) => {
207
+
if (!data) return data;
208
+
return {
209
+
...data,
210
+
publication: {
211
+
...data.publication!,
212
+
leaflets_in_publications:
213
+
data.publication?.leaflets_in_publications.filter(
214
+
(l) => l.doc !== props.document_uri,
215
+
) || [],
216
+
documents_in_publications:
217
+
data.publication?.documents_in_publications.filter(
218
+
(d) => d.documents?.uri !== props.document_uri,
219
+
) || [],
220
+
},
221
+
};
222
+
}, false);
223
+
await deletePost(props.document_uri);
224
+
}}
225
+
>
226
+
Delete
227
+
</ButtonPrimary>
228
+
</div>
229
+
);
230
+
}
231
+
}
240
232
241
233
function PublishedDate(props: { dateString: string }) {
242
234
const formattedDate = useLocalizedDate(props.dateString, {
···
245
237
day: "2-digit",
246
238
});
247
239
248
-
return <p className="text-sm text-tertiary">Published {formattedDate}</p>;
240
+
return (
241
+
<p className="text-sm text-tertiary">
242
+
Published {formattedDate}
243
+
</p>
244
+
);
249
245
}
-23
app/lish/[did]/[publication]/dashboard/deletePost.ts
-23
app/lish/[did]/[publication]/dashboard/deletePost.ts
···
30
30
.delete()
31
31
.eq("doc", document_uri),
32
32
]);
33
-
34
-
return revalidatePath("/lish/[did]/[publication]/dashboard", "layout");
35
-
}
36
-
37
-
export async function unpublishPost(document_uri: string) {
38
-
let identity = await getIdentityData();
39
-
if (!identity || !identity.atp_did) throw new Error("No Identity");
40
-
41
-
const oauthClient = await createOauthClient();
42
-
let credentialSession = await oauthClient.restore(identity.atp_did);
43
-
let agent = new AtpBaseClient(
44
-
credentialSession.fetchHandler.bind(credentialSession),
45
-
);
46
-
let uri = new AtUri(document_uri);
47
-
if (uri.host !== identity.atp_did) return;
48
-
49
-
await Promise.all([
50
-
agent.pub.leaflet.document.delete({
51
-
repo: credentialSession.did,
52
-
rkey: uri.rkey,
53
-
}),
54
-
supabaseServerClient.from("documents").delete().eq("uri", document_uri),
55
-
]);
56
33
return revalidatePath("/lish/[did]/[publication]/dashboard", "layout");
57
34
}
+4
-3
app/lish/[did]/[publication]/icon.ts
+4
-3
app/lish/[did]/[publication]/icon.ts
···
14
14
};
15
15
16
16
export const contentType = "image/png";
17
-
export default async function Icon(props: {
18
-
params: Promise<{ did: string; publication: string }>;
17
+
export default async function Icon({
18
+
params,
19
+
}: {
20
+
params: { did: string; publication: string };
19
21
}) {
20
-
const params = await props.params;
21
22
try {
22
23
let did = decodeURIComponent(params.did);
23
24
let uri;
+2
-3
app/lish/[did]/[publication]/opengraph-image.ts
+2
-3
app/lish/[did]/[publication]/opengraph-image.ts
···
4
4
export const revalidate = 60;
5
5
6
6
export default async function OpenGraphImage(props: {
7
-
params: Promise<{ publication: string; did: string }>;
7
+
params: { publication: string; did: string };
8
8
}) {
9
-
let params = await props.params;
10
9
return getMicroLinkOgImage(
11
-
`/lish/${encodeURIComponent(params.did)}/${encodeURIComponent(params.publication)}/`,
10
+
`/lish/${encodeURIComponent(props.params.did)}/${encodeURIComponent(props.params.publication)}/`,
12
11
);
13
12
}
+19
-6
app/lish/[did]/[publication]/page.tsx
+19
-6
app/lish/[did]/[publication]/page.tsx
···
16
16
import { CommentTiny } from "components/Icons/CommentTiny";
17
17
import { LocalizedDate } from "./LocalizedDate";
18
18
import { PublicationHomeLayout } from "./PublicationHomeLayout";
19
+
import { EditTiny } from "components/Icons/EditTiny";
20
+
import { getIdentityData } from "actions/getIdentityData";
19
21
20
22
export default async function Publication(props: {
21
23
params: Promise<{ publication: string; did: string }>;
···
23
25
let params = await props.params;
24
26
let did = decodeURIComponent(params.did);
25
27
if (!did) return <PubNotFound />;
28
+
let identity = await getIdentityData();
26
29
let agent = new BskyAgent({ service: "https://public.api.bsky.app" });
27
30
let uri;
28
31
let publication_name = decodeURIComponent(params.publication);
···
101
104
</p>
102
105
)}
103
106
<div className="sm:pt-4 pt-4">
104
-
<SubscribeWithBluesky
105
-
base_url={getPublicationURL(publication)}
106
-
pubName={publication.name}
107
-
pub_uri={publication.uri}
108
-
subscribers={publication.publication_subscriptions}
109
-
/>
107
+
{identity?.atp_did === publication.identity_did ? (
108
+
<a
109
+
href={`${getPublicationURL(publication)}/dashboard`}
110
+
className="flex gap-2 items-center hover:!no-underline selected-outline px-4 py-2 bg-accent-1 text-accent-2 font-bold w-fit rounded-lg !border-accent-1 !outline-accent-1 mx-auto text-base"
111
+
>
112
+
<EditTiny /> Edit Publication
113
+
</a>
114
+
) : (
115
+
<SubscribeWithBluesky
116
+
base_url={getPublicationURL(publication)}
117
+
pubName={publication.name}
118
+
pub_uri={publication.uri}
119
+
subscribers={publication.publication_subscriptions}
120
+
pub_creator={publication.identity_did}
121
+
/>
122
+
)}
110
123
</div>
111
124
</div>
112
125
<div className="publicationPostList w-full flex flex-col gap-4">
+7
-6
app/lish/createPub/CreatePubForm.tsx
+7
-6
app/lish/createPub/CreatePubForm.tsx
···
127
127
onChange={(e) => setShowInDiscover(e.target.checked)}
128
128
>
129
129
<div className=" pt-0.5 flex flex-col text-sm text-tertiary ">
130
-
<p className="font-bold italic">Show In Discover</p>
131
-
<p className="text-sm text-tertiary font-normal">
132
-
Your posts will appear on our{" "}
130
+
<p className="font-bold italic">
131
+
Show In{" "}
133
132
<a href="/discover" target="_blank">
134
133
Discover
135
-
</a>{" "}
136
-
page. You can change this at any time!
134
+
</a>
135
+
</p>
136
+
<p className="text-sm text-tertiary font-normal">
137
+
You'll be able to change this later!
137
138
</p>
138
139
</div>
139
140
</Checkbox>
140
141
<hr className="border-border-light" />
141
142
142
-
<div className="flex w-full justify-end">
143
+
<div className="flex w-full justify-center">
143
144
<ButtonPrimary
144
145
type="submit"
145
146
disabled={
+2
-5
app/lish/createPub/UpdatePubForm.tsx
+2
-5
app/lish/createPub/UpdatePubForm.tsx
···
66
66
if (!pubData) return;
67
67
e.preventDefault();
68
68
props.setLoadingAction(true);
69
+
console.log("step 1:update");
69
70
let data = await updatePublication({
70
71
uri: pubData.uri,
71
72
name: nameValue,
···
170
171
</a>
171
172
</p>
172
173
<p className="text-xs text-tertiary font-normal">
173
-
Your posts will appear on our{" "}
174
-
<a href="/discover" target="_blank">
175
-
Discover
176
-
</a>{" "}
177
-
page. You can change this at any time!
174
+
This publication will appear on our public Discover page
178
175
</p>
179
176
</div>
180
177
</Checkbox>
-1
app/lish/createPub/createPublication.ts
-1
app/lish/createPub/createPublication.ts
+11
-3
app/lish/createPub/getPublicationURL.ts
+11
-3
app/lish/createPub/getPublicationURL.ts
···
3
3
import { isProductionDomain } from "src/utils/isProductionDeployment";
4
4
import { Json } from "supabase/database.types";
5
5
6
-
export function getPublicationURL(pub: { uri: string; record: Json }) {
6
+
export function getPublicationURL(pub: {
7
+
uri: string;
8
+
name: string;
9
+
record: Json;
10
+
}) {
7
11
let record = pub.record as PubLeafletPublication.Record;
8
12
if (isProductionDomain() && record?.base_path)
9
13
return `https://${record.base_path}`;
10
14
else return getBasePublicationURL(pub);
11
15
}
12
16
13
-
export function getBasePublicationURL(pub: { uri: string; record: Json }) {
17
+
export function getBasePublicationURL(pub: {
18
+
uri: string;
19
+
name: string;
20
+
record: Json;
21
+
}) {
14
22
let record = pub.record as PubLeafletPublication.Record;
15
23
let aturi = new AtUri(pub.uri);
16
-
return `/lish/${aturi.host}/${encodeURIComponent(aturi.rkey || record?.name)}`;
24
+
return `/lish/${aturi.host}/${encodeURIComponent(aturi.rkey || record?.name || pub.name)}`;
17
25
}
+1
-1
app/lish/createPub/page.tsx
+1
-1
app/lish/createPub/page.tsx
···
26
26
<div className="createPubContent h-full flex items-center max-w-sm w-full mx-auto">
27
27
<div className="createPubFormWrapper h-fit w-full flex flex-col gap-4">
28
28
<h2 className="text-center">Create Your Publication!</h2>
29
-
<div className="opaque-container w-full sm:py-4 p-3">
29
+
<div className="container w-full p-3">
30
30
<CreatePubForm />
31
31
</div>
32
32
</div>
+1
-1
app/login/LoginForm.tsx
+1
-1
app/login/LoginForm.tsx
···
213
213
</ButtonPrimary>
214
214
<button
215
215
type="button"
216
-
className={`${props.compact ? "text-xs mt-0.5" : "text-sm mt-[6px]"} text-accent-contrast place-self-center`}
216
+
className={`${props.compact ? "text-xs" : "text-sm"} text-accent-contrast place-self-center mt-[6px]`}
217
217
onClick={() => setSigningWithHandle(true)}
218
218
>
219
219
use an ATProto handle
-20
app/p/[didOrHandle]/[rkey]/l-quote/[quote]/opengraph-image.ts
-20
app/p/[didOrHandle]/[rkey]/l-quote/[quote]/opengraph-image.ts
···
1
-
import { getMicroLinkOgImage } from "src/utils/getMicroLinkOgImage";
2
-
import { decodeQuotePosition } from "app/lish/[did]/[publication]/[rkey]/quotePosition";
3
-
4
-
export const runtime = "edge";
5
-
export const revalidate = 60;
6
-
7
-
export default async function OpenGraphImage(props: {
8
-
params: Promise<{ didOrHandle: string; rkey: string; quote: string }>;
9
-
}) {
10
-
let params = await props.params;
11
-
let quotePosition = decodeQuotePosition(params.quote);
12
-
return getMicroLinkOgImage(
13
-
`/p/${decodeURIComponent(params.didOrHandle)}/${params.rkey}/l-quote/${params.quote}#${quotePosition?.pageId ? `${quotePosition.pageId}~` : ""}${quotePosition?.start.block.join(".")}_${quotePosition?.start.offset}`,
14
-
{
15
-
width: 620,
16
-
height: 324,
17
-
deviceScaleFactor: 2,
18
-
},
19
-
);
20
-
}
-8
app/p/[didOrHandle]/[rkey]/l-quote/[quote]/page.tsx
-8
app/p/[didOrHandle]/[rkey]/l-quote/[quote]/page.tsx
-13
app/p/[didOrHandle]/[rkey]/opengraph-image.ts
-13
app/p/[didOrHandle]/[rkey]/opengraph-image.ts
···
1
-
import { getMicroLinkOgImage } from "src/utils/getMicroLinkOgImage";
2
-
3
-
export const runtime = "edge";
4
-
export const revalidate = 60;
5
-
6
-
export default async function OpenGraphImage(props: {
7
-
params: Promise<{ rkey: string; didOrHandle: string }>;
8
-
}) {
9
-
let params = await props.params;
10
-
return getMicroLinkOgImage(
11
-
`/p/${params.didOrHandle}/${params.rkey}/`,
12
-
);
13
-
}
-90
app/p/[didOrHandle]/[rkey]/page.tsx
-90
app/p/[didOrHandle]/[rkey]/page.tsx
···
1
-
import { supabaseServerClient } from "supabase/serverClient";
2
-
import { AtUri } from "@atproto/syntax";
3
-
import { ids } from "lexicons/api/lexicons";
4
-
import { PubLeafletDocument } from "lexicons/api";
5
-
import { Metadata } from "next";
6
-
import { idResolver } from "app/(home-pages)/reader/idResolver";
7
-
import { DocumentPageRenderer } from "app/lish/[did]/[publication]/[rkey]/DocumentPageRenderer";
8
-
9
-
export async function generateMetadata(props: {
10
-
params: Promise<{ didOrHandle: string; rkey: string }>;
11
-
}): Promise<Metadata> {
12
-
let params = await props.params;
13
-
let didOrHandle = decodeURIComponent(params.didOrHandle);
14
-
15
-
// Resolve handle to DID if necessary
16
-
let did = didOrHandle;
17
-
if (!didOrHandle.startsWith("did:")) {
18
-
try {
19
-
let resolved = await idResolver.handle.resolve(didOrHandle);
20
-
if (resolved) did = resolved;
21
-
} catch (e) {
22
-
return { title: "404" };
23
-
}
24
-
}
25
-
26
-
let { data: document } = await supabaseServerClient
27
-
.from("documents")
28
-
.select("*, documents_in_publications(publications(*))")
29
-
.eq("uri", AtUri.make(did, ids.PubLeafletDocument, params.rkey))
30
-
.single();
31
-
32
-
if (!document) return { title: "404" };
33
-
34
-
let docRecord = document.data as PubLeafletDocument.Record;
35
-
36
-
// For documents in publications, include publication name
37
-
let publicationName = document.documents_in_publications[0]?.publications?.name;
38
-
39
-
return {
40
-
icons: {
41
-
other: {
42
-
rel: "alternate",
43
-
url: document.uri,
44
-
},
45
-
},
46
-
title: publicationName
47
-
? `${docRecord.title} - ${publicationName}`
48
-
: docRecord.title,
49
-
description: docRecord?.description || "",
50
-
};
51
-
}
52
-
53
-
export default async function StandaloneDocumentPage(props: {
54
-
params: Promise<{ didOrHandle: string; rkey: string }>;
55
-
}) {
56
-
let params = await props.params;
57
-
let didOrHandle = decodeURIComponent(params.didOrHandle);
58
-
59
-
// Resolve handle to DID if necessary
60
-
let did = didOrHandle;
61
-
if (!didOrHandle.startsWith("did:")) {
62
-
try {
63
-
let resolved = await idResolver.handle.resolve(didOrHandle);
64
-
if (!resolved) {
65
-
return (
66
-
<div className="p-4 text-lg text-center flex flex-col gap-4">
67
-
<p>Sorry, can't resolve handle.</p>
68
-
<p>
69
-
This may be a glitch on our end. If the issue persists please{" "}
70
-
<a href="mailto:contact@leaflet.pub">send us a note</a>.
71
-
</p>
72
-
</div>
73
-
);
74
-
}
75
-
did = resolved;
76
-
} catch (e) {
77
-
return (
78
-
<div className="p-4 text-lg text-center flex flex-col gap-4">
79
-
<p>Sorry, can't resolve handle.</p>
80
-
<p>
81
-
This may be a glitch on our end. If the issue persists please{" "}
82
-
<a href="mailto:contact@leaflet.pub">send us a note</a>.
83
-
</p>
84
-
</div>
85
-
);
86
-
}
87
-
}
88
-
89
-
return <DocumentPageRenderer did={did} rkey={params.rkey} />;
90
-
}
+159
app/templates/TemplateList.tsx
+159
app/templates/TemplateList.tsx
···
1
+
"use client";
2
+
3
+
import { ButtonPrimary } from "components/Buttons";
4
+
import Image from "next/image";
5
+
import Link from "next/link";
6
+
import { createNewLeafletFromTemplate } from "actions/createNewLeafletFromTemplate";
7
+
import { AddTiny } from "components/Icons/AddTiny";
8
+
9
+
export function LeafletTemplate(props: {
10
+
title: string;
11
+
description?: string;
12
+
image: string;
13
+
alt: string;
14
+
templateID: string; // readonly id for the leaflet that will be duplicated
15
+
}) {
16
+
return (
17
+
<div className="flex flex-col gap-4">
18
+
<div className="flex flex-col gap-2">
19
+
<div className="max-w-[274px] h-[154px] relative">
20
+
<Image
21
+
className="absolute top-0 left-0 rounded-md w-full h-full object-cover"
22
+
src={props.image}
23
+
alt={props.alt}
24
+
width={274}
25
+
height={154}
26
+
/>
27
+
</div>
28
+
</div>
29
+
<div className={`flex flex-col ${props.description ? "gap-4" : "gap-2"}`}>
30
+
<div className="gap-0">
31
+
<h3 className="font-bold text-center text-secondary">
32
+
{props.title}
33
+
</h3>
34
+
{props.description && (
35
+
<div className="text-tertiary text-sm font-normal text-center">
36
+
{props.description}
37
+
</div>
38
+
)}
39
+
</div>
40
+
<div className="flex sm:flex-row flex-col gap-2 justify-center items-center bottom-4">
41
+
<Link
42
+
href={`https://leaflet.pub/` + props.templateID}
43
+
target="_blank"
44
+
className="no-underline hover:no-underline"
45
+
>
46
+
<ButtonPrimary className="bg-primary hover:outline-hidden! hover:scale-105 hover:rotate-3 transition-all">
47
+
Preview
48
+
</ButtonPrimary>
49
+
</Link>
50
+
<ButtonPrimary
51
+
className=" hover:outline-hidden! hover:scale-105 hover:-rotate-2 transition-all"
52
+
onClick={async () => {
53
+
let id = await createNewLeafletFromTemplate(
54
+
props.templateID,
55
+
false,
56
+
);
57
+
window.open(`/${id}`, "_blank");
58
+
}}
59
+
>
60
+
Create
61
+
<AddTiny />
62
+
</ButtonPrimary>
63
+
</div>
64
+
</div>
65
+
</div>
66
+
);
67
+
}
68
+
69
+
export function TemplateList(props: {
70
+
name: string;
71
+
description?: string;
72
+
children: React.ReactNode;
73
+
}) {
74
+
return (
75
+
<div className="templateLeafletGrid flex flex-col gap-6">
76
+
<div className="flex flex-col gap-0 text-center">
77
+
<h3 className="text-[24px]">{props.name}</h3>
78
+
<p className="text-secondary">{props.description}</p>
79
+
</div>
80
+
<div className="grid auto-rows-max md:grid-cols-4 sm:grid-cols-3 grid-cols-2 gap-y-8 gap-x-6 sm:gap-6 grow pb-8">
81
+
{props.children}
82
+
</div>
83
+
</div>
84
+
);
85
+
}
86
+
87
+
export function TemplateListThemes() {
88
+
return (
89
+
<>
90
+
<TemplateList
91
+
name="Themes"
92
+
description="A small sampling of Leaflet's infinite theme possibilities!"
93
+
>
94
+
<LeafletTemplate
95
+
title="Foliage"
96
+
image="/templates/template-foliage-548x308.jpg"
97
+
alt="preview image of Foliage theme, with lots of green and leafy bg"
98
+
templateID="e4323c1d-15c1-407d-afaf-e5d772a35f0e"
99
+
/>
100
+
<LeafletTemplate
101
+
title="Lunar"
102
+
image="/templates/template-lunar-548x308.jpg"
103
+
alt="preview image of Lunar theme, with dark grey, red, and moon bg"
104
+
templateID="219d14ab-096c-4b48-83ee-36446e335c3e"
105
+
/>
106
+
<LeafletTemplate
107
+
title="Paper"
108
+
image="/templates/template-paper-548x308.jpg"
109
+
alt="preview image of Paper theme, with red, gold, green and marbled paper bg"
110
+
templateID="9b28ceea-0220-42ac-87e6-3976d156f653"
111
+
/>
112
+
<LeafletTemplate
113
+
title="Oceanic"
114
+
image="/templates/template-oceanic-548x308.jpg"
115
+
alt="preview image of Oceanic theme, with dark and light blue and ocean bg"
116
+
templateID="a65a56d7-713d-437e-9c42-f18bdc6fe2a7"
117
+
/>
118
+
</TemplateList>
119
+
</>
120
+
);
121
+
}
122
+
123
+
export function TemplateListExamples() {
124
+
return (
125
+
<TemplateList
126
+
name="Examples"
127
+
description="Creative documents you can make and share with Leaflet"
128
+
>
129
+
<LeafletTemplate
130
+
title="Reading List"
131
+
description="Make a list for your own reading, or share recs with friends!"
132
+
image="/templates/template-reading-548x308.jpg"
133
+
alt="preview image of Reading List template, with a few sections and example books as sub-pages"
134
+
templateID="a5655b68-fe7a-4494-bda6-c9847523b2f6"
135
+
/>
136
+
<LeafletTemplate
137
+
title="Travel Plan"
138
+
description="Organize a trip — notes, logistics, itinerary, even a shared scrapbook"
139
+
image="/templates/template-travel-548x308.jpg"
140
+
alt="preview image of a Travel Plan template, with pages for itinerary, logistics, research, and a travel diary canvas"
141
+
templateID="4d6f1392-dfd3-4015-925d-df55b7da5566"
142
+
/>
143
+
<LeafletTemplate
144
+
title="Gift Guide"
145
+
description="Share your favorite things — products, restaurants, movies…"
146
+
image="/templates/template-gift-548x308.jpg"
147
+
alt="preview image for a Gift Guide template, with three blank canvases for different categories"
148
+
templateID="de73df29-35d9-4a43-a441-7ce45ad3b498"
149
+
/>
150
+
<LeafletTemplate
151
+
title="Event Page"
152
+
description="Host an event — from a single meetup, to a whole conference!"
153
+
image="/templates/template-event-548x308.jpg"
154
+
alt="preview image for an Event Page template, with an event info section and linked pages / canvases for more info"
155
+
templateID="23d8a4ec-b2f6-438a-933d-726d2188974d"
156
+
/>
157
+
</TemplateList>
158
+
);
159
+
}
+108
app/templates/icon.tsx
+108
app/templates/icon.tsx
···
1
+
// NOTE: duplicated from home/icon.tsx
2
+
// we could make it different so it's clear it's not your personal colors?
3
+
4
+
import { ImageResponse } from "next/og";
5
+
import type { Fact } from "src/replicache";
6
+
import type { Attribute } from "src/replicache/attributes";
7
+
import { Database } from "../../supabase/database.types";
8
+
import { createServerClient } from "@supabase/ssr";
9
+
import { parseHSBToRGB } from "src/utils/parseHSB";
10
+
import { cookies } from "next/headers";
11
+
12
+
// Route segment config
13
+
export const revalidate = 0;
14
+
export const preferredRegion = ["sfo1"];
15
+
export const dynamic = "force-dynamic";
16
+
export const fetchCache = "force-no-store";
17
+
18
+
// Image metadata
19
+
export const size = {
20
+
width: 32,
21
+
height: 32,
22
+
};
23
+
export const contentType = "image/png";
24
+
25
+
// Image generation
26
+
let supabase = createServerClient<Database>(
27
+
process.env.NEXT_PUBLIC_SUPABASE_API_URL as string,
28
+
process.env.SUPABASE_SERVICE_ROLE_KEY as string,
29
+
{ cookies: {} },
30
+
);
31
+
export default async function Icon() {
32
+
let cookieStore = await cookies();
33
+
let identity = cookieStore.get("identity");
34
+
let rootEntity: string | null = null;
35
+
if (identity) {
36
+
let res = await supabase
37
+
.from("identities")
38
+
.select(
39
+
`*,
40
+
permission_tokens!identities_home_page_fkey(*, permission_token_rights(*)),
41
+
permission_token_on_homepage(
42
+
*, permission_tokens(*, permission_token_rights(*))
43
+
)
44
+
`,
45
+
)
46
+
.eq("id", identity?.value)
47
+
.single();
48
+
rootEntity = res.data?.permission_tokens?.root_entity || null;
49
+
}
50
+
let outlineColor, fillColor;
51
+
if (rootEntity) {
52
+
let { data } = await supabase.rpc("get_facts", {
53
+
root: rootEntity,
54
+
});
55
+
let initialFacts = (data as unknown as Fact<Attribute>[]) || [];
56
+
let themePageBG = initialFacts.find(
57
+
(f) => f.attribute === "theme/card-background",
58
+
) as Fact<"theme/card-background"> | undefined;
59
+
60
+
let themePrimary = initialFacts.find(
61
+
(f) => f.attribute === "theme/primary",
62
+
) as Fact<"theme/primary"> | undefined;
63
+
64
+
outlineColor = parseHSBToRGB(`hsba(${themePageBG?.data.value})`);
65
+
66
+
fillColor = parseHSBToRGB(`hsba(${themePrimary?.data.value})`);
67
+
}
68
+
69
+
return new ImageResponse(
70
+
(
71
+
// ImageResponse JSX element
72
+
<div style={{ display: "flex" }}>
73
+
<svg
74
+
width="32"
75
+
height="32"
76
+
viewBox="0 0 32 32"
77
+
fill="none"
78
+
xmlns="http://www.w3.org/2000/svg"
79
+
>
80
+
{/* outline */}
81
+
<path
82
+
fillRule="evenodd"
83
+
clipRule="evenodd"
84
+
d="M3.09628 21.8809C2.1044 23.5376 1.19806 25.3395 0.412496 27.2953C-0.200813 28.8223 0.539843 30.5573 2.06678 31.1706C3.59372 31.7839 5.32873 31.0433 5.94204 29.5163C6.09732 29.1297 6.24696 28.7489 6.39151 28.3811L6.39286 28.3777C6.94334 26.9769 7.41811 25.7783 7.99246 24.6987C8.63933 24.6636 9.37895 24.6582 10.2129 24.6535L10.3177 24.653C11.8387 24.6446 13.6711 24.6345 15.2513 24.3147C16.8324 23.9947 18.789 23.2382 19.654 21.2118C19.8881 20.6633 20.1256 19.8536 19.9176 19.0311C19.98 19.0311 20.044 19.031 20.1096 19.031C20.1447 19.031 20.1805 19.0311 20.2169 19.0311C21.0513 19.0316 22.2255 19.0324 23.2752 18.7469C24.5 18.4137 25.7878 17.6248 26.3528 15.9629C26.557 15.3624 26.5948 14.7318 26.4186 14.1358C26.4726 14.1262 26.528 14.1165 26.5848 14.1065C26.6121 14.1018 26.6398 14.0969 26.6679 14.092C27.3851 13.9667 28.3451 13.7989 29.1653 13.4921C29.963 13.1936 31.274 12.5268 31.6667 10.9987C31.8906 10.1277 31.8672 9.20568 31.3642 8.37294C31.1551 8.02669 30.889 7.75407 30.653 7.55302C30.8728 7.27791 31.1524 6.89517 31.345 6.47292C31.6791 5.74032 31.8513 4.66394 31.1679 3.61078C30.3923 2.4155 29.0623 2.2067 28.4044 2.1526C27.7203 2.09635 26.9849 2.15644 26.4564 2.2042C26.3846 2.02839 26.2858 1.84351 26.1492 1.66106C25.4155 0.681263 24.2775 0.598914 23.6369 0.61614C22.3428 0.650943 21.3306 1.22518 20.5989 1.82076C20.2149 2.13334 19.8688 2.48545 19.5698 2.81786C18.977 2.20421 18.1625 1.90193 17.3552 1.77751C15.7877 1.53594 14.5082 2.58853 13.6056 3.74374C12.4805 5.18375 11.7295 6.8566 10.7361 8.38059C10.3814 8.14984 9.83685 7.89945 9.16529 7.93065C8.05881 7.98204 7.26987 8.73225 6.79424 9.24551C5.96656 10.1387 5.46273 11.5208 5.10424 12.7289C4.71615 14.0368 4.38077 15.5845 4.06569 17.1171C3.87054 18.0664 3.82742 18.5183 4.01638 20.2489C3.43705 21.1826 3.54993 21.0505 3.09628 21.8809Z"
85
+
fill={outlineColor ? outlineColor : "#FFFFFF"}
86
+
/>
87
+
88
+
{/* fill */}
89
+
<path
90
+
fillRule="evenodd"
91
+
clipRule="evenodd"
92
+
d="M9.86889 10.2435C10.1927 10.528 10.5723 10.8615 11.3911 10.5766C11.9265 10.3903 12.6184 9.17682 13.3904 7.82283C14.5188 5.84367 15.8184 3.56431 17.0505 3.7542C18.5368 3.98325 18.4453 4.80602 18.3749 5.43886C18.3255 5.88274 18.2866 6.23317 18.8098 6.21972C19.3427 6.20601 19.8613 5.57971 20.4632 4.8529C21.2945 3.84896 22.2847 2.65325 23.6906 2.61544C24.6819 2.58879 24.6663 3.01595 24.6504 3.44913C24.6403 3.72602 24.63 4.00537 24.8826 4.17024C25.1314 4.33266 25.7571 4.2759 26.4763 4.21065C27.6294 4.10605 29.023 3.97963 29.4902 4.6995C29.9008 5.33235 29.3776 5.96135 28.8762 6.56423C28.4514 7.07488 28.0422 7.56679 28.2293 8.02646C28.3819 8.40149 28.6952 8.61278 29.0024 8.81991C29.5047 9.15866 29.9905 9.48627 29.7297 10.5009C29.4539 11.5737 27.7949 11.8642 26.2398 12.1366C24.937 12.3647 23.7072 12.5801 23.4247 13.2319C23.2475 13.6407 23.5414 13.8311 23.8707 14.0444C24.2642 14.2992 24.7082 14.5869 24.4592 15.3191C23.8772 17.031 21.9336 17.031 20.1095 17.0311C18.5438 17.0311 17.0661 17.0311 16.6131 18.1137C16.3515 18.7387 16.7474 18.849 17.1818 18.9701C17.7135 19.1183 18.3029 19.2826 17.8145 20.4267C16.8799 22.6161 13.3934 22.6357 10.2017 22.6536C9.03136 22.6602 7.90071 22.6665 6.95003 22.7795C6.84152 22.7924 6.74527 22.8547 6.6884 22.948C5.81361 24.3834 5.19318 25.9622 4.53139 27.6462C4.38601 28.0162 4.23862 28.3912 4.08611 28.7709C3.88449 29.2729 3.31413 29.5163 2.81217 29.3147C2.31021 29.1131 2.06673 28.5427 2.26834 28.0408C3.01927 26.1712 3.88558 24.452 4.83285 22.8739C6.37878 20.027 9.42621 16.5342 12.6488 13.9103C15.5162 11.523 18.2544 9.73614 21.4413 8.38026C21.8402 8.21054 21.7218 7.74402 21.3053 7.86437C18.4789 8.68119 15.9802 10.3013 13.3904 11.9341C10.5735 13.71 8.21288 16.1115 6.76027 17.8575C6.50414 18.1653 5.94404 17.9122 6.02468 17.5199C6.65556 14.4512 7.30668 11.6349 8.26116 10.605C9.16734 9.62708 9.47742 9.8995 9.86889 10.2435Z"
93
+
fill={fillColor ? fillColor : "#272727"}
94
+
/>
95
+
</svg>
96
+
</div>
97
+
),
98
+
// ImageResponse options
99
+
{
100
+
// For convenience, we can re-use the exported icons size metadata
101
+
// config to also set the ImageResponse's width and height.
102
+
...size,
103
+
headers: {
104
+
"Cache-Control": "no-cache",
105
+
},
106
+
},
107
+
);
108
+
}
+29
app/templates/page.tsx
+29
app/templates/page.tsx
···
1
+
import Link from "next/link";
2
+
import { TemplateListExamples, TemplateListThemes } from "./TemplateList";
3
+
import { ActionButton } from "components/ActionBar/ActionButton";
4
+
import { HomeSmall } from "components/Icons/HomeSmall";
5
+
6
+
export const metadata = {
7
+
title: "Leaflet Templates",
8
+
description: "example themes and documents you can use!",
9
+
};
10
+
11
+
export default function Templates() {
12
+
return (
13
+
<div className="flex h-full bg-bg-leaflet">
14
+
<div className="home relative max-w-(--breakpoint-lg) w-full h-full mx-auto flex sm:flex-row flex-col-reverse px-4 sm:px-6 ">
15
+
<div className="homeOptions z-10 shrink-0 sm:static absolute bottom-0 place-self-end sm:place-self-start flex sm:flex-col flex-row-reverse gap-2 sm:w-fit w-full items-center pb-2 pt-1 sm:pt-7">
16
+
{/* NOT using <HomeButton /> b/c it does a permission check we don't need */}
17
+
<Link href="/home">
18
+
<ActionButton icon={<HomeSmall />} label="Go Home" />
19
+
</Link>
20
+
</div>
21
+
<div className="flex flex-col gap-10 py-6 pt-3 sm:pt-6 sm:pb-12 sm:pl-6 grow w-full h-full overflow-y-scroll no-scrollbar">
22
+
<h1 className="text-center">Template Library</h1>
23
+
<TemplateListThemes />
24
+
<TemplateListExamples />
25
+
</div>
26
+
</div>
27
+
</div>
28
+
);
29
+
}
+17
-20
appview/index.ts
+17
-20
appview/index.ts
···
104
104
data: record.value as Json,
105
105
});
106
106
if (docResult.error) console.log(docResult.error);
107
-
if (record.value.publication) {
108
-
let publicationURI = new AtUri(record.value.publication);
107
+
let publicationURI = new AtUri(record.value.publication);
109
108
110
-
if (publicationURI.host !== evt.uri.host) {
111
-
console.log("Unauthorized to create post!");
112
-
return;
113
-
}
114
-
let docInPublicationResult = await supabase
115
-
.from("documents_in_publications")
116
-
.upsert({
117
-
publication: record.value.publication,
118
-
document: evt.uri.toString(),
119
-
});
120
-
await supabase
121
-
.from("documents_in_publications")
122
-
.delete()
123
-
.neq("publication", record.value.publication)
124
-
.eq("document", evt.uri.toString());
125
-
126
-
if (docInPublicationResult.error)
127
-
console.log(docInPublicationResult.error);
109
+
if (publicationURI.host !== evt.uri.host) {
110
+
console.log("Unauthorized to create post!");
111
+
return;
128
112
}
113
+
let docInPublicationResult = await supabase
114
+
.from("documents_in_publications")
115
+
.upsert({
116
+
publication: record.value.publication,
117
+
document: evt.uri.toString(),
118
+
});
119
+
await supabase
120
+
.from("documents_in_publications")
121
+
.delete()
122
+
.neq("publication", record.value.publication)
123
+
.eq("document", evt.uri.toString());
124
+
if (docInPublicationResult.error)
125
+
console.log(docInPublicationResult.error);
129
126
}
130
127
if (evt.event === "delete") {
131
128
await supabase.from("documents").delete().eq("uri", evt.uri.toString());
+25
-48
components/ActionBar/Publications.tsx
+25
-48
components/ActionBar/Publications.tsx
···
12
12
import { PublishSmall } from "components/Icons/PublishSmall";
13
13
import { Popover } from "components/Popover";
14
14
import { BlueskyLogin } from "app/login/LoginForm";
15
-
import { ButtonSecondary } from "components/Buttons";
15
+
import { ButtonPrimary } from "components/Buttons";
16
16
import { useIsMobile } from "src/hooks/isMobile";
17
17
import { useState } from "react";
18
-
import { LooseLeafSmall } from "components/Icons/LooseleafSmall";
19
-
import { navPages } from "./Navigation";
20
18
21
19
export const PublicationButtons = (props: {
22
-
currentPage: navPages;
23
20
currentPubUri: string | undefined;
24
21
}) => {
25
22
let { identity } = useIdentityData();
26
-
let looseleaves = identity?.permission_token_on_homepage.find(
27
-
(f) => f.permission_tokens.leaflets_to_documents,
28
-
);
29
23
30
24
// don't show pub list button if not logged in or no pub list
31
25
// we show a "start a pub" banner instead
32
26
if (!identity || !identity.atp_did || identity.publications.length === 0)
33
27
return <PubListEmpty />;
34
-
35
28
return (
36
29
<div className="pubListWrapper w-full flex flex-col gap-1 sm:bg-transparent sm:border-0">
37
-
{looseleaves && (
38
-
<>
39
-
<SpeedyLink
40
-
href={`/looseleafs`}
41
-
className="flex gap-2 items-start text-secondary font-bold hover:no-underline! hover:text-accent-contrast w-full"
42
-
>
43
-
{/*TODO How should i get if this is the current page or not?
44
-
theres not "pub" to check the uri for. Do i need to add it as an option to NavPages? thats kinda annoying*/}
45
-
<ActionButton
46
-
label="Looseleafs"
47
-
icon={<LooseLeafSmall />}
48
-
nav
49
-
className={
50
-
props.currentPage === "looseleafs"
51
-
? "bg-bg-page! border-border!"
52
-
: ""
53
-
}
54
-
/>
55
-
</SpeedyLink>
56
-
<hr className="border-border-light border-dashed mx-1" />
57
-
</>
58
-
)}
59
-
60
30
{identity.publications?.map((d) => {
61
31
return (
62
32
<PublicationOption
63
33
{...d}
64
34
key={d.uri}
65
35
record={d.record}
36
+
asActionButton
66
37
current={d.uri === props.currentPubUri}
67
38
/>
68
39
);
···
81
52
uri: string;
82
53
name: string;
83
54
record: Json;
55
+
asActionButton?: boolean;
84
56
current?: boolean;
85
57
}) => {
86
58
let record = props.record as PubLeafletPublication.Record | null;
···
91
63
href={`${getBasePublicationURL(props)}/dashboard`}
92
64
className="flex gap-2 items-start text-secondary font-bold hover:no-underline! hover:text-accent-contrast w-full"
93
65
>
94
-
<ActionButton
95
-
label={record.name}
96
-
icon={<PubIcon record={record} uri={props.uri} />}
97
-
nav
98
-
className={props.current ? "bg-bg-page! border-border!" : ""}
99
-
/>
66
+
{props.asActionButton ? (
67
+
<ActionButton
68
+
label={record.name}
69
+
icon={<PubIcon record={record} uri={props.uri} />}
70
+
nav
71
+
className={props.current ? "bg-bg-page! border-border!" : ""}
72
+
/>
73
+
) : (
74
+
<>
75
+
<PubIcon record={record} uri={props.uri} />
76
+
<div className="truncate">{record.name}</div>
77
+
</>
78
+
)}
100
79
</SpeedyLink>
101
80
);
102
81
};
103
82
104
83
const PubListEmpty = () => {
84
+
let { identity } = useIdentityData();
105
85
let isMobile = useIsMobile();
106
86
107
87
let [state, setState] = useState<"default" | "info">("default");
···
118
98
/>
119
99
);
120
100
121
-
if (isMobile && state === "info") return <PubListEmptyContent />;
101
+
if (isMobile && state === "info") return <PublishPopoverContent />;
122
102
else
123
103
return (
124
104
<Popover
125
105
side="right"
126
106
align="start"
127
107
className="p-1! max-w-56"
128
-
asChild
129
108
trigger={
130
109
<ActionButton
131
110
label="Publish"
···
135
114
/>
136
115
}
137
116
>
138
-
<PubListEmptyContent />
117
+
<PublishPopoverContent />
139
118
</Popover>
140
119
);
141
120
};
142
121
143
-
export const PubListEmptyContent = (props: { compact?: boolean }) => {
122
+
const PublishPopoverContent = () => {
144
123
let { identity } = useIdentityData();
145
124
146
125
return (
147
-
<div
148
-
className={`bg-[var(--accent-light)] w-full rounded-md flex flex-col text-center justify-center p-2 pb-4 text-sm`}
149
-
>
126
+
<div className="bg-[var(--accent-light)] w-full rounded-md flex flex-col text-center justify-center p-2 pb-4 text-sm">
150
127
<div className="mx-auto pt-2 scale-90">
151
128
<PubListEmptyIllo />
152
129
</div>
153
130
<div className="pt-1 font-bold">Publish on AT Proto</div>
154
-
{identity && !identity.atp_did ? (
131
+
{identity && identity.atp_did ? (
155
132
// has ATProto account and no pubs
156
133
<>
157
134
<div className="pb-2 text-secondary text-xs">
···
159
136
on AT Proto
160
137
</div>
161
138
<SpeedyLink href={`lish/createPub`} className=" hover:no-underline!">
162
-
<ButtonSecondary className="text-sm mx-auto" compact>
139
+
<ButtonPrimary className="text-sm mx-auto" compact>
163
140
Start a Publication!
164
-
</ButtonSecondary>
141
+
</ButtonPrimary>
165
142
</SpeedyLink>
166
143
</>
167
144
) : (
168
145
// no ATProto account and no pubs
169
146
<>
170
147
<div className="pb-2 text-secondary text-xs">
171
-
Link a Bluesky account to start <br /> a new publication on AT Proto
148
+
Link a Bluesky account to start a new publication on AT Proto
172
149
</div>
173
150
174
151
<BlueskyLogin compact />
+6
-43
components/Blocks/BaseTextareaBlock.tsx
+6
-43
components/Blocks/BaseTextareaBlock.tsx
···
5
5
import { BlockProps } from "./Block";
6
6
import { getCoordinatesInTextarea } from "src/utils/getCoordinatesInTextarea";
7
7
import { focusBlock } from "src/utils/focusBlock";
8
-
import { generateKeyBetween } from "fractional-indexing";
9
-
import { v7 } from "uuid";
10
-
import { elementId } from "src/utils/elementId";
11
-
import { Replicache } from "replicache";
12
-
import { ReplicacheMutators } from "src/replicache";
13
8
14
-
type BaseTextareaBlockProps = AutosizeTextareaProps & {
15
-
block: Pick<
16
-
BlockProps,
17
-
"previousBlock" | "nextBlock" | "parent" | "position" | "nextPosition"
18
-
>;
19
-
rep?: Replicache<ReplicacheMutators> | null;
20
-
permissionSet?: string;
21
-
};
22
-
23
-
export function BaseTextareaBlock(props: BaseTextareaBlockProps) {
24
-
let { block, rep, permissionSet, ...passDownProps } = props;
9
+
export function BaseTextareaBlock(
10
+
props: AutosizeTextareaProps & {
11
+
block: Pick<BlockProps, "previousBlock" | "nextBlock">;
12
+
},
13
+
) {
14
+
let { block, ...passDownProps } = props;
25
15
return (
26
16
<AsyncValueAutosizeTextarea
27
17
{...passDownProps}
28
18
noWrap
29
19
onKeyDown={(e) => {
30
-
// Shift-Enter or Ctrl-Enter: create new text block below and focus it
31
-
if (
32
-
(e.shiftKey || e.ctrlKey || e.metaKey) &&
33
-
e.key === "Enter" &&
34
-
rep &&
35
-
permissionSet
36
-
) {
37
-
e.preventDefault();
38
-
let newEntityID = v7();
39
-
rep.mutate.addBlock({
40
-
parent: block.parent,
41
-
type: "text",
42
-
factID: v7(),
43
-
permission_set: permissionSet,
44
-
position: generateKeyBetween(
45
-
block.position,
46
-
block.nextPosition || null,
47
-
),
48
-
newEntityID,
49
-
});
50
-
51
-
setTimeout(() => {
52
-
document.getElementById(elementId.block(newEntityID).text)?.focus();
53
-
}, 10);
54
-
return true;
55
-
}
56
-
57
20
if (e.key === "ArrowUp") {
58
21
let selection = e.currentTarget.selectionStart;
59
22
+1
-10
components/Blocks/BlockCommands.tsx
+1
-10
components/Blocks/BlockCommands.tsx
···
32
32
import { BlockMathSmall } from "components/Icons/BlockMathSmall";
33
33
import { BlockCodeSmall } from "components/Icons/BlockCodeSmall";
34
34
import { QuoteSmall } from "components/Icons/QuoteSmall";
35
-
import { LAST_USED_CODE_LANGUAGE_KEY } from "src/utils/codeLanguageStorage";
36
35
37
36
type Props = {
38
37
parent: string;
···
311
310
type: "block",
312
311
hiddenInPublication: false,
313
312
onSelect: async (rep, props) => {
314
-
let entity = await createBlockWithType(rep, props, "code");
315
-
let lastLang = localStorage.getItem(LAST_USED_CODE_LANGUAGE_KEY);
316
-
if (lastLang) {
317
-
await rep.mutate.assertFact({
318
-
entity,
319
-
attribute: "block/code-language",
320
-
data: { type: "string", value: lastLang },
321
-
});
322
-
}
313
+
createBlockWithType(rep, props, "code");
323
314
},
324
315
},
325
316
+1
-6
components/Blocks/CodeBlock.tsx
+1
-6
components/Blocks/CodeBlock.tsx
···
13
13
import { useEntitySetContext } from "components/EntitySetProvider";
14
14
import { flushSync } from "react-dom";
15
15
import { elementId } from "src/utils/elementId";
16
-
import { LAST_USED_CODE_LANGUAGE_KEY } from "src/utils/codeLanguageStorage";
17
16
18
17
export function CodeBlock(props: BlockProps) {
19
18
let { rep, rootEntity } = useReplicache();
···
26
25
let focusedBlock = useUIState(
27
26
(s) => s.focusedEntity?.entityID === props.entityID,
28
27
);
29
-
let entity_set = useEntitySetContext();
30
-
let { permissions } = entity_set;
28
+
let { permissions } = useEntitySetContext();
31
29
const [html, setHTML] = useState<string | null>(null);
32
30
33
31
useLayoutEffect(() => {
···
102
100
}}
103
101
value={lang}
104
102
onChange={async (e) => {
105
-
localStorage.setItem(LAST_USED_CODE_LANGUAGE_KEY, e.target.value);
106
103
await rep?.mutate.assertFact({
107
104
attribute: "block/code-language",
108
105
entity: props.entityID,
···
126
123
data-entityid={props.entityID}
127
124
id={elementId.block(props.entityID).input}
128
125
block={props}
129
-
rep={rep}
130
-
permissionSet={entity_set.set}
131
126
spellCheck={false}
132
127
autoCapitalize="none"
133
128
autoCorrect="off"
+16
-65
components/Blocks/EmbedBlock.tsx
+16
-65
components/Blocks/EmbedBlock.tsx
···
10
10
import { Input } from "components/Input";
11
11
import { isUrl } from "src/utils/isURL";
12
12
import { elementId } from "src/utils/elementId";
13
+
import { deleteBlock } from "./DeleteBlock";
13
14
import { focusBlock } from "src/utils/focusBlock";
14
15
import { useDrag } from "src/hooks/useDrag";
15
16
import { BlockEmbedSmall } from "components/Icons/BlockEmbedSmall";
16
17
import { CheckTiny } from "components/Icons/CheckTiny";
17
-
import { DotLoader } from "components/utils/DotLoader";
18
-
import {
19
-
LinkPreviewBody,
20
-
LinkPreviewMetadataResult,
21
-
} from "app/api/link_previews/route";
22
18
23
19
export const EmbedBlock = (props: BlockProps & { preview?: boolean }) => {
24
20
let { permissions } = useEntitySetContext();
···
136
132
137
133
let entity_set = useEntitySetContext();
138
134
let [linkValue, setLinkValue] = useState("");
139
-
let [loading, setLoading] = useState(false);
140
135
let { rep } = useReplicache();
141
136
let submit = async () => {
142
137
let entity = props.entityID;
···
154
149
}
155
150
let link = linkValue;
156
151
if (!linkValue.startsWith("http")) link = `https://${linkValue}`;
152
+
// these mutations = simpler subset of addLinkBlock
157
153
if (!rep) return;
158
-
159
-
// Try to get embed URL from iframely, fallback to direct URL
160
-
setLoading(true);
161
-
try {
162
-
let res = await fetch("/api/link_previews", {
163
-
headers: { "Content-Type": "application/json" },
164
-
method: "POST",
165
-
body: JSON.stringify({ url: link, type: "meta" } as LinkPreviewBody),
166
-
});
167
-
168
-
let embedUrl = link;
169
-
let embedHeight = 360;
170
-
171
-
if (res.status === 200) {
172
-
let data = await (res.json() as LinkPreviewMetadataResult);
173
-
if (data.success && data.data.links?.player?.[0]) {
174
-
let embed = data.data.links.player[0];
175
-
embedUrl = embed.href;
176
-
embedHeight = embed.media?.height || 300;
177
-
}
178
-
}
179
-
180
-
await rep.mutate.assertFact([
181
-
{
182
-
entity: entity,
183
-
attribute: "embed/url",
184
-
data: {
185
-
type: "string",
186
-
value: embedUrl,
187
-
},
188
-
},
189
-
{
190
-
entity: entity,
191
-
attribute: "embed/height",
192
-
data: {
193
-
type: "number",
194
-
value: embedHeight,
195
-
},
196
-
},
197
-
]);
198
-
} catch {
199
-
// On any error, fallback to using the URL directly
200
-
await rep.mutate.assertFact([
201
-
{
202
-
entity: entity,
203
-
attribute: "embed/url",
204
-
data: {
205
-
type: "string",
206
-
value: link,
207
-
},
208
-
},
209
-
]);
210
-
} finally {
211
-
setLoading(false);
212
-
}
154
+
await rep.mutate.assertFact({
155
+
entity: entity,
156
+
attribute: "block/type",
157
+
data: { type: "block-type-union", value: "embed" },
158
+
});
159
+
await rep?.mutate.assertFact({
160
+
entity: entity,
161
+
attribute: "embed/url",
162
+
data: {
163
+
type: "string",
164
+
value: link,
165
+
},
166
+
});
213
167
};
214
168
let smoker = useSmoker();
215
169
···
217
171
<form
218
172
onSubmit={(e) => {
219
173
e.preventDefault();
220
-
if (loading) return;
221
174
let rect = document
222
175
.getElementById("embed-block-submit")
223
176
?.getBoundingClientRect();
···
259
212
<button
260
213
type="submit"
261
214
id="embed-block-submit"
262
-
disabled={loading}
263
215
className={`p-1 ${isSelected && !isLocked ? "text-accent-contrast" : "text-border"}`}
264
216
onMouseDown={(e) => {
265
217
e.preventDefault();
266
-
if (loading) return;
267
218
if (!linkValue || linkValue === "") {
268
219
smoker({
269
220
error: true,
···
283
234
submit();
284
235
}}
285
236
>
286
-
{loading ? <DotLoader /> : <CheckTiny />}
237
+
<CheckTiny />
287
238
</button>
288
239
</div>
289
240
</form>
+2
-2
components/Blocks/RSVPBlock/SendUpdate.tsx
+2
-2
components/Blocks/RSVPBlock/SendUpdate.tsx
···
9
9
import { sendUpdateToRSVPS } from "actions/sendUpdateToRSVPS";
10
10
import { useReplicache } from "src/replicache";
11
11
import { Checkbox } from "components/Checkbox";
12
-
import { useReadOnlyShareLink } from "app/[leaflet_id]/actions/ShareOptions";
12
+
import { usePublishLink } from "components/ShareOptions";
13
13
14
14
export function SendUpdateButton(props: { entityID: string }) {
15
-
let publishLink = useReadOnlyShareLink();
15
+
let publishLink = usePublishLink();
16
16
let { permissions } = useEntitySetContext();
17
17
let { permission_token } = useReplicache();
18
18
let [input, setInput] = useState("");
-8
components/Blocks/TextBlock/RenderYJSFragment.tsx
-8
components/Blocks/TextBlock/RenderYJSFragment.tsx
···
60
60
);
61
61
}
62
62
63
-
if (node.constructor === XmlElement && node.nodeName === "hard_break") {
64
-
return <br key={index} />;
65
-
}
66
-
67
63
return null;
68
64
})
69
65
)}
···
148
144
node: XmlElement | XmlText | XmlHook,
149
145
): string {
150
146
if (node.constructor === XmlElement) {
151
-
// Handle hard_break nodes specially
152
-
if (node.nodeName === "hard_break") {
153
-
return "\n";
154
-
}
155
147
return node
156
148
.toArray()
157
149
.map((f) => YJSFragmentToString(f))
+3
-12
components/Blocks/TextBlock/inputRules.ts
+3
-12
components/Blocks/TextBlock/inputRules.ts
···
11
11
import { schema } from "./schema";
12
12
import { useUIState } from "src/useUIState";
13
13
import { flushSync } from "react-dom";
14
-
import { LAST_USED_CODE_LANGUAGE_KEY } from "src/utils/codeLanguageStorage";
15
14
export const inputrules = (
16
15
propsRef: MutableRefObject<BlockProps & { entity_set: { set: string } }>,
17
16
repRef: MutableRefObject<Replicache<ReplicacheMutators> | null>,
···
109
108
110
109
// Code Block
111
110
new InputRule(/^```\s$/, (state, match) => {
112
-
flushSync(() => {
111
+
flushSync(() =>
113
112
repRef.current?.mutate.assertFact({
114
113
entity: propsRef.current.entityID,
115
114
attribute: "block/type",
116
115
data: { type: "block-type-union", value: "code" },
117
-
});
118
-
let lastLang = localStorage.getItem(LAST_USED_CODE_LANGUAGE_KEY);
119
-
if (lastLang) {
120
-
repRef.current?.mutate.assertFact({
121
-
entity: propsRef.current.entityID,
122
-
attribute: "block/code-language",
123
-
data: { type: "string", value: lastLang },
124
-
});
125
-
}
126
-
});
116
+
}),
117
+
);
127
118
setTimeout(() => {
128
119
focusBlock({ ...propsRef.current, type: "code" }, { type: "start" });
129
120
}, 20);
+5
-5
components/Blocks/TextBlock/keymap.ts
+5
-5
components/Blocks/TextBlock/keymap.ts
···
145
145
);
146
146
},
147
147
"Shift-Enter": (state, dispatch, view) => {
148
-
// Insert a hard break
149
-
let hardBreak = schema.nodes.hard_break.create();
150
-
if (dispatch) {
151
-
dispatch(state.tr.replaceSelectionWith(hardBreak).scrollIntoView());
148
+
if (multiLine) {
149
+
return baseKeymap.Enter(state, dispatch, view);
152
150
}
153
-
return true;
151
+
return um.withUndoGroup(() =>
152
+
enter(propsRef, repRef)(state, dispatch, view),
153
+
);
154
154
},
155
155
"Ctrl-Enter": CtrlEnter(propsRef, repRef),
156
156
"Meta-Enter": CtrlEnter(propsRef, repRef),
-7
components/Blocks/TextBlock/schema.ts
-7
components/Blocks/TextBlock/schema.ts
+21
-35
components/Buttons.tsx
+21
-35
components/Buttons.tsx
···
10
10
import { PopoverArrow } from "./Icons/PopoverArrow";
11
11
12
12
type ButtonProps = Omit<JSX.IntrinsicElements["button"], "content">;
13
-
14
13
export const ButtonPrimary = forwardRef<
15
14
HTMLButtonElement,
16
15
ButtonProps & {
···
36
35
m-0 h-max
37
36
${fullWidth ? "w-full" : fullWidthOnMobile ? "w-full sm:w-max" : "w-max"}
38
37
${compact ? "py-0 px-1" : "px-2 py-0.5 "}
39
-
bg-accent-1 disabled:bg-border-light
40
-
border border-accent-1 rounded-md disabled:border-border-light
41
-
outline outline-transparent outline-offset-1 focus:outline-accent-1 hover:outline-accent-1
42
-
text-base font-bold text-accent-2 disabled:text-border disabled:hover:text-border
38
+
bg-accent-1 outline-transparent border border-accent-1
39
+
rounded-md text-base font-bold text-accent-2
43
40
flex gap-2 items-center justify-center shrink-0
41
+
transparent-outline focus:outline-accent-1 hover:outline-accent-1 outline-offset-1
42
+
disabled:bg-border-light disabled:border-border-light disabled:text-border disabled:hover:text-border
44
43
${className}
45
44
`}
46
45
>
···
71
70
<button
72
71
{...buttonProps}
73
72
ref={ref}
74
-
className={`
75
-
m-0 h-max
73
+
className={`m-0 h-max
76
74
${fullWidth ? "w-full" : fullWidthOnMobile ? "w-full sm:w-max" : "w-max"}
77
-
${compact ? "py-0 px-1" : "px-2 py-0.5 "}
78
-
bg-bg-page disabled:bg-border-light
79
-
border border-accent-contrast rounded-md
80
-
outline outline-transparent focus:outline-accent-contrast hover:outline-accent-contrast outline-offset-1
81
-
text-base font-bold text-accent-contrast disabled:text-border disabled:hover:text-border
82
-
flex gap-2 items-center justify-center shrink-0
83
-
${props.className}
84
-
`}
75
+
${props.compact ? "py-0 px-1" : "px-2 py-0.5 "}
76
+
bg-bg-page outline-transparent
77
+
rounded-md text-base font-bold text-accent-contrast
78
+
flex gap-2 items-center justify-center shrink-0
79
+
transparent-outline focus:outline-accent-contrast hover:outline-accent-contrast outline-offset-1
80
+
border border-accent-contrast
81
+
disabled:bg-border-light disabled:text-border disabled:hover:text-border
82
+
${props.className}
83
+
`}
85
84
>
86
85
{props.children}
87
86
</button>
···
93
92
HTMLButtonElement,
94
93
{
95
94
fullWidth?: boolean;
96
-
fullWidthOnMobile?: boolean;
97
95
children: React.ReactNode;
98
96
compact?: boolean;
99
97
} & ButtonProps
100
98
>((props, ref) => {
101
-
let {
102
-
className,
103
-
fullWidth,
104
-
fullWidthOnMobile,
105
-
compact,
106
-
children,
107
-
...buttonProps
108
-
} = props;
99
+
let { fullWidth, children, compact, ...buttonProps } = props;
109
100
return (
110
101
<button
111
102
{...buttonProps}
112
103
ref={ref}
113
-
className={`
114
-
m-0 h-max
115
-
${fullWidth ? "w-full" : fullWidthOnMobile ? "w-full sm:w-max" : "w-max"}
116
-
${compact ? "py-0 px-1" : "px-2 py-0.5 "}
117
-
bg-transparent hover:bg-[var(--accent-light)]
118
-
border border-transparent rounded-md hover:border-[var(--accent-light)]
119
-
outline outline-transparent focus:outline-[var(--accent-light)] hover:outline-[var(--accent-light)] outline-offset-1
120
-
text-base font-bold text-accent-contrast disabled:text-border
121
-
flex gap-2 items-center justify-center shrink-0
122
-
${props.className}
123
-
`}
104
+
className={`m-0 h-max ${fullWidth ? "w-full" : "w-max"} ${compact ? "px-0" : "px-1"}
105
+
bg-transparent text-base font-bold text-accent-contrast
106
+
flex gap-2 items-center justify-center shrink-0
107
+
hover:underline disabled:text-border
108
+
${props.className}
109
+
`}
124
110
>
125
111
{children}
126
112
</button>
+173
components/HelpPopover.tsx
+173
components/HelpPopover.tsx
···
1
+
"use client";
2
+
import { ShortcutKey } from "./Layout";
3
+
import { Media } from "./Media";
4
+
import { Popover } from "./Popover";
5
+
import { metaKey } from "src/utils/metaKey";
6
+
import { useEntitySetContext } from "./EntitySetProvider";
7
+
import { useState } from "react";
8
+
import { ActionButton } from "components/ActionBar/ActionButton";
9
+
import { HelpSmall } from "./Icons/HelpSmall";
10
+
import { isMac } from "src/utils/isDevice";
11
+
import { useIsMobile } from "src/hooks/isMobile";
12
+
13
+
export const HelpPopover = (props: { noShortcuts?: boolean }) => {
14
+
let entity_set = useEntitySetContext();
15
+
let isMobile = useIsMobile();
16
+
17
+
return entity_set.permissions.write ? (
18
+
<Popover
19
+
side={isMobile ? "top" : "right"}
20
+
align={isMobile ? "center" : "start"}
21
+
asChild
22
+
className="max-w-xs w-full"
23
+
trigger={<ActionButton icon={<HelpSmall />} label="About" />}
24
+
>
25
+
<div className="flex flex-col text-sm gap-2 text-secondary">
26
+
{/* about links */}
27
+
<HelpLink text="📖 Leaflet Manual" url="https://about.leaflet.pub" />
28
+
<HelpLink text="💡 Make with Leaflet" url="https://make.leaflet.pub" />
29
+
<HelpLink
30
+
text="✨ Explore Publications"
31
+
url="https://leaflet.pub/discover"
32
+
/>
33
+
<HelpLink text="📣 Newsletter" url="https://buttondown.com/leaflet" />
34
+
{/* contact links */}
35
+
<div className="columns-2 gap-2">
36
+
<HelpLink
37
+
text="🦋 Bluesky"
38
+
url="https://bsky.app/profile/leaflet.pub"
39
+
/>
40
+
<HelpLink text="💌 Email" url="mailto:contact@leaflet.pub" />
41
+
</div>
42
+
{/* keyboard shortcuts: desktop only */}
43
+
<Media mobile={false}>
44
+
{!props.noShortcuts && (
45
+
<>
46
+
<hr className="text-border my-1" />
47
+
<div className="flex flex-col gap-1">
48
+
<Label>Text Shortcuts</Label>
49
+
<KeyboardShortcut name="Bold" keys={[metaKey(), "B"]} />
50
+
<KeyboardShortcut name="Italic" keys={[metaKey(), "I"]} />
51
+
<KeyboardShortcut name="Underline" keys={[metaKey(), "U"]} />
52
+
<KeyboardShortcut
53
+
name="Highlight"
54
+
keys={[metaKey(), isMac() ? "Ctrl" : "Meta", "H"]}
55
+
/>
56
+
<KeyboardShortcut
57
+
name="Strikethrough"
58
+
keys={[metaKey(), isMac() ? "Ctrl" : "Meta", "X"]}
59
+
/>
60
+
<KeyboardShortcut name="Inline Link" keys={[metaKey(), "K"]} />
61
+
62
+
<Label>Block Shortcuts</Label>
63
+
{/* shift + up/down arrows (or click + drag): select multiple blocks */}
64
+
<KeyboardShortcut
65
+
name="Move Block Up"
66
+
keys={["Shift", metaKey(), "↑"]}
67
+
/>
68
+
<KeyboardShortcut
69
+
name="Move Block Down"
70
+
keys={["Shift", metaKey(), "↓"]}
71
+
/>
72
+
{/* cmd/ctrl-a: first selects all text in a block; again selects all blocks on page */}
73
+
{/* cmd/ctrl + up/down arrows: go to beginning / end of doc */}
74
+
75
+
<Label>Canvas Shortcuts</Label>
76
+
<OtherShortcut name="Add Block" description="Double click" />
77
+
<OtherShortcut name="Select Block" description="Long press" />
78
+
79
+
<Label>Outliner Shortcuts</Label>
80
+
<KeyboardShortcut
81
+
name="Make List"
82
+
keys={[metaKey(), isMac() ? "Opt" : "Alt", "L"]}
83
+
/>
84
+
{/* tab / shift + tab: indent / outdent */}
85
+
<KeyboardShortcut
86
+
name="Toggle Checkbox"
87
+
keys={[metaKey(), "Enter"]}
88
+
/>
89
+
<KeyboardShortcut
90
+
name="Toggle Fold"
91
+
keys={[metaKey(), "Shift", "Enter"]}
92
+
/>
93
+
<KeyboardShortcut
94
+
name="Fold All"
95
+
keys={[metaKey(), isMac() ? "Opt" : "Alt", "Shift", "↑"]}
96
+
/>
97
+
<KeyboardShortcut
98
+
name="Unfold All"
99
+
keys={[metaKey(), isMac() ? "Opt" : "Alt", "Shift", "↓"]}
100
+
/>
101
+
</div>
102
+
</>
103
+
)}
104
+
</Media>
105
+
{/* links: terms and privacy */}
106
+
<hr className="text-border my-1" />
107
+
{/* <HelpLink
108
+
text="Terms and Privacy Policy"
109
+
url="https://leaflet.pub/legal"
110
+
/> */}
111
+
<div>
112
+
<a href="https://leaflet.pub/legal" target="_blank">
113
+
Terms and Privacy Policy
114
+
</a>
115
+
</div>
116
+
</div>
117
+
</Popover>
118
+
) : null;
119
+
};
120
+
121
+
const KeyboardShortcut = (props: { name: string; keys: string[] }) => {
122
+
return (
123
+
<div className="flex gap-2 justify-between items-center">
124
+
{props.name}
125
+
<div className="flex gap-1 items-center font-bold">
126
+
{props.keys.map((key, index) => {
127
+
return <ShortcutKey key={index}>{key}</ShortcutKey>;
128
+
})}
129
+
</div>
130
+
</div>
131
+
);
132
+
};
133
+
134
+
const OtherShortcut = (props: { name: string; description: string }) => {
135
+
return (
136
+
<div className="flex justify-between items-center">
137
+
<span>{props.name}</span>
138
+
<span>
139
+
<strong>{props.description}</strong>
140
+
</span>
141
+
</div>
142
+
);
143
+
};
144
+
145
+
const Label = (props: { children: React.ReactNode }) => {
146
+
return <div className="text-tertiary font-bold pt-2 ">{props.children}</div>;
147
+
};
148
+
149
+
const HelpLink = (props: { url: string; text: string }) => {
150
+
const [isHovered, setIsHovered] = useState(false);
151
+
const handleMouseEnter = () => {
152
+
setIsHovered(true);
153
+
};
154
+
const handleMouseLeave = () => {
155
+
setIsHovered(false);
156
+
};
157
+
return (
158
+
<a
159
+
href={props.url}
160
+
target="_blank"
161
+
className="py-2 px-2 rounded-md flex flex-col gap-1 bg-border-light hover:bg-border hover:no-underline"
162
+
style={{
163
+
backgroundColor: isHovered
164
+
? "color-mix(in oklab, rgb(var(--accent-contrast)), rgb(var(--bg-page)) 85%)"
165
+
: "color-mix(in oklab, rgb(var(--accent-contrast)), rgb(var(--bg-page)) 75%)",
166
+
}}
167
+
onMouseEnter={handleMouseEnter}
168
+
onMouseLeave={handleMouseLeave}
169
+
>
170
+
<strong>{props.text}</strong>
171
+
</a>
172
+
);
173
+
};
+76
components/HomeButton.tsx
+76
components/HomeButton.tsx
···
1
+
"use client";
2
+
import Link from "next/link";
3
+
import { useEntitySetContext } from "./EntitySetProvider";
4
+
import { ActionButton } from "components/ActionBar/ActionButton";
5
+
import { useParams, useSearchParams } from "next/navigation";
6
+
import { useIdentityData } from "./IdentityProvider";
7
+
import { useReplicache } from "src/replicache";
8
+
import { addLeafletToHome } from "actions/addLeafletToHome";
9
+
import { useSmoker } from "./Toast";
10
+
import { AddToHomeSmall } from "./Icons/AddToHomeSmall";
11
+
import { HomeSmall } from "./Icons/HomeSmall";
12
+
import { permission } from "process";
13
+
14
+
export function HomeButton() {
15
+
let { permissions } = useEntitySetContext();
16
+
let searchParams = useSearchParams();
17
+
18
+
return (
19
+
<>
20
+
<Link
21
+
href="/home"
22
+
prefetch
23
+
className="hover:no-underline"
24
+
style={{ textDecorationLine: "none !important" }}
25
+
>
26
+
<ActionButton icon={<HomeSmall />} label="Go Home" />
27
+
</Link>
28
+
{<AddToHomeButton />}
29
+
</>
30
+
);
31
+
}
32
+
33
+
const AddToHomeButton = (props: {}) => {
34
+
let { permission_token } = useReplicache();
35
+
let { identity, mutate } = useIdentityData();
36
+
let smoker = useSmoker();
37
+
if (
38
+
identity?.permission_token_on_homepage.find(
39
+
(pth) => pth.permission_tokens.id === permission_token.id,
40
+
) ||
41
+
!identity
42
+
)
43
+
return null;
44
+
return (
45
+
<ActionButton
46
+
onClick={async (e) => {
47
+
await addLeafletToHome(permission_token.id);
48
+
mutate((identity) => {
49
+
if (!identity) return;
50
+
return {
51
+
...identity,
52
+
permission_token_on_homepage: [
53
+
...identity.permission_token_on_homepage,
54
+
{
55
+
created_at: new Date().toISOString(),
56
+
permission_tokens: {
57
+
...permission_token,
58
+
leaflets_in_publications: [],
59
+
},
60
+
},
61
+
],
62
+
};
63
+
});
64
+
smoker({
65
+
position: {
66
+
x: e.clientX + 64,
67
+
y: e.clientY,
68
+
},
69
+
text: "Leaflet added to your home!",
70
+
});
71
+
}}
72
+
icon={<AddToHomeSmall />}
73
+
label="Add to Home"
74
+
/>
75
+
);
76
+
};
-21
components/Icons/ArchiveSmall.tsx
-21
components/Icons/ArchiveSmall.tsx
···
1
-
import { Props } from "./Props";
2
-
3
-
export const ArchiveSmall = (props: Props) => {
4
-
return (
5
-
<svg
6
-
width="24"
7
-
height="24"
8
-
viewBox="0 0 24 24"
9
-
fill="none"
10
-
xmlns="http://www.w3.org/2000/svg"
11
-
{...props}
12
-
>
13
-
<path
14
-
fillRule="evenodd"
15
-
clipRule="evenodd"
16
-
d="M14.3935 2.33729C14.4781 2.30741 14.5682 2.29611 14.6576 2.30415C14.7774 2.31514 14.897 2.32836 15.0165 2.34211C15.2401 2.36784 15.5571 2.40755 15.9337 2.46375C16.6844 2.57577 17.6834 2.755 18.6552 3.02334C20.043 3.40654 21.1623 4.08204 21.9307 4.65549C22.3161 4.94319 22.6172 5.20811 22.8237 5.40315C22.9788 5.5496 23.0813 5.6572 23.1271 5.70673C23.3287 5.92633 23.375 6.26081 23.1986 6.51162C23.0315 6.74906 22.723 6.84022 22.4537 6.73167C22.0456 6.56715 21.4938 6.48314 21.0486 6.65428C20.807 6.74717 20.531 6.94113 20.3218 7.3713L20.6009 7.19094C20.7969 7.06426 21.0472 7.05737 21.2499 7.17306C21.4527 7.28875 21.574 7.50775 21.5646 7.74096L21.2277 16.1284C21.2197 16.3285 21.1162 16.5127 20.9494 16.6237L11.9336 22.6232C11.7666 22.7343 11.5564 22.7585 11.3685 22.6883L2.23473 19.2743C2.00112 19.187 1.84179 18.9692 1.82933 18.7201L1.40252 10.1857C1.39041 9.94356 1.5194 9.71628 1.73347 9.60253L2.89319 8.98631C3.19801 8.82434 3.57642 8.94015 3.73838 9.24497C3.8855 9.52184 3.80344 9.85944 3.55872 10.0404L4.46834 10.3669C4.529 10.1684 4.63256 9.64884 4.57793 9.06783C4.51992 8.45086 4.29459 7.8533 3.74994 7.45779C3.09256 6.98978 2.55044 6.51789 2.315 6.27264C2.07596 6.02363 2.08403 5.62799 2.33304 5.38894C2.58204 5.14989 2.97769 5.15797 3.21674 5.40697C3.38499 5.58224 3.87255 6.01278 4.49863 6.45635C5.12762 6.90198 5.83958 7.31975 6.4589 7.5144C7.00579 7.68628 7.7553 7.62969 8.5369 7.43649C9.3015 7.24751 10.0054 6.95105 10.4074 6.74228C10.5756 6.65494 10.7743 6.64864 10.9477 6.72514C12.2233 7.28795 12.9191 8.50607 13.2891 9.66169C13.5067 10.3415 13.6259 11.0415 13.6803 11.6632L15.3414 10.5898C15.3412 10.5032 15.3407 10.4155 15.3403 10.3268C15.3336 9.034 15.3259 7.52674 16.0328 6.1972C15.7338 6.16682 15.3912 6.12949 15.0302 6.08539C13.9285 5.95083 12.5649 5.74352 11.7833 5.45362C11.0189 5.17008 10.3102 4.75223 9.80152 4.41446C9.6696 4.32685 9.54977 4.24371 9.4444 4.16843C9.26969 4.41598 9.11811 4.6909 8.99766 4.9675C8.79907 5.42358 8.71173 5.82238 8.71173 6.05267C8.71173 6.39784 8.43191 6.67767 8.08673 6.67767C7.74155 6.67767 7.46173 6.39784 7.46173 6.05267C7.46173 5.58769 7.61509 5.01162 7.8516 4.46846C8.09203 3.91632 8.44552 3.33542 8.89963 2.8725C9.12701 2.64071 9.4943 2.62192 9.74446 2.82883L9.74577 2.8299C9.80956 2.88191 9.87475 2.93223 9.94039 2.98188C10.0714 3.08094 10.2612 3.21923 10.493 3.37315C10.9612 3.68404 11.5799 4.04492 12.218 4.28164C12.8391 4.512 14.0548 4.70696 15.1817 4.84461C15.7313 4.91174 16.2384 4.96292 16.6084 4.99732C16.8076 5.01584 17.007 5.03362 17.2065 5.04896C17.4444 5.06698 17.6512 5.21883 17.7397 5.44036C17.8282 5.66191 17.7828 5.9145 17.6228 6.09143C16.7171 7.09276 16.6045 8.33681 16.5923 9.78143L18.8039 8.35222C18.7998 8.30706 18.8006 8.26075 18.8068 8.21391C19.0047 6.71062 19.6821 5.84043 20.6001 5.48753C20.6783 5.45746 20.7569 5.4317 20.8356 5.40989C20.1821 4.96625 19.3286 4.50604 18.3225 4.22826C17.4178 3.97844 16.4732 3.80809 15.7493 3.70006C15.3886 3.64625 15.0857 3.60832 14.8736 3.58392C14.8084 3.57642 14.7519 3.57021 14.705 3.56521C14.6894 3.57354 14.6728 3.58282 14.6556 3.59303C14.5489 3.65657 14.4711 3.72644 14.4347 3.7856C14.2538 4.07957 13.8688 4.17123 13.5749 3.99032C13.2809 3.80941 13.1892 3.42445 13.3701 3.13047C13.5575 2.82606 13.8293 2.63024 14.0162 2.51897C14.1352 2.44809 14.2601 2.38531 14.3906 2.33829L14.3921 2.33776L14.3935 2.33729ZM12.4675 12.447C12.4635 11.7846 12.3687 10.8866 12.0986 10.0428C11.8096 9.1402 11.353 8.39584 10.6886 7.99621C10.209 8.21933 9.54785 8.47423 8.83684 8.64998C7.98278 8.86108 6.96103 8.98249 6.08412 8.70689C5.98146 8.67463 5.87826 8.63824 5.77495 8.59834C5.79615 8.71819 5.81166 8.83611 5.82244 8.95081C5.89602 9.73333 5.75996 10.4455 5.64541 10.7895L11.68 12.9559L12.4675 12.447ZM4.77065 13.1487C4.60756 13.0891 4.43494 13.2099 4.43494 13.3835V14.9513C4.43494 15.1613 4.5662 15.3489 4.76351 15.421L8.55169 16.8036C8.71479 16.8631 8.88741 16.7423 8.88741 16.5687V15.001C8.88741 14.7909 8.75614 14.6033 8.55884 14.5313L4.77065 13.1487ZM2.69778 11.0594L11.1256 14.085L11.0552 17.5412C11.0482 17.8863 11.3222 18.1718 11.6673 18.1788C12.0124 18.1859 12.2979 17.9118 12.3049 17.5667L12.3778 13.9933L20.2673 8.89485L19.9915 15.7596L12.2366 20.9201L12.2469 20.4127C12.254 20.0676 11.9799 19.7821 11.6348 19.7751C11.2897 19.768 11.0042 20.0421 10.9972 20.3872L10.9804 21.2088L3.05725 18.2473L2.69778 11.0594Z"
17
-
fill="currentColor"
18
-
/>
19
-
</svg>
20
-
);
21
-
};
-19
components/Icons/LooseleafSmall.tsx
-19
components/Icons/LooseleafSmall.tsx
···
1
-
import { Props } from "./Props";
2
-
3
-
export const LooseLeafSmall = (props: Props) => {
4
-
return (
5
-
<svg
6
-
width="24"
7
-
height="24"
8
-
viewBox="0 0 24 24"
9
-
fill="none"
10
-
xmlns="http://www.w3.org/2000/svg"
11
-
{...props}
12
-
>
13
-
<path
14
-
d="M16.5339 4.65788L21.9958 5.24186C22.4035 5.28543 22.7014 5.6481 22.6638 6.05632C22.5159 7.65303 22.3525 9.87767 22.0925 11.9186C21.9621 12.9418 21.805 13.9374 21.6091 14.8034C21.4166 15.6542 21.1733 16.442 20.8454 17.0104C20.1989 18.131 19.0036 18.9569 17.9958 19.4782C17.4793 19.7453 16.9792 19.9495 16.569 20.0827C16.3649 20.1489 16.1724 20.2013 16.0046 20.234C15.8969 20.255 15.7254 20.2816 15.5495 20.2682C15.5466 20.2681 15.5423 20.2684 15.5378 20.2682C15.527 20.2678 15.5112 20.267 15.4919 20.2663C15.4526 20.2647 15.3959 20.2623 15.3239 20.2584C15.1788 20.2506 14.9699 20.2366 14.7116 20.2145C14.1954 20.1703 13.4757 20.0909 12.6598 19.9489C11.0477 19.6681 8.97633 19.1301 7.36198 18.0807C6.70824 17.6557 5.95381 17.064 5.21842 16.4469C5.09798 16.5214 4.97261 16.591 4.81803 16.6706C4.28341 16.9455 3.71779 17.0389 3.17935 16.9137C2.64094 16.7885 2.20091 16.4608 1.89126 16.0231C1.28226 15.1618 1.16463 13.8852 1.5729 12.5514L1.60708 12.4606C1.7005 12.255 1.88295 12.1001 2.10513 12.0436C2.35906 11.9792 2.62917 12.0524 2.81607 12.236L2.82486 12.2448C2.8309 12.2507 2.84033 12.2596 2.8522 12.2712C2.87664 12.295 2.91343 12.3309 2.9606 12.3766C3.05513 12.4682 3.19281 12.6016 3.3649 12.7653C3.70953 13.0931 4.19153 13.5443 4.73795 14.0378C5.84211 15.0349 7.17372 16.1691 8.17937 16.8229C9.53761 17.7059 11.3696 18.2017 12.9177 18.4713C13.6815 18.6043 14.3565 18.679 14.8395 18.7204C15.0804 18.741 15.2731 18.7533 15.404 18.7604C15.4691 18.7639 15.5195 18.7659 15.5524 18.7672C15.5684 18.7679 15.5809 18.7689 15.5886 18.7692H15.5983L15.6374 18.7731C15.6457 18.7724 15.671 18.7704 15.7175 18.7614C15.8087 18.7436 15.9399 18.7095 16.1052 18.6559C16.4345 18.549 16.8594 18.3773 17.3063 18.1461C18.2257 17.6706 19.1147 17.0089 19.5466 16.2604C19.7578 15.8941 19.9618 15.2874 20.1462 14.4723C20.3271 13.6723 20.4767 12.7294 20.6042 11.7292C20.8232 10.0102 20.9711 8.17469 21.1042 6.65397L16.3747 6.14909C15.963 6.10498 15.6648 5.73562 15.7087 5.3239C15.7528 4.91222 16.1222 4.61399 16.5339 4.65788ZM12.0593 13.1315L12.2038 13.1647L12.3776 13.235C12.7592 13.4197 12.9689 13.7541 13.0837 14.0573C13.2089 14.3885 13.2545 14.7654 13.2858 15.0573C13.3144 15.3233 13.3319 15.5214 13.361 15.6774C13.4345 15.6215 13.5233 15.5493 13.6413 15.4479C13.7924 15.318 14.0034 15.1374 14.2429 15.0114C14.4965 14.878 14.8338 14.7772 15.2175 14.8747C15.5354 14.9556 15.7394 15.1539 15.8679 15.3229C15.9757 15.4648 16.0814 15.6631 16.1247 15.736C16.1889 15.8438 16.2218 15.8788 16.239 15.8922C16.2438 15.896 16.2462 15.8979 16.2497 15.8991C16.2541 15.9005 16.2717 15.9049 16.3093 15.9049C16.6541 15.9051 16.934 16.1851 16.9343 16.5299C16.9343 16.875 16.6543 17.1548 16.3093 17.1549C15.9766 17.1549 15.6957 17.0542 15.4694 16.8776C15.2617 16.7153 15.1322 16.5129 15.0505 16.3756C14.9547 16.2147 14.9262 16.1561 14.8815 16.0944C14.8684 16.0989 14.849 16.1051 14.8249 16.1178C14.7289 16.1684 14.6182 16.2555 14.4557 16.3952C14.3175 16.514 14.1171 16.6946 13.9069 16.821C13.6882 16.9524 13.3571 17.0902 12.9684 16.9938C12.4305 16.8602 12.2473 16.3736 12.1764 16.1051C12.1001 15.8159 12.0709 15.4542 12.0427 15.1911C12.0102 14.8884 11.9751 14.662 11.9138 14.4997C11.9011 14.4662 11.8884 14.4403 11.8776 14.4206C11.7899 14.4801 11.6771 14.5721 11.5329 14.7047C11.3855 14.8404 11.181 15.0386 11.0016 15.196C10.8175 15.3575 10.5936 15.5364 10.3512 15.6569C10.19 15.737 9.99118 15.7919 9.77214 15.7594C9.55026 15.7264 9.38367 15.6153 9.27019 15.5045C9.08085 15.3197 8.96362 15.0503 8.91081 14.9391C8.8766 14.8671 8.85074 14.814 8.82585 14.7692C8.541 14.777 8.27798 14.5891 8.20378 14.3014C8.11797 13.9674 8.31907 13.6269 8.653 13.5407L8.79558 13.5124C8.93966 13.4936 9.0875 13.5034 9.23308 13.5485C9.42396 13.6076 9.569 13.7155 9.67449 13.8239C9.85113 14.0055 9.96389 14.244 10.027 14.3776C10.0723 14.3417 10.124 14.3034 10.1774 14.2565C10.3474 14.1073 10.4942 13.9615 10.6862 13.7848C10.8571 13.6276 11.0614 13.4475 11.2731 13.32C11.4428 13.2178 11.7294 13.081 12.0593 13.1315ZM2.84537 14.3366C2.88081 14.6965 2.98677 14.9742 3.11588 15.1569C3.24114 15.334 3.38295 15.4211 3.5192 15.4528C3.63372 15.4794 3.79473 15.4775 4.00553 15.3932C3.9133 15.3109 3.82072 15.2311 3.73209 15.151C3.40947 14.8597 3.10909 14.5828 2.84537 14.3366ZM8.73601 3.86003C9.14672 3.91292 9.43715 4.28918 9.38445 4.69987C9.25964 5.66903 9.14642 7.35598 8.87077 9.02018C8.59001 10.7151 8.11848 12.5766 7.20085 14.1003C6.98712 14.4551 6.52539 14.5698 6.17057 14.3561C5.81623 14.1423 5.70216 13.6814 5.91569 13.3268C6.68703 12.0463 7.121 10.4066 7.39128 8.77506C7.66663 7.11265 7.74965 5.64618 7.89616 4.50847C7.94916 4.09794 8.32546 3.80744 8.73601 3.86003ZM11.7614 8.36784C12.1238 8.21561 12.4973 8.25977 12.8054 8.46452C13.0762 8.64474 13.2601 8.92332 13.3884 9.18912C13.5214 9.46512 13.6241 9.79028 13.7009 10.1354C13.7561 10.3842 13.7827 10.6162 13.8034 10.8044C13.8257 11.0069 13.8398 11.1363 13.864 11.2438C13.8806 11.3174 13.8959 11.3474 13.9011 11.3561C13.9095 11.3609 13.9289 11.3695 13.9655 11.3786C14.0484 11.3991 14.0814 11.3929 14.0895 11.3913C14.1027 11.3885 14.1323 11.3804 14.2028 11.3366C14.3137 11.2677 14.6514 11.0042 15.0563 10.8288L15.1364 10.7985C15.3223 10.7392 15.4987 10.7526 15.6335 10.7838C15.7837 10.8188 15.918 10.883 16.0231 10.9421C16.2276 11.057 16.4458 11.2251 16.613 11.3503C16.8019 11.4917 16.9527 11.5999 17.0827 11.6676C17.1539 11.7047 17.1908 11.7142 17.2009 11.7165L17.2849 11.7047C17.5751 11.6944 17.8425 11.8891 17.9138 12.1823C17.995 12.5174 17.7897 12.8554 17.4548 12.9372C17.0733 13.0299 16.7253 12.8909 16.5046 12.776C16.2705 12.6541 16.042 12.4845 15.864 12.3512C15.6704 12.2064 15.5344 12.1038 15.4216 12.0387C15.2178 12.1436 15.1125 12.2426 14.862 12.3981C14.7283 12.4811 14.5564 12.5716 14.3415 12.6159C14.1216 12.6611 13.8975 12.6501 13.6647 12.5924C13.3819 12.5222 13.1344 12.3858 12.9479 12.1657C12.7701 11.9555 12.689 11.7172 12.6442 11.5182C12.601 11.3259 12.58 11.112 12.5612 10.9411C12.5408 10.7561 12.5194 10.5827 12.4802 10.4059C12.4169 10.1215 12.3411 9.89526 12.2624 9.73209C12.2296 9.66404 12.1981 9.61255 12.1716 9.57487C12.1263 9.61576 12.0615 9.68493 11.9802 9.7985C11.8864 9.92952 11.7821 10.0922 11.6589 10.2838C11.5393 10.4698 11.4043 10.6782 11.2634 10.8786C11.123 11.0782 10.9664 11.2843 10.7975 11.4635C10.633 11.6381 10.4285 11.8185 10.1862 11.9342C9.87476 12.0828 9.50095 11.9507 9.35222 11.6393C9.20377 11.3279 9.33594 10.9551 9.64714 10.8063C9.69148 10.7851 9.77329 10.7282 9.88835 10.6061C9.99931 10.4883 10.1167 10.3365 10.2409 10.1598C10.3647 9.98378 10.4855 9.79617 10.6071 9.60709C10.7249 9.42397 10.8479 9.23258 10.9636 9.07096C11.1814 8.76677 11.4424 8.50191 11.7614 8.36784ZM12.4304 2.81218C13.631 2.81246 14.6042 3.78628 14.6042 4.98698C14.6041 5.39899 14.4869 5.78271 14.2878 6.111L15.0007 6.9069C15.2772 7.21532 15.2515 7.689 14.9431 7.96549C14.6347 8.24164 14.1609 8.21606 13.8845 7.90788L13.1139 7.0485C12.8988 7.11984 12.6695 7.16075 12.4304 7.16081C11.2296 7.16081 10.2558 6.18766 10.2555 4.98698C10.2555 3.7861 11.2295 2.81218 12.4304 2.81218ZM12.4304 4.31218C12.0579 4.31218 11.7555 4.61453 11.7555 4.98698C11.7558 5.35924 12.058 5.66081 12.4304 5.66081C12.8024 5.66053 13.104 5.35907 13.1042 4.98698C13.1042 4.6147 12.8026 4.31246 12.4304 4.31218Z"
15
-
fill="currentColor"
16
-
/>
17
-
</svg>
18
-
);
19
-
};
+21
components/Icons/TemplateRemoveSmall.tsx
+21
components/Icons/TemplateRemoveSmall.tsx
···
1
+
import { Props } from "./Props";
2
+
3
+
export const TemplateRemoveSmall = (props: Props) => {
4
+
return (
5
+
<svg
6
+
width="24"
7
+
height="24"
8
+
viewBox="0 0 24 24"
9
+
fill="none"
10
+
xmlns="http://www.w3.org/2000/svg"
11
+
{...props}
12
+
>
13
+
<path
14
+
fillRule="evenodd"
15
+
clipRule="evenodd"
16
+
d="M21.6598 1.22969C22.0503 0.839167 22.6835 0.839167 23.074 1.22969C23.4646 1.62021 23.4646 2.25338 23.074 2.6439L21.9991 3.71887C22 3.72121 22.001 3.72355 22.002 3.7259L21.0348 4.69374C21.0347 4.69033 21.0345 4.68693 21.0344 4.68353L17.2882 8.42972L17.2977 8.43313L16.3813 9.35011L16.3714 9.34656L15.5955 10.1224L15.6058 10.1261L14.6894 11.0431L14.6787 11.0393L14.3959 11.3221L14.4067 11.326L13.4903 12.2429L13.479 12.2389L12.8919 12.8261L12.9034 12.8302L10.2156 15.5198L10.2028 15.5152L9.35969 16.3583C9.36255 16.3614 9.36541 16.3645 9.36826 16.3676L7.20585 18.5314C7.19871 18.5321 7.19159 18.5328 7.18448 18.5335L6.26611 19.4519C6.27069 19.4539 6.27528 19.4559 6.27989 19.4579L5.40679 20.3316C5.40244 20.3291 5.39809 20.3267 5.39376 20.3242L2.54817 23.1698C2.15765 23.5603 1.52448 23.5603 1.13396 23.1698C0.743434 22.7793 0.743433 22.1461 1.13396 21.7556L4.57518 18.3144C4.5862 18.296 4.59778 18.2779 4.6099 18.2599C4.72342 18.0917 4.86961 17.964 5.02393 17.8656L6.39488 16.4947C6.25376 16.4822 6.10989 16.4734 5.96441 16.4685C5.20904 16.4433 4.461 16.5264 3.88183 16.7201C3.2818 16.9207 2.99485 17.1912 2.91069 17.4452C2.80892 17.7525 2.47737 17.919 2.17013 17.8173C1.8629 17.7155 1.69634 17.3839 1.79811 17.0767C2.05627 16.2973 2.78206 15.852 3.51019 15.6085C4.2592 15.3581 5.15477 15.2689 6.00346 15.2972C6.48903 15.3133 6.97583 15.3686 7.42782 15.4617L8.11942 14.7701L7.89431 14.6896C7.7838 14.6501 7.69213 14.5705 7.63742 14.4667L5.91365 11.1952C5.86162 11.0964 5.84836 10.9944 5.86434 10.9002L5.85245 10.9196L5.11563 9.4308C4.96523 9.11293 5.04515 8.78343 5.24544 8.56361L5.25054 8.55806C5.25749 8.55058 5.26457 8.54323 5.2718 8.53601L6.43022 7.3457C6.6445 7.11834 6.97346 7.03892 7.26837 7.14439L9.80363 8.05107L12.9624 7.10485C13.1067 7.02062 13.2859 6.99834 13.4555 7.05901L14.4322 7.40831C14.7942 6.69891 14.93 5.89897 15.0777 5.02873L15.0777 5.02872L15.0958 4.9222C15.2586 3.96572 15.4529 2.86736 16.1798 2.04515C17.0056 1.11114 18.7307 0.837125 20.2663 1.83615C20.4285 1.94168 20.5821 2.05061 20.7266 2.16294L21.6598 1.22969ZM19.8899 2.99965C19.8075 2.93935 19.72 2.87895 19.6271 2.81856C18.4897 2.07854 17.4326 2.39759 17.0579 2.82147C16.5869 3.3541 16.4234 4.10723 16.2512 5.11887L16.2231 5.28522L16.2231 5.28523C16.1304 5.83581 16.0274 6.44661 15.8342 7.05527L19.8899 2.99965ZM14.288 8.60148L13.2682 8.23675L11.6654 8.71688L13.5122 9.37736L14.288 8.60148ZM12.5953 10.2942L9.59692 9.22187L9.58424 9.21734L7.10654 8.33124L6.82935 8.61605L12.3125 10.577L12.5953 10.2942ZM11.3957 11.4938L6.56005 9.76447L6.04788 10.6006C6.16458 10.5123 6.32269 10.4767 6.48628 10.5352L10.8085 12.081L11.3957 11.4938ZM17.0099 12.2569L16.2294 11.9778L15.313 12.8948L16.8798 13.4551L18.7426 16.9905L18.0747 17.8398L19.1912 18.2615C19.6607 18.4294 20.1033 18.1358 20.2179 17.728L20.7391 16.3648C20.824 16.1511 20.8112 15.9108 20.7039 15.7071L19.124 12.7086L18.8949 11.321L18.8931 11.3104L18.8904 11.2969C18.8874 11.234 18.8742 11.1705 18.8497 11.1087L18.3522 9.8537L16.5121 11.6949L16.5482 11.7078L16.5582 11.7115L17.1419 11.9202L17.0099 12.2569ZM12.0382 16.1716L14.7261 13.482L16.0553 13.9574C16.1658 13.9969 16.2575 14.0764 16.3122 14.1803L18.0359 17.4518C18.2352 17.83 17.8658 18.2557 17.4633 18.1118L12.0382 16.1716ZM8.44038 19.7717L7.26492 20.9479C7.80247 21.0274 8.35468 21.0252 8.82243 20.8811C9.24804 20.7499 9.52382 20.5096 9.73008 20.285C9.79978 20.2091 9.87046 20.1246 9.92979 20.0536L9.92981 20.0536L9.92999 20.0534L9.9306 20.0527C9.95072 20.0286 9.96953 20.0061 9.98653 19.9861C10.0618 19.8973 10.1248 19.8281 10.1905 19.7694C10.307 19.6651 10.4472 19.579 10.6908 19.5395C10.9182 19.5027 11.2529 19.5041 11.7567 19.6004C11.6943 19.6815 11.6359 19.764 11.5823 19.8476C11.3276 20.2439 11.1352 20.7322 11.2038 21.2293C11.3097 21.9955 11.8139 22.4463 12.3522 22.6544C12.8626 22.8518 13.4377 22.8513 13.8631 22.731C14.7279 22.4863 15.6213 21.724 15.4107 20.664C15.3105 20.1591 14.9656 19.7211 14.4516 19.3701C14.3677 19.3128 14.2783 19.2571 14.1833 19.203C14.5987 19.0436 14.9889 19.0051 15.2828 19.1025C15.59 19.2042 15.9215 19.0377 16.0233 18.7304C16.1251 18.4232 15.9585 18.0916 15.6513 17.9899C14.6724 17.6656 13.5751 18.0821 12.7766 18.6397C12.6141 18.5938 12.4436 18.5504 12.265 18.5097C11.5394 18.3444 10.9698 18.307 10.5035 18.3825C10.018 18.4612 9.67586 18.657 9.40877 18.8961C9.28262 19.009 9.17853 19.1268 9.09296 19.2277C9.06342 19.2625 9.03731 19.2937 9.0131 19.3227L9.01295 19.3228C8.9605 19.3856 8.91697 19.4377 8.86686 19.4922C8.73917 19.6313 8.63185 19.7134 8.47726 19.761C8.46519 19.7648 8.45289 19.7683 8.44038 19.7717ZM12.5683 20.4811C12.3863 20.7644 12.3505 20.965 12.3648 21.0689C12.4003 21.3259 12.5445 21.4722 12.7749 21.5613C13.0331 21.6611 13.3469 21.659 13.544 21.6032C14.1554 21.4302 14.2952 21.0637 14.2612 20.8923C14.2391 20.7814 14.1422 20.578 13.7907 20.338C13.6005 20.2082 13.347 20.076 13.0173 19.9508C12.8341 20.1242 12.681 20.3057 12.5683 20.4811Z"
17
+
fill="currentColor"
18
+
/>
19
+
</svg>
20
+
);
21
+
};
+25
components/Icons/TemplateSmall.tsx
+25
components/Icons/TemplateSmall.tsx
···
1
+
import { Props } from "./Props";
2
+
3
+
export const TemplateSmall = (props: Props & { fill?: string }) => {
4
+
return (
5
+
<svg
6
+
width="24"
7
+
height="24"
8
+
viewBox="0 0 24 24"
9
+
fill="none"
10
+
xmlns="http://www.w3.org/2000/svg"
11
+
{...props}
12
+
>
13
+
<path
14
+
d="M14.1876 3.5073C14.3657 2.68428 14.8409 1.80449 15.1974 1.39941L15.2085 1.38682C15.5258 1.02605 16.1664 0.297788 17.7348 0.0551971C19.7272 -0.252968 22.338 1.22339 23.1781 3.53026C23.9464 5.63998 22.4863 7.65134 21.1778 8.49107C20.443 8.96256 19.8776 9.29865 19.5389 9.6655C19.6381 9.88024 19.8755 10.4623 19.9945 10.8588C20.1304 11.312 20.1356 11.8263 20.2444 12.3342C20.6412 13.1008 21.4615 14.6122 21.6483 14.9894C21.9441 15.5868 22.0637 16.0554 21.901 16.59C21.7793 16.99 21.3809 18.0037 21.2098 18.4064C21.1134 18.6333 20.6741 19.1794 20.165 19.3516C19.5207 19.5694 19.2 19.533 18.2867 19.1682C17.9231 19.3768 17.3068 19.3194 17.0874 19.2128C16.9902 19.5392 16.6234 19.8695 16.4353 20.0055C16.5008 20.1749 16.6684 20.619 16.5759 21.4191C16.4257 22.7176 14.6119 24.4819 12.2763 23.8544C10.5744 23.3971 10.2099 22.1002 10.0744 21.5462C8.16651 22.8209 5.74592 21.9772 4.43632 21.1133C3.44653 20.4603 3.16063 19.4467 3.2199 18.7888C2.57837 19.147 1.33433 19.2159 0.756062 17.9729C0.320217 17.036 0.838862 15.6535 2.49397 14.7706C3.56898 14.1971 5.01017 14.061 6.14456 14.136C5.47545 12.9417 4.17774 10.4051 3.97777 9.74456C3.72779 8.91889 3.94746 8.3129 4.30348 7.88113C4.6595 7.44936 5.21244 6.90396 5.75026 6.38129C6.28808 5.85862 7.06074 5.85862 7.7349 6.07072C8.27424 6.2404 9.36352 6.65146 9.84074 6.83578C10.5069 6.63086 11.9689 6.18102 12.4877 6.02101C13.0065 5.861 13.184 5.78543 13.7188 5.90996C13.8302 5.37643 14.0045 4.35336 14.1876 3.5073Z"
15
+
fill={props.fill || "transparent"}
16
+
/>
17
+
<path
18
+
fillRule="evenodd"
19
+
clipRule="evenodd"
20
+
d="M19.6271 2.81856C18.4896 2.07854 17.4326 2.39759 17.0578 2.82147C16.5869 3.3541 16.4234 4.10723 16.2512 5.11887L16.2231 5.28522L16.2231 5.28523C16.0919 6.06363 15.9405 6.96241 15.5423 7.80533L17.4557 8.48962C18.0778 7.71969 18.7304 7.28473 19.2974 6.92363L19.3687 6.87829C20.0258 6.46022 20.473 6.17579 20.7913 5.5972C21.0667 5.09643 21.0978 4.64884 20.9415 4.23092C20.7767 3.79045 20.3738 3.3044 19.6271 2.81856ZM15.0777 5.02873C14.9299 5.89897 14.7941 6.69891 14.4321 7.4083L13.4555 7.05901C13.2858 6.99834 13.1067 7.02061 12.9624 7.10485L9.80359 8.05107L7.26833 7.14438C6.97342 7.03892 6.64447 7.11834 6.43018 7.3457L5.27176 8.53601C5.26453 8.54323 5.25745 8.55058 5.2505 8.55806L5.2454 8.56361C5.04511 8.78343 4.9652 9.11292 5.1156 9.43079L5.85241 10.9196L5.8643 10.9002C5.84832 10.9944 5.86158 11.0964 5.91361 11.1952L7.63738 14.4667C7.6921 14.5705 7.78376 14.6501 7.89428 14.6896L17.4633 18.1118C17.8658 18.2557 18.2352 17.83 18.0359 17.4518L16.3121 14.1803C16.2574 14.0764 16.1657 13.9969 16.0552 13.9574L6.48624 10.5352C6.32266 10.4767 6.16454 10.5123 6.04784 10.6006L6.56002 9.76447L16.8798 13.4551L18.7426 16.9905L18.0747 17.8398L19.1912 18.2615C19.6606 18.4294 20.1033 18.1358 20.2179 17.728L20.7391 16.3648C20.8239 16.1511 20.8112 15.9108 20.7039 15.7071L19.124 12.7086L18.8949 11.321C18.8935 11.3129 18.892 11.3049 18.8904 11.2969C18.8874 11.234 18.8741 11.1705 18.8496 11.1087L18.1936 9.45372C18.7455 8.68856 19.3357 8.28878 19.927 7.9122C19.9681 7.88603 20.0096 7.85977 20.0514 7.83331C20.6663 7.44436 21.3511 7.01112 21.8182 6.16211C22.2345 5.40522 22.3314 4.60167 22.0392 3.82037C21.7555 3.06161 21.1334 2.40034 20.2662 1.83615C18.7307 0.837123 17.0056 1.11114 16.1798 2.04515C15.4528 2.86736 15.2586 3.96572 15.0958 4.92219L15.0777 5.02872L15.0777 5.02873ZM13.2681 8.23675L11.6653 8.71688L16.3567 10.3947L16.6254 9.4374L13.2681 8.23675ZM16.5481 11.7078L16.5582 11.7114L17.1419 11.9202L17.0098 12.2569L6.82932 8.61605L7.1065 8.33124L9.5842 9.21734L9.59688 9.22187L16.5481 11.7078ZM12.5683 20.4811C12.3863 20.7644 12.3505 20.965 12.3648 21.0689C12.4003 21.3259 12.5444 21.4722 12.7748 21.5613C13.0331 21.6611 13.3469 21.659 13.544 21.6032C14.1553 21.4302 14.2952 21.0637 14.2611 20.8923C14.2391 20.7814 14.1421 20.578 13.7906 20.338C13.6004 20.2082 13.3469 20.076 13.0173 19.9508C12.834 20.1242 12.681 20.3057 12.5683 20.4811ZM11.7567 19.6004C11.6942 19.6815 11.6359 19.764 11.5822 19.8476C11.3276 20.2439 11.1351 20.7322 11.2038 21.2293C11.3096 21.9955 11.8139 22.4463 12.3521 22.6544C12.8626 22.8518 13.4377 22.8513 13.863 22.731C14.7279 22.4863 15.6213 21.724 15.4107 20.664C15.3104 20.1591 14.9656 19.7211 14.4515 19.3701C14.3677 19.3128 14.2783 19.2571 14.1833 19.203C14.5987 19.0436 14.9889 19.0051 15.2827 19.1025C15.59 19.2042 15.9215 19.0377 16.0233 18.7304C16.125 18.4232 15.9585 18.0916 15.6513 17.9899C14.6724 17.6656 13.5751 18.0821 12.7766 18.6397C12.6141 18.5938 12.4436 18.5504 12.265 18.5097C11.5393 18.3444 10.9698 18.307 10.5034 18.3825C10.018 18.4612 9.67582 18.657 9.40873 18.8961C9.28258 19.009 9.17849 19.1268 9.09292 19.2277C9.06338 19.2625 9.03727 19.2937 9.01306 19.3227L9.01291 19.3228C8.96046 19.3856 8.91693 19.4377 8.86682 19.4922C8.73913 19.6313 8.63181 19.7134 8.47722 19.761C8.03942 19.896 7.30137 19.8237 6.60705 19.5851C6.27195 19.4699 5.98787 19.3293 5.79222 19.1916C5.64379 19.0871 5.59428 19.019 5.58047 19L5.58045 19C5.57827 18.997 5.57698 18.9952 5.57634 18.9947C5.57144 18.9579 5.57397 18.938 5.57539 18.9305C5.57674 18.9233 5.57829 18.9201 5.58128 18.9156C5.59031 18.9023 5.63142 18.8546 5.76375 18.7965C6.04383 18.6735 6.48291 18.6061 7.03421 18.5487C7.12534 18.5392 7.22003 18.5299 7.31675 18.5205L7.31734 18.5205L7.31774 18.5204C7.75337 18.478 8.22986 18.4315 8.60602 18.3399C8.83695 18.2837 9.10046 18.1956 9.31444 18.0333C9.55604 17.8501 9.73703 17.5659 9.72457 17.1949C9.71117 16.7955 9.50249 16.4807 9.2559 16.2553C9.01235 16.0327 8.69774 15.863 8.36729 15.7333C7.70363 15.4729 6.85166 15.3254 6.00343 15.2972C5.15473 15.2689 4.25916 15.3581 3.51015 15.6085C2.78202 15.852 2.05623 16.2973 1.79807 17.0767C1.6963 17.3839 1.86287 17.7155 2.1701 17.8173C2.47733 17.919 2.80889 17.7525 2.91065 17.4452C2.99481 17.1912 3.28176 16.9207 3.8818 16.7201C4.46096 16.5264 5.209 16.4433 5.96437 16.4685C6.7202 16.4937 7.43275 16.6256 7.93908 16.8243C8.19363 16.9243 8.36538 17.0292 8.46519 17.1204C8.4773 17.1315 8.4878 17.1419 8.49689 17.1515C8.45501 17.1668 8.39992 17.1838 8.3287 17.2012C8.04154 17.2711 7.67478 17.3072 7.24492 17.3496L7.24413 17.3497L7.24246 17.3498C7.13635 17.3603 7.02639 17.3711 6.91284 17.3829C6.38763 17.4376 5.76632 17.5153 5.29238 17.7234C5.0477 17.8309 4.78839 17.9954 4.60986 18.2599C4.42009 18.541 4.36482 18.8707 4.42432 19.213C4.49899 19.6426 4.83826 19.9534 5.11763 20.15C5.42736 20.368 5.81812 20.5533 6.22607 20.6935C7.01783 20.9656 8.03865 21.1226 8.82239 20.8811C9.248 20.7499 9.52379 20.5096 9.73004 20.285C9.79974 20.2091 9.87042 20.1246 9.92975 20.0536L9.92977 20.0536L9.92995 20.0534C9.9503 20.0291 9.96932 20.0063 9.98649 19.9861C10.0618 19.8973 10.1248 19.8281 10.1905 19.7694C10.3069 19.6651 10.4472 19.579 10.6908 19.5395C10.9181 19.5027 11.2529 19.5041 11.7567 19.6004Z"
21
+
fill="currentColor"
22
+
/>
23
+
</svg>
24
+
);
25
+
};
-19
components/Icons/UnpublishSmall.tsx
-19
components/Icons/UnpublishSmall.tsx
···
1
-
import { Props } from "./Props";
2
-
3
-
export const UnpublishSmall = (props: Props) => {
4
-
return (
5
-
<svg
6
-
width="24"
7
-
height="24"
8
-
viewBox="0 0 24 24"
9
-
fill="none"
10
-
xmlns="http://www.w3.org/2000/svg"
11
-
{...props}
12
-
>
13
-
<path
14
-
d="M15.5207 11.5526C15.9624 11.2211 16.5896 11.3101 16.9211 11.7518L18.9162 14.411L21.5754 12.4158C22.017 12.0845 22.6433 12.1735 22.9748 12.6151C23.306 13.0568 23.2172 13.684 22.7756 14.0155L20.1164 16.0106L22.1115 18.6698C22.4425 19.1114 22.3537 19.7378 21.9123 20.0692C21.4707 20.4006 20.8434 20.3114 20.5119 19.87L18.5168 17.2108L15.8576 19.2059C15.416 19.537 14.7897 19.4479 14.4582 19.0067C14.1267 18.565 14.2158 17.9378 14.6574 17.6063L17.3166 15.6112L15.3215 12.952C14.9902 12.5103 15.0792 11.8841 15.5207 11.5526ZM12.2062 4.29378C13.7932 3.59008 15.5128 3.49569 16.9767 4.29769C19.1391 5.48261 19.9471 8.15954 19.5314 10.8885C19.4793 11.2296 19.1606 11.4638 18.8195 11.4119C18.4786 11.3598 18.2444 11.042 18.2961 10.701C18.669 8.25384 17.8985 6.22855 16.3761 5.39436C15.5192 4.92484 14.4833 4.85746 13.4006 5.1805C13.3522 5.21491 13.3004 5.24633 13.2414 5.26644C13.0411 5.33451 12.8498 5.39707 12.6662 5.45686C12.6176 5.47894 12.5684 5.50065 12.5197 5.52425C11.1279 6.19898 9.77207 7.47892 8.81657 9.22249C7.86108 10.9662 7.51225 12.7985 7.69254 14.3348C7.87314 15.8723 8.57043 17.0593 9.65739 17.6551C10.3281 18.0226 11.1012 18.1431 11.9211 18.0272C12.2625 17.9791 12.5786 18.2161 12.6271 18.5575C12.6754 18.8992 12.4375 19.216 12.0959 19.2645C11.0448 19.4131 9.99397 19.2653 9.0568 18.7518C7.96346 18.1527 7.21589 17.1633 6.79801 15.9862C6.74111 15.914 6.69783 15.829 6.67692 15.7332C6.5875 15.3237 6.4571 14.8734 6.30387 14.4188C6.00205 14.7748 5.69607 15.0308 5.37419 15.1834C5.04355 15.3401 4.70719 15.3838 4.38102 15.327C4.06576 15.272 3.79527 15.129 3.57145 14.9696C2.96057 14.5342 2.36597 14.0627 1.89274 13.5487C1.4209 13.036 1.0333 12.4423 0.8986 11.7596C0.842171 11.4736 0.768809 11.1336 0.89274 10.5985C0.997303 10.1475 1.23987 9.57405 1.69059 8.73226L1.60758 8.66585C1.60246 8.66173 1.59696 8.65743 1.59196 8.65315C1.16612 8.2884 1.07023 7.69032 1.08708 7.21468C1.1054 6.69843 1.25893 6.12189 1.54411 5.6014C1.81576 5.10576 2.17253 4.65997 2.58903 4.35433C3.00424 4.04981 3.53772 3.84664 4.10661 3.97737C4.12165 3.98084 4.13775 3.98453 4.15251 3.98909L5.22575 4.3221C5.62556 4.21028 6.05447 4.1958 6.48747 4.32015L6.54801 4.34065L6.54997 4.34163C6.55156 4.34227 6.55431 4.34319 6.55778 4.34456C6.56529 4.34752 6.57742 4.35226 6.59294 4.35823C6.62402 4.3702 6.67024 4.3877 6.72868 4.40901C6.84618 4.45186 7.01173 4.50951 7.20133 4.56819C7.59399 4.6897 8.04168 4.79978 8.382 4.81624C9.99154 4.89405 10.8568 4.72942 12.2062 4.29378ZM12.5441 6.13655C13.7669 5.47408 15.1231 5.29219 16.256 5.91292C17.1747 6.41641 17.7296 7.33256 17.9572 8.39729C18.0148 8.66723 17.8433 8.93322 17.5734 8.99104C17.3035 9.04869 17.0375 8.8771 16.9797 8.60726C16.7956 7.74535 16.3745 7.11819 15.7756 6.78987C15.0408 6.38732 14.0621 6.45197 13.0216 7.01546C12.7704 7.15159 12.5186 7.31527 12.2716 7.50472C13.0464 8.19627 13.6187 8.92334 13.9347 9.64632C14.2881 10.4549 14.3328 11.2901 13.9328 12.0203C13.5333 12.7492 12.7922 13.1542 11.9211 13.2918C11.1394 13.4153 10.2177 13.3313 9.2277 13.0614C9.20118 13.3705 9.19947 13.6697 9.21989 13.9539C9.30483 15.1342 9.77626 15.9936 10.5109 16.3963C10.8983 16.6086 11.346 16.6898 11.8351 16.6405C12.1098 16.6128 12.3552 16.8131 12.383 17.0877C12.4107 17.3624 12.2103 17.6077 11.9357 17.6356C11.2725 17.7026 10.6177 17.5951 10.0304 17.2733C8.89778 16.6525 8.32161 15.4121 8.22184 14.0252C8.12182 12.6321 8.49018 11.0188 9.32243 9.49983C10.1548 7.98089 11.316 6.80199 12.5441 6.13655ZM2.67204 9.54866C2.32412 10.2204 2.17134 10.6184 2.11051 10.8807C2.04887 11.1469 2.07605 11.2695 2.12516 11.5184C2.19851 11.8898 2.4242 12.2809 2.81169 12.702C3.1981 13.1217 3.71082 13.5349 4.29606 13.952C4.42383 14.043 4.52152 14.0826 4.59489 14.0955C4.65746 14.1064 4.73234 14.1036 4.83805 14.0535C5.04286 13.9565 5.35376 13.6844 5.76383 13.035C5.42543 12.2826 5.08809 11.7185 4.84391 11.4735C4.57886 11.2075 4.20518 10.9304 3.87907 10.7108C3.71974 10.6035 3.57875 10.514 3.4777 10.452C3.42724 10.421 3.3866 10.3967 3.35954 10.3807C3.34614 10.3728 3.33581 10.366 3.32926 10.3621L3.32047 10.3582C3.29879 10.3457 3.278 10.3312 3.25797 10.3162C2.98299 10.1101 2.79521 9.83996 2.67204 9.54866ZM11.5216 8.17561C11.0336 8.67806 10.5807 9.28455 10.1994 9.9803C9.81804 10.6763 9.54956 11.3844 9.38883 12.0662C10.3261 12.3341 11.1364 12.4037 11.7648 12.3045C12.4323 12.1991 12.8487 11.9177 13.0558 11.5399C13.2683 11.1518 13.2832 10.6541 13.0177 10.0467C12.7657 9.47024 12.2702 8.82723 11.5216 8.17561ZM9.63883 6.07112C9.45477 6.07962 9.26355 6.08427 9.06266 6.08382C9.01613 6.11598 8.96536 6.1545 8.91032 6.20003C8.71163 6.36444 8.4977 6.58912 8.28434 6.84651C7.85781 7.36118 7.46925 7.96403 7.24626 8.37093C6.99703 8.82575 6.71681 9.39869 6.51969 9.97542C6.34987 10.4725 6.25688 10.9316 6.26969 11.3055C6.3691 11.4655 6.46736 11.6376 6.56266 11.8182C6.76355 10.7536 7.14751 9.66653 7.71989 8.6219C8.25537 7.64475 8.9105 6.78559 9.63883 6.07112ZM6.12516 5.51741C5.92665 5.46415 5.72213 5.47396 5.50895 5.54378C5.15736 5.78936 4.57147 6.28659 4.28727 6.81136C3.94853 7.43736 3.7629 8.31657 3.71598 8.67561C3.71568 8.67793 3.71436 8.68015 3.71403 8.68245C3.72929 8.72056 3.74152 8.76064 3.74919 8.80257C3.79805 9.07007 3.89591 9.222 3.99626 9.30354L3.99723 9.3055C4.02922 9.32447 4.07496 9.35213 4.13102 9.38655C4.24364 9.45571 4.40052 9.5546 4.57731 9.67366C4.82014 9.83722 5.11483 10.0498 5.39079 10.283C5.44136 10.068 5.50384 9.85578 5.5734 9.65218C5.79598 9.00089 6.10514 8.37255 6.3693 7.89046C6.61869 7.4354 7.0422 6.77704 7.51481 6.20686C7.57748 6.13127 7.64175 6.05648 7.70719 5.98323C7.39142 5.92263 7.08276 5.84103 6.83219 5.76351C6.61847 5.69737 6.43222 5.63106 6.29997 5.58284C6.23424 5.55887 6.1809 5.53953 6.14372 5.52522C6.13705 5.52265 6.1308 5.51963 6.12516 5.51741ZM3.81559 5.19319C3.71663 5.17448 3.55572 5.19609 3.32926 5.36214C3.09558 5.53353 2.84889 5.82236 2.64079 6.20198C2.4462 6.55708 2.34736 6.94361 2.3361 7.25862C2.3235 7.61435 2.42004 7.7163 2.40446 7.70296L2.81657 8.03304C2.92255 7.54286 3.11192 6.88062 3.40739 6.33479C3.61396 5.95324 3.91707 5.60514 4.21794 5.31722L3.81559 5.19319Z"
15
-
fill="currentColor"
16
-
/>
17
-
</svg>
18
-
);
19
-
};
+1
-18
components/IdentityProvider.tsx
+1
-18
components/IdentityProvider.tsx
···
4
4
import useSWR, { KeyedMutator, mutate } from "swr";
5
5
import { DashboardState } from "./PageLayouts/DashboardLayout";
6
6
import { supabaseBrowserClient } from "supabase/browserClient";
7
-
import { produce, Draft } from "immer";
8
7
9
8
export type InterfaceState = {
10
9
dashboards: { [id: string]: DashboardState | undefined };
11
10
};
12
-
export type Identity = Awaited<ReturnType<typeof getIdentityData>>;
11
+
type Identity = Awaited<ReturnType<typeof getIdentityData>>;
13
12
let IdentityContext = createContext({
14
13
identity: null as Identity,
15
14
mutate: (() => {}) as KeyedMutator<Identity>,
16
15
});
17
16
export const useIdentityData = () => useContext(IdentityContext);
18
-
19
-
export function mutateIdentityData(
20
-
mutate: KeyedMutator<Identity>,
21
-
recipe: (draft: Draft<NonNullable<Identity>>) => void,
22
-
) {
23
-
mutate(
24
-
(data) => {
25
-
if (!data) return data;
26
-
return produce(data, recipe);
27
-
},
28
-
{ revalidate: false },
29
-
);
30
-
}
31
17
export function IdentityContextProvider(props: {
32
18
children: React.ReactNode;
33
19
initialValue: Identity;
···
35
21
let { data: identity, mutate } = useSWR("identity", () => getIdentityData(), {
36
22
fallbackData: props.initialValue,
37
23
});
38
-
useEffect(() => {
39
-
mutate(props.initialValue);
40
-
}, [props.initialValue]);
41
24
useEffect(() => {
42
25
if (!identity?.atp_did) return;
43
26
let supabase = supabaseBrowserClient();
+2
-10
components/Input.tsx
+2
-10
components/Input.tsx
···
100
100
JSX.IntrinsicElements["textarea"],
101
101
) => {
102
102
let { label, textarea, ...inputProps } = props;
103
-
let style = `
104
-
appearance-none resize-none w-full
105
-
bg-transparent
106
-
outline-hidden focus:outline-0
107
-
font-normal not-italic text-base text-primary disabled:text-tertiary
108
-
disabled:cursor-not-allowed
109
-
${props.className}`;
103
+
let style = `appearance-none w-full font-normal not-italic bg-transparent text-base text-primary focus:outline-0 ${props.className} outline-hidden resize-none`;
110
104
return (
111
-
<label
112
-
className={`input-with-border flex flex-col gap-px text-sm text-tertiary font-bold italic leading-tight py-1! px-[6px]! ${props.disabled && "bg-border-light! cursor-not-allowed! hover:border-border!"}`}
113
-
>
105
+
<label className=" input-with-border flex flex-col gap-px text-sm text-tertiary font-bold italic leading-tight py-1! px-[6px]!">
114
106
{props.label}
115
107
{textarea ? (
116
108
<textarea {...inputProps} className={style} />
+3
-2
components/Layout.tsx
+3
-2
components/Layout.tsx
···
1
-
"use client";
2
1
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
3
2
import { theme } from "tailwind.config";
4
3
import { NestedCardThemeProvider } from "./ThemeManager/ThemeProvider";
···
7
6
import { useState } from "react";
8
7
9
8
export const Separator = (props: { classname?: string }) => {
10
-
return <div className={`h-full border-r border-border ${props.classname}`} />;
9
+
return (
10
+
<div className={`min-h-full border-r border-border ${props.classname}`} />
11
+
);
11
12
};
12
13
13
14
export const Menu = (props: {
+23
-27
components/PageLayouts/DashboardLayout.tsx
+23
-27
components/PageLayouts/DashboardLayout.tsx
···
33
33
drafts: boolean;
34
34
published: boolean;
35
35
docs: boolean;
36
-
archived: boolean;
36
+
templates: boolean;
37
37
};
38
38
};
39
39
···
45
45
const defaultDashboardState: DashboardState = {
46
46
display: undefined,
47
47
sort: undefined,
48
-
filter: {
49
-
drafts: false,
50
-
published: false,
51
-
docs: false,
52
-
archived: false,
53
-
},
48
+
filter: { drafts: false, published: false, docs: false, templates: false },
54
49
};
55
50
56
51
export const useDashboardStore = create<DashboardStore>((set, get) => ({
···
260
255
hasBackgroundImage: boolean;
261
256
defaultDisplay: Exclude<DashboardState["display"], undefined>;
262
257
hasPubs: boolean;
263
-
hasArchived: boolean;
258
+
hasTemplates: boolean;
264
259
}) => {
265
260
let { display, sort } = useDashboardState();
266
261
display = display || props.defaultDisplay;
···
281
276
<DisplayToggle setState={setState} display={display} />
282
277
<Separator classname="h-4 min-h-4!" />
283
278
284
-
{props.hasPubs ? (
279
+
{props.hasPubs || props.hasTemplates ? (
285
280
<>
281
+
{props.hasPubs}
282
+
{props.hasTemplates}
286
283
<FilterOptions
287
284
hasPubs={props.hasPubs}
288
-
hasArchived={props.hasArchived}
285
+
hasTemplates={props.hasTemplates}
289
286
/>
290
287
<Separator classname="h-4 min-h-4!" />{" "}
291
288
</>
···
372
369
);
373
370
}
374
371
375
-
const FilterOptions = (props: {
376
-
hasPubs: boolean;
377
-
hasArchived: boolean;
378
-
}) => {
372
+
const FilterOptions = (props: { hasPubs: boolean; hasTemplates: boolean }) => {
379
373
let { filter } = useDashboardState();
380
374
let setState = useSetDashboardState();
381
375
let filterCount = Object.values(filter).filter(Boolean).length;
···
412
406
</>
413
407
)}
414
408
415
-
{props.hasArchived && (
416
-
<Checkbox
417
-
small
418
-
checked={filter.archived}
419
-
onChange={(e) =>
420
-
setState({
421
-
filter: { ...filter, archived: !!e.target.checked },
422
-
})
423
-
}
424
-
>
425
-
Archived
426
-
</Checkbox>
409
+
{props.hasTemplates && (
410
+
<>
411
+
<Checkbox
412
+
small
413
+
checked={filter.templates}
414
+
onChange={(e) =>
415
+
setState({
416
+
filter: { ...filter, templates: !!e.target.checked },
417
+
})
418
+
}
419
+
>
420
+
Templates
421
+
</Checkbox>
422
+
</>
427
423
)}
428
424
<Checkbox
429
425
small
···
445
441
docs: false,
446
442
published: false,
447
443
drafts: false,
448
-
archived: false,
444
+
templates: false,
449
445
},
450
446
});
451
447
}}
+6
-52
components/PageSWRDataProvider.tsx
+6
-52
components/PageSWRDataProvider.tsx
···
7
7
import { getPollData } from "actions/pollActions";
8
8
import type { GetLeafletDataReturnType } from "app/api/rpc/[command]/get_leaflet_data";
9
9
import { createContext, useContext } from "react";
10
-
import { getPublicationMetadataFromLeafletData } from "src/utils/getPublicationMetadataFromLeafletData";
11
-
import { getPublicationURL } from "app/lish/createPub/getPublicationURL";
12
-
import { AtUri } from "@atproto/syntax";
13
10
14
11
export const StaticLeafletDataContext = createContext<
15
12
null | GetLeafletDataReturnType["result"]["data"]
···
69
66
};
70
67
export function useLeafletPublicationData() {
71
68
let { data, mutate } = useLeafletData();
72
-
73
-
// First check for leaflets in publications
74
-
let pubData = getPublicationMetadataFromLeafletData(data);
75
-
76
69
return {
77
-
data: pubData || null,
70
+
data:
71
+
data?.leaflets_in_publications?.[0] ||
72
+
data?.permission_token_rights[0].entity_sets?.permission_tokens?.find(
73
+
(p) => p.leaflets_in_publications.length,
74
+
)?.leaflets_in_publications?.[0] ||
75
+
null,
78
76
mutate,
79
77
};
80
78
}
···
82
80
let { data, mutate } = useLeafletData();
83
81
return { data: data?.custom_domain_routes, mutate: mutate };
84
82
}
85
-
86
-
export function useLeafletPublicationStatus() {
87
-
const data = useContext(StaticLeafletDataContext);
88
-
if (!data) return null;
89
-
90
-
const publishedInPublication = data.leaflets_in_publications?.find(
91
-
(l) => l.doc,
92
-
);
93
-
const publishedStandalone = data.leaflets_to_documents?.find(
94
-
(l) => !!l.documents,
95
-
);
96
-
97
-
const documentUri =
98
-
publishedInPublication?.documents?.uri ?? publishedStandalone?.document;
99
-
100
-
// Compute the full post URL for sharing
101
-
let postShareLink: string | undefined;
102
-
if (publishedInPublication?.publications && publishedInPublication.documents) {
103
-
// Published in a publication - use publication URL + document rkey
104
-
const docUri = new AtUri(publishedInPublication.documents.uri);
105
-
postShareLink = `${getPublicationURL(publishedInPublication.publications)}/${docUri.rkey}`;
106
-
} else if (publishedStandalone?.document) {
107
-
// Standalone published post - use /p/{did}/{rkey} format
108
-
const docUri = new AtUri(publishedStandalone.document);
109
-
postShareLink = `/p/${docUri.host}/${docUri.rkey}`;
110
-
}
111
-
112
-
return {
113
-
token: data,
114
-
leafletId: data.root_entity,
115
-
shareLink: data.id,
116
-
// Draft state - in a publication but not yet published
117
-
draftInPublication:
118
-
data.leaflets_in_publications?.[0]?.publication ?? undefined,
119
-
// Published state
120
-
isPublished: !!(publishedInPublication || publishedStandalone),
121
-
publishedAt:
122
-
publishedInPublication?.documents?.indexed_at ??
123
-
publishedStandalone?.documents?.indexed_at,
124
-
documentUri,
125
-
// Full URL for sharing published posts
126
-
postShareLink,
127
-
};
128
-
}
+1
-4
components/Pages/Page.tsx
+1
-4
components/Pages/Page.tsx
···
16
16
import { PageOptions } from "./PageOptions";
17
17
import { CardThemeProvider } from "components/ThemeManager/ThemeProvider";
18
18
import { useDrawerOpen } from "app/lish/[did]/[publication]/[rkey]/Interactions/InteractionDrawer";
19
-
import { usePreserveScroll } from "src/hooks/usePreserveScroll";
20
19
21
20
export function Page(props: {
22
21
entityID: string;
···
61
60
/>
62
61
}
63
62
>
64
-
{props.first && pageType === "doc" && (
63
+
{props.first && (
65
64
<>
66
65
<PublicationMetadata />
67
66
</>
···
84
83
pageType: "canvas" | "doc";
85
84
drawerOpen: boolean | undefined;
86
85
}) => {
87
-
let { ref } = usePreserveScroll<HTMLDivElement>(props.id);
88
86
return (
89
87
// this div wraps the contents AND the page options.
90
88
// it needs to be its own div because this container does NOT scroll, and therefore doesn't clip the absolutely positioned pageOptions
···
97
95
it needs to be a separate div so that the user can scroll from anywhere on the page if there isn't a card border
98
96
*/}
99
97
<div
100
-
ref={ref}
101
98
onClick={props.onClickAction}
102
99
id={props.id}
103
100
className={`
+13
-19
components/Pages/PublicationMetadata.tsx
+13
-19
components/Pages/PublicationMetadata.tsx
···
25
25
let record = pub?.documents?.data as PubLeafletDocument.Record | null;
26
26
let publishedAt = record?.publishedAt;
27
27
28
-
if (!pub) return null;
28
+
if (!pub || !pub.publications) return null;
29
29
30
30
if (typeof title !== "string") {
31
31
title = pub?.title || "";
···
36
36
return (
37
37
<div className={`flex flex-col px-3 sm:px-4 pb-5 sm:pt-3 pt-2`}>
38
38
<div className="flex gap-2">
39
-
{pub.publications && (
40
-
<Link
41
-
href={
42
-
identity?.atp_did === pub.publications?.identity_did
43
-
? `${getBasePublicationURL(pub.publications)}/dashboard`
44
-
: getPublicationURL(pub.publications)
45
-
}
46
-
className="leafletMetadata text-accent-contrast font-bold hover:no-underline"
47
-
>
48
-
{pub.publications?.name}
49
-
</Link>
50
-
)}
39
+
<Link
40
+
href={
41
+
identity?.atp_did === pub.publications?.identity_did
42
+
? `${getBasePublicationURL(pub.publications)}/dashboard`
43
+
: getPublicationURL(pub.publications)
44
+
}
45
+
className="leafletMetadata text-accent-contrast font-bold hover:no-underline"
46
+
>
47
+
{pub.publications?.name}
48
+
</Link>
51
49
<div className="font-bold text-tertiary px-1 text-sm flex place-items-center bg-border-light rounded-md ">
52
50
Editor
53
51
</div>
···
83
81
<Link
84
82
target="_blank"
85
83
className="text-sm"
86
-
href={
87
-
pub.publications
88
-
? `${getPublicationURL(pub.publications)}/${new AtUri(pub.doc).rkey}`
89
-
: `/p/${new AtUri(pub.doc).host}/${new AtUri(pub.doc).rkey}`
90
-
}
84
+
href={`${getPublicationURL(pub.publications)}/${new AtUri(pub.doc).rkey}`}
91
85
>
92
86
View Post
93
87
</Link>
···
175
169
let record = pub?.documents?.data as PubLeafletDocument.Record | null;
176
170
let publishedAt = record?.publishedAt;
177
171
178
-
if (!pub) return null;
172
+
if (!pub || !pub.publications) return null;
179
173
180
174
return (
181
175
<div className={`flex flex-col px-3 sm:px-4 pb-5 sm:pt-3 pt-2`}>
+43
-2
components/ThemeManager/PubThemeSetter.tsx
+43
-2
components/ThemeManager/PubThemeSetter.tsx
···
16
16
import { PubAccentPickers } from "./PubPickers/PubAcccentPickers";
17
17
import { Separator } from "components/Layout";
18
18
import { PubSettingsHeader } from "app/lish/[did]/[publication]/dashboard/PublicationSettings";
19
-
import { ColorToRGB, ColorToRGBA } from "./colorToLexicons";
20
19
21
20
export type ImageState = {
22
21
src: string;
···
40
39
theme: localPubTheme,
41
40
setTheme,
42
41
changes,
43
-
} = useLocalPubTheme(record?.theme, showPageBackground);
42
+
} = useLocalPubTheme(record, showPageBackground);
44
43
let [image, setImage] = useState<ImageState | null>(
45
44
PubLeafletThemeBackgroundImage.isMain(record?.theme?.backgroundImage)
46
45
? {
···
344
343
</div>
345
344
);
346
345
};
346
+
347
+
export function ColorToRGBA(color: Color) {
348
+
if (!color)
349
+
return {
350
+
$type: "pub.leaflet.theme.color#rgba" as const,
351
+
r: 0,
352
+
g: 0,
353
+
b: 0,
354
+
a: 1,
355
+
};
356
+
let c = color.toFormat("rgba");
357
+
const r = c.getChannelValue("red");
358
+
const g = c.getChannelValue("green");
359
+
const b = c.getChannelValue("blue");
360
+
const a = c.getChannelValue("alpha");
361
+
return {
362
+
$type: "pub.leaflet.theme.color#rgba" as const,
363
+
r: Math.round(r),
364
+
g: Math.round(g),
365
+
b: Math.round(b),
366
+
a: Math.round(a * 100),
367
+
};
368
+
}
369
+
function ColorToRGB(color: Color) {
370
+
if (!color)
371
+
return {
372
+
$type: "pub.leaflet.theme.color#rgb" as const,
373
+
r: 0,
374
+
g: 0,
375
+
b: 0,
376
+
};
377
+
let c = color.toFormat("rgb");
378
+
const r = c.getChannelValue("red");
379
+
const g = c.getChannelValue("green");
380
+
const b = c.getChannelValue("blue");
381
+
return {
382
+
$type: "pub.leaflet.theme.color#rgb" as const,
383
+
r: Math.round(r),
384
+
g: Math.round(g),
385
+
b: Math.round(b),
386
+
};
387
+
}
+25
-35
components/ThemeManager/PublicationThemeProvider.tsx
+25
-35
components/ThemeManager/PublicationThemeProvider.tsx
···
16
16
accentText: "#FFFFFF",
17
17
accentBackground: "#0000FF",
18
18
};
19
-
20
-
// Default page background for standalone leaflets (matches editor default)
21
-
const StandalonePageBackground = "#FFFFFF";
22
19
function parseThemeColor(
23
20
c: PubLeafletThemeColor.Rgb | PubLeafletThemeColor.Rgba,
24
21
) {
···
29
26
}
30
27
31
28
let useColor = (
32
-
theme: PubLeafletPublication.Record["theme"] | null | undefined,
29
+
record: PubLeafletPublication.Record | null | undefined,
33
30
c: keyof typeof PubThemeDefaults,
34
31
) => {
35
32
return useMemo(() => {
36
-
let v = theme?.[c];
33
+
let v = record?.theme?.[c];
37
34
if (isColor(v)) {
38
35
return parseThemeColor(v);
39
36
} else return parseColor(PubThemeDefaults[c]);
40
-
}, [theme?.[c]]);
37
+
}, [record?.theme?.[c]]);
41
38
};
42
39
let isColor = (
43
40
c: any,
···
56
53
return (
57
54
<PublicationThemeProvider
58
55
pub_creator={pub?.identity_did || ""}
59
-
theme={(pub?.record as PubLeafletPublication.Record)?.theme}
56
+
record={pub?.record as PubLeafletPublication.Record}
60
57
>
61
58
<PublicationBackgroundProvider
62
-
theme={(pub?.record as PubLeafletPublication.Record)?.theme}
59
+
record={pub?.record as PubLeafletPublication.Record}
63
60
pub_creator={pub?.identity_did || ""}
64
61
>
65
62
{props.children}
···
69
66
}
70
67
71
68
export function PublicationBackgroundProvider(props: {
72
-
theme?: PubLeafletPublication.Record["theme"] | null;
69
+
record?: PubLeafletPublication.Record | null;
73
70
pub_creator: string;
74
71
className?: string;
75
72
children: React.ReactNode;
76
73
}) {
77
-
let backgroundImage = props.theme?.backgroundImage?.image?.ref
78
-
? blobRefToSrc(props.theme?.backgroundImage?.image?.ref, props.pub_creator)
74
+
let backgroundImage = props.record?.theme?.backgroundImage?.image?.ref
75
+
? blobRefToSrc(
76
+
props.record?.theme?.backgroundImage?.image?.ref,
77
+
props.pub_creator,
78
+
)
79
79
: null;
80
80
81
-
let backgroundImageRepeat = props.theme?.backgroundImage?.repeat;
82
-
let backgroundImageSize = props.theme?.backgroundImage?.width || 500;
81
+
let backgroundImageRepeat = props.record?.theme?.backgroundImage?.repeat;
82
+
let backgroundImageSize = props.record?.theme?.backgroundImage?.width || 500;
83
83
return (
84
84
<div
85
85
className="PubBackgroundWrapper w-full bg-bg-leaflet text-primary h-full flex flex-col bg-cover bg-center bg-no-repeat items-stretch"
···
96
96
export function PublicationThemeProvider(props: {
97
97
local?: boolean;
98
98
children: React.ReactNode;
99
-
theme?: PubLeafletPublication.Record["theme"] | null;
99
+
record?: PubLeafletPublication.Record | null;
100
100
pub_creator: string;
101
-
isStandalone?: boolean;
102
101
}) {
103
-
let colors = usePubTheme(props.theme, props.isStandalone);
102
+
let colors = usePubTheme(props.record);
104
103
return (
105
104
<BaseThemeProvider local={props.local} {...colors}>
106
105
{props.children}
···
108
107
);
109
108
}
110
109
111
-
export const usePubTheme = (
112
-
theme?: PubLeafletPublication.Record["theme"] | null,
113
-
isStandalone?: boolean,
114
-
) => {
115
-
let bgLeaflet = useColor(theme, "backgroundColor");
116
-
let bgPage = useColor(theme, "pageBackground");
117
-
// For standalone documents, use the editor default page background (#FFFFFF)
118
-
// For publications without explicit pageBackground, use bgLeaflet
119
-
if (isStandalone && !theme?.pageBackground) {
120
-
bgPage = parseColor(StandalonePageBackground);
121
-
} else if (theme && !theme.pageBackground) {
122
-
bgPage = bgLeaflet;
123
-
}
124
-
let showPageBackground = theme?.showPageBackground;
110
+
export const usePubTheme = (record?: PubLeafletPublication.Record | null) => {
111
+
let bgLeaflet = useColor(record, "backgroundColor");
112
+
let bgPage = useColor(record, "pageBackground");
113
+
bgPage = record?.theme?.pageBackground ? bgPage : bgLeaflet;
114
+
let showPageBackground = record?.theme?.showPageBackground;
125
115
126
-
let primary = useColor(theme, "primary");
116
+
let primary = useColor(record, "primary");
127
117
128
-
let accent1 = useColor(theme, "accentBackground");
129
-
let accent2 = useColor(theme, "accentText");
118
+
let accent1 = useColor(record, "accentBackground");
119
+
let accent2 = useColor(record, "accentText");
130
120
131
121
let highlight1 = useEntity(null, "theme/highlight-1")?.data.value;
132
122
let highlight2 = useColorAttribute(null, "theme/highlight-2");
···
146
136
};
147
137
148
138
export const useLocalPubTheme = (
149
-
theme: PubLeafletPublication.Record["theme"] | undefined,
139
+
record: PubLeafletPublication.Record | undefined,
150
140
showPageBackground?: boolean,
151
141
) => {
152
-
const pubTheme = usePubTheme(theme);
142
+
const pubTheme = usePubTheme(record);
153
143
const [localOverrides, setTheme] = useState<Partial<typeof pubTheme>>({});
154
144
155
145
const mergedTheme = useMemo(() => {
+2
-4
components/ThemeManager/ThemeProvider.tsx
+2
-4
components/ThemeManager/ThemeProvider.tsx
···
73
73
return (
74
74
<PublicationThemeProvider
75
75
{...props}
76
-
theme={(pub.publications?.record as PubLeafletPublication.Record)?.theme}
76
+
record={pub.publications?.record as PubLeafletPublication.Record}
77
77
pub_creator={pub.publications?.identity_did}
78
78
/>
79
79
);
···
339
339
return (
340
340
<PublicationBackgroundProvider
341
341
pub_creator={pub?.publications.identity_did || ""}
342
-
theme={
343
-
(pub.publications?.record as PubLeafletPublication.Record)?.theme
344
-
}
342
+
record={pub?.publications.record as PubLeafletPublication.Record}
345
343
>
346
344
{props.children}
347
345
</PublicationBackgroundProvider>
+2
-2
components/ThemeManager/ThemeSetter.tsx
+2
-2
components/ThemeManager/ThemeSetter.tsx
···
70
70
}, [rep, props.entityID]);
71
71
72
72
if (!permission) return null;
73
-
if (pub?.publications) return null;
73
+
if (pub) return null;
74
74
75
75
return (
76
76
<>
···
111
111
}, [rep, props.entityID]);
112
112
113
113
if (!permission) return null;
114
-
if (pub?.publications) return null;
114
+
if (pub) return null;
115
115
return (
116
116
<div className="themeSetterContent flex flex-col w-full overflow-y-scroll no-scrollbar">
117
117
<div className="themeBGLeaflet flex">
-44
components/ThemeManager/colorToLexicons.ts
-44
components/ThemeManager/colorToLexicons.ts
···
1
-
import { Color } from "react-aria-components";
2
-
3
-
export function ColorToRGBA(color: Color) {
4
-
if (!color)
5
-
return {
6
-
$type: "pub.leaflet.theme.color#rgba" as const,
7
-
r: 0,
8
-
g: 0,
9
-
b: 0,
10
-
a: 1,
11
-
};
12
-
let c = color.toFormat("rgba");
13
-
const r = c.getChannelValue("red");
14
-
const g = c.getChannelValue("green");
15
-
const b = c.getChannelValue("blue");
16
-
const a = c.getChannelValue("alpha");
17
-
return {
18
-
$type: "pub.leaflet.theme.color#rgba" as const,
19
-
r: Math.round(r),
20
-
g: Math.round(g),
21
-
b: Math.round(b),
22
-
a: Math.round(a * 100),
23
-
};
24
-
}
25
-
26
-
export function ColorToRGB(color: Color) {
27
-
if (!color)
28
-
return {
29
-
$type: "pub.leaflet.theme.color#rgb" as const,
30
-
r: 0,
31
-
g: 0,
32
-
b: 0,
33
-
};
34
-
let c = color.toFormat("rgb");
35
-
const r = c.getChannelValue("red");
36
-
const g = c.getChannelValue("green");
37
-
const b = c.getChannelValue("blue");
38
-
return {
39
-
$type: "pub.leaflet.theme.color#rgb" as const,
40
-
r: Math.round(r),
41
-
g: Math.round(g),
42
-
b: Math.round(b),
43
-
};
44
-
}
+1
-1
feeds/index.ts
+1
-1
feeds/index.ts
+1
-5
lexicons/api/lexicons.ts
+1
-5
lexicons/api/lexicons.ts
···
1408
1408
description: 'Record containing a document',
1409
1409
record: {
1410
1410
type: 'object',
1411
-
required: ['pages', 'author', 'title'],
1411
+
required: ['pages', 'author', 'title', 'publication'],
1412
1412
properties: {
1413
1413
title: {
1414
1414
type: 'string',
···
1435
1435
author: {
1436
1436
type: 'string',
1437
1437
format: 'at-identifier',
1438
-
},
1439
-
theme: {
1440
-
type: 'ref',
1441
-
ref: 'lex:pub.leaflet.publication#theme',
1442
1438
},
1443
1439
pages: {
1444
1440
type: 'array',
+1
-3
lexicons/api/types/pub/leaflet/document.ts
+1
-3
lexicons/api/types/pub/leaflet/document.ts
···
6
6
import { validate as _validate } from '../../../lexicons'
7
7
import { type $Typed, is$typed as _is$typed, type OmitKey } from '../../../util'
8
8
import type * as ComAtprotoRepoStrongRef from '../../com/atproto/repo/strongRef'
9
-
import type * as PubLeafletPublication from './publication'
10
9
import type * as PubLeafletPagesLinearDocument from './pages/linearDocument'
11
10
import type * as PubLeafletPagesCanvas from './pages/canvas'
12
11
···
20
19
postRef?: ComAtprotoRepoStrongRef.Main
21
20
description?: string
22
21
publishedAt?: string
23
-
publication?: string
22
+
publication: string
24
23
author: string
25
-
theme?: PubLeafletPublication.Theme
26
24
pages: (
27
25
| $Typed<PubLeafletPagesLinearDocument.Main>
28
26
| $Typed<PubLeafletPagesCanvas.Main>
+2
-5
lexicons/pub/leaflet/document.json
+2
-5
lexicons/pub/leaflet/document.json
···
13
13
"required": [
14
14
"pages",
15
15
"author",
16
-
"title"
16
+
"title",
17
+
"publication"
17
18
],
18
19
"properties": {
19
20
"title": {
···
41
42
"author": {
42
43
"type": "string",
43
44
"format": "at-identifier"
44
-
},
45
-
"theme": {
46
-
"type": "ref",
47
-
"ref": "pub.leaflet.publication#theme"
48
45
},
49
46
"pages": {
50
47
"type": "array",
+1
-2
lexicons/src/document.ts
+1
-2
lexicons/src/document.ts
···
14
14
description: "Record containing a document",
15
15
record: {
16
16
type: "object",
17
-
required: ["pages", "author", "title"],
17
+
required: ["pages", "author", "title", "publication"],
18
18
properties: {
19
19
title: { type: "string", maxLength: 1280, maxGraphemes: 128 },
20
20
postRef: { type: "ref", ref: "com.atproto.repo.strongRef" },
···
22
22
publishedAt: { type: "string", format: "datetime" },
23
23
publication: { type: "string", format: "at-uri" },
24
24
author: { type: "string", format: "at-identifier" },
25
-
theme: { type: "ref", ref: "pub.leaflet.publication#theme" },
26
25
pages: {
27
26
type: "array",
28
27
items: {
+1
-1
next-env.d.ts
+1
-1
next-env.d.ts
···
1
1
/// <reference types="next" />
2
2
/// <reference types="next/image-types/global" />
3
-
import "./.next/dev/types/routes.d.ts";
3
+
/// <reference path="./.next/types/routes.d.ts" />
4
4
5
5
// NOTE: This file should not be edited
6
6
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
+2
-2
next.config.js
+2
-2
next.config.js
···
21
21
},
22
22
];
23
23
},
24
-
serverExternalPackages: ["yjs", "pino"],
24
+
serverExternalPackages: ["yjs"],
25
25
pageExtensions: ["js", "jsx", "ts", "tsx", "md", "mdx"],
26
26
images: {
27
27
loader: "custom",
···
31
31
{ protocol: "https", hostname: "bdefzwcumgzjwllsnaej.supabase.co" },
32
32
],
33
33
},
34
-
reactCompiler: true,
35
34
experimental: {
35
+
reactCompiler: true,
36
36
serverActions: {
37
37
bodySizeLimit: "5mb",
38
38
},
+465
-2330
package-lock.json
+465
-2330
package-lock.json
···
21
21
"@hono/node-server": "^1.14.3",
22
22
"@mdx-js/loader": "^3.1.0",
23
23
"@mdx-js/react": "^3.1.0",
24
-
"@next/bundle-analyzer": "16.0.3",
25
-
"@next/mdx": "16.0.3",
24
+
"@next/bundle-analyzer": "^15.3.2",
25
+
"@next/mdx": "15.3.2",
26
26
"@radix-ui/react-dialog": "^1.1.15",
27
27
"@radix-ui/react-dropdown-menu": "^2.1.16",
28
28
"@radix-ui/react-popover": "^1.1.15",
···
51
51
"linkifyjs": "^4.2.0",
52
52
"luxon": "^3.7.2",
53
53
"multiformats": "^13.3.2",
54
-
"next": "16.0.3",
54
+
"next": "^15.5.3",
55
55
"pg": "^8.16.3",
56
56
"prosemirror-commands": "^1.5.2",
57
57
"prosemirror-inputrules": "^1.4.0",
···
59
59
"prosemirror-model": "^1.21.0",
60
60
"prosemirror-schema-basic": "^1.2.2",
61
61
"prosemirror-state": "^1.4.3",
62
-
"react": "19.2.0",
62
+
"react": "^19.1.1",
63
63
"react-aria-components": "^1.8.0",
64
64
"react-day-picker": "^9.3.0",
65
-
"react-dom": "19.2.0",
65
+
"react-dom": "^19.1.1",
66
66
"react-use-measure": "^2.1.1",
67
67
"redlock": "^5.0.0-beta.2",
68
68
"rehype-parse": "^9.0.0",
···
92
92
"@types/katex": "^0.16.7",
93
93
"@types/luxon": "^3.7.1",
94
94
"@types/node": "^22.15.17",
95
-
"@types/react": "19.2.6",
96
-
"@types/react-dom": "19.2.3",
95
+
"@types/react": "19.1.3",
96
+
"@types/react-dom": "19.1.3",
97
97
"@types/uuid": "^10.0.0",
98
98
"drizzle-kit": "^0.21.2",
99
99
"esbuild": "^0.25.4",
100
-
"eslint": "^9.39.1",
101
-
"eslint-config-next": "16.0.3",
100
+
"eslint": "8.57.0",
101
+
"eslint-config-next": "^15.5.3",
102
102
"postcss": "^8.4.38",
103
103
"prettier": "3.2.5",
104
104
"supabase": "^1.187.3",
···
567
567
"node": ">=18.7.0"
568
568
}
569
569
},
570
-
"node_modules/@babel/code-frame": {
571
-
"version": "7.27.1",
572
-
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
573
-
"integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
574
-
"dev": true,
575
-
"dependencies": {
576
-
"@babel/helper-validator-identifier": "^7.27.1",
577
-
"js-tokens": "^4.0.0",
578
-
"picocolors": "^1.1.1"
579
-
},
580
-
"engines": {
581
-
"node": ">=6.9.0"
582
-
}
583
-
},
584
-
"node_modules/@babel/compat-data": {
585
-
"version": "7.28.5",
586
-
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz",
587
-
"integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==",
588
-
"dev": true,
589
-
"engines": {
590
-
"node": ">=6.9.0"
591
-
}
592
-
},
593
-
"node_modules/@babel/core": {
594
-
"version": "7.28.5",
595
-
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz",
596
-
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
597
-
"dev": true,
598
-
"dependencies": {
599
-
"@babel/code-frame": "^7.27.1",
600
-
"@babel/generator": "^7.28.5",
601
-
"@babel/helper-compilation-targets": "^7.27.2",
602
-
"@babel/helper-module-transforms": "^7.28.3",
603
-
"@babel/helpers": "^7.28.4",
604
-
"@babel/parser": "^7.28.5",
605
-
"@babel/template": "^7.27.2",
606
-
"@babel/traverse": "^7.28.5",
607
-
"@babel/types": "^7.28.5",
608
-
"@jridgewell/remapping": "^2.3.5",
609
-
"convert-source-map": "^2.0.0",
610
-
"debug": "^4.1.0",
611
-
"gensync": "^1.0.0-beta.2",
612
-
"json5": "^2.2.3",
613
-
"semver": "^6.3.1"
614
-
},
615
-
"engines": {
616
-
"node": ">=6.9.0"
617
-
},
618
-
"funding": {
619
-
"type": "opencollective",
620
-
"url": "https://opencollective.com/babel"
621
-
}
622
-
},
623
-
"node_modules/@babel/core/node_modules/json5": {
624
-
"version": "2.2.3",
625
-
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
626
-
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
627
-
"dev": true,
628
-
"bin": {
629
-
"json5": "lib/cli.js"
630
-
},
631
-
"engines": {
632
-
"node": ">=6"
633
-
}
634
-
},
635
-
"node_modules/@babel/core/node_modules/semver": {
636
-
"version": "6.3.1",
637
-
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
638
-
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
639
-
"dev": true,
640
-
"bin": {
641
-
"semver": "bin/semver.js"
642
-
}
643
-
},
644
-
"node_modules/@babel/generator": {
645
-
"version": "7.28.5",
646
-
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz",
647
-
"integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==",
648
-
"dev": true,
649
-
"dependencies": {
650
-
"@babel/parser": "^7.28.5",
651
-
"@babel/types": "^7.28.5",
652
-
"@jridgewell/gen-mapping": "^0.3.12",
653
-
"@jridgewell/trace-mapping": "^0.3.28",
654
-
"jsesc": "^3.0.2"
655
-
},
656
-
"engines": {
657
-
"node": ">=6.9.0"
658
-
}
659
-
},
660
-
"node_modules/@babel/helper-compilation-targets": {
661
-
"version": "7.27.2",
662
-
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
663
-
"integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
664
-
"dev": true,
665
-
"dependencies": {
666
-
"@babel/compat-data": "^7.27.2",
667
-
"@babel/helper-validator-option": "^7.27.1",
668
-
"browserslist": "^4.24.0",
669
-
"lru-cache": "^5.1.1",
670
-
"semver": "^6.3.1"
671
-
},
672
-
"engines": {
673
-
"node": ">=6.9.0"
674
-
}
675
-
},
676
-
"node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": {
677
-
"version": "5.1.1",
678
-
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
679
-
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
680
-
"dev": true,
681
-
"dependencies": {
682
-
"yallist": "^3.0.2"
683
-
}
684
-
},
685
-
"node_modules/@babel/helper-compilation-targets/node_modules/semver": {
686
-
"version": "6.3.1",
687
-
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
688
-
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
689
-
"dev": true,
690
-
"bin": {
691
-
"semver": "bin/semver.js"
692
-
}
693
-
},
694
-
"node_modules/@babel/helper-compilation-targets/node_modules/yallist": {
695
-
"version": "3.1.1",
696
-
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
697
-
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
698
-
"dev": true
699
-
},
700
-
"node_modules/@babel/helper-globals": {
701
-
"version": "7.28.0",
702
-
"resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
703
-
"integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
704
-
"dev": true,
705
-
"engines": {
706
-
"node": ">=6.9.0"
707
-
}
708
-
},
709
-
"node_modules/@babel/helper-module-imports": {
710
-
"version": "7.27.1",
711
-
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
712
-
"integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
713
-
"dev": true,
714
-
"dependencies": {
715
-
"@babel/traverse": "^7.27.1",
716
-
"@babel/types": "^7.27.1"
717
-
},
718
-
"engines": {
719
-
"node": ">=6.9.0"
720
-
}
721
-
},
722
-
"node_modules/@babel/helper-module-transforms": {
723
-
"version": "7.28.3",
724
-
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz",
725
-
"integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==",
726
-
"dev": true,
727
-
"dependencies": {
728
-
"@babel/helper-module-imports": "^7.27.1",
729
-
"@babel/helper-validator-identifier": "^7.27.1",
730
-
"@babel/traverse": "^7.28.3"
731
-
},
732
-
"engines": {
733
-
"node": ">=6.9.0"
734
-
},
735
-
"peerDependencies": {
736
-
"@babel/core": "^7.0.0"
737
-
}
738
-
},
739
570
"node_modules/@babel/helper-string-parser": {
740
571
"version": "7.27.1",
741
572
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
···
746
577
}
747
578
},
748
579
"node_modules/@babel/helper-validator-identifier": {
749
-
"version": "7.28.5",
750
-
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
751
-
"integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
752
-
"engines": {
753
-
"node": ">=6.9.0"
754
-
}
755
-
},
756
-
"node_modules/@babel/helper-validator-option": {
757
580
"version": "7.27.1",
758
-
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
759
-
"integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
760
-
"dev": true,
761
-
"engines": {
762
-
"node": ">=6.9.0"
763
-
}
764
-
},
765
-
"node_modules/@babel/helpers": {
766
-
"version": "7.28.4",
767
-
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz",
768
-
"integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==",
769
-
"dev": true,
770
-
"dependencies": {
771
-
"@babel/template": "^7.27.2",
772
-
"@babel/types": "^7.28.4"
773
-
},
774
-
"engines": {
775
-
"node": ">=6.9.0"
776
-
}
777
-
},
778
-
"node_modules/@babel/parser": {
779
-
"version": "7.28.5",
780
-
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz",
781
-
"integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
782
-
"dev": true,
783
-
"dependencies": {
784
-
"@babel/types": "^7.28.5"
785
-
},
786
-
"bin": {
787
-
"parser": "bin/babel-parser.js"
788
-
},
789
-
"engines": {
790
-
"node": ">=6.0.0"
791
-
}
792
-
},
793
-
"node_modules/@babel/template": {
794
-
"version": "7.27.2",
795
-
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
796
-
"integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
797
-
"dev": true,
798
-
"dependencies": {
799
-
"@babel/code-frame": "^7.27.1",
800
-
"@babel/parser": "^7.27.2",
801
-
"@babel/types": "^7.27.1"
802
-
},
803
-
"engines": {
804
-
"node": ">=6.9.0"
805
-
}
806
-
},
807
-
"node_modules/@babel/traverse": {
808
-
"version": "7.28.5",
809
-
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz",
810
-
"integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==",
811
-
"dev": true,
812
-
"dependencies": {
813
-
"@babel/code-frame": "^7.27.1",
814
-
"@babel/generator": "^7.28.5",
815
-
"@babel/helper-globals": "^7.28.0",
816
-
"@babel/parser": "^7.28.5",
817
-
"@babel/template": "^7.27.2",
818
-
"@babel/types": "^7.28.5",
819
-
"debug": "^4.3.1"
820
-
},
581
+
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
582
+
"integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
583
+
"license": "MIT",
821
584
"engines": {
822
585
"node": ">=6.9.0"
823
586
}
824
587
},
825
588
"node_modules/@babel/types": {
826
-
"version": "7.28.5",
827
-
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz",
828
-
"integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==",
589
+
"version": "7.27.1",
590
+
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz",
591
+
"integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==",
592
+
"license": "MIT",
829
593
"dependencies": {
830
594
"@babel/helper-string-parser": "^7.27.1",
831
-
"@babel/helper-validator-identifier": "^7.28.5"
595
+
"@babel/helper-validator-identifier": "^7.27.1"
832
596
},
833
597
"engines": {
834
598
"node": ">=6.9.0"
···
950
714
"source-map-support": "^0.5.21"
951
715
}
952
716
},
953
-
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-arm": {
954
-
"version": "0.18.20",
955
-
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz",
956
-
"integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==",
957
-
"cpu": [
958
-
"arm"
959
-
],
960
-
"dev": true,
961
-
"optional": true,
962
-
"os": [
963
-
"android"
964
-
],
965
-
"engines": {
966
-
"node": ">=12"
967
-
}
968
-
},
969
-
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-arm64": {
970
-
"version": "0.18.20",
971
-
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz",
972
-
"integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==",
973
-
"cpu": [
974
-
"arm64"
975
-
],
976
-
"dev": true,
977
-
"optional": true,
978
-
"os": [
979
-
"android"
980
-
],
981
-
"engines": {
982
-
"node": ">=12"
983
-
}
984
-
},
985
-
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-x64": {
986
-
"version": "0.18.20",
987
-
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz",
988
-
"integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==",
989
-
"cpu": [
990
-
"x64"
991
-
],
992
-
"dev": true,
993
-
"optional": true,
994
-
"os": [
995
-
"android"
996
-
],
997
-
"engines": {
998
-
"node": ">=12"
999
-
}
1000
-
},
1001
-
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/darwin-arm64": {
1002
-
"version": "0.18.20",
1003
-
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz",
1004
-
"integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==",
1005
-
"cpu": [
1006
-
"arm64"
1007
-
],
1008
-
"dev": true,
1009
-
"optional": true,
1010
-
"os": [
1011
-
"darwin"
1012
-
],
1013
-
"engines": {
1014
-
"node": ">=12"
1015
-
}
1016
-
},
1017
-
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/darwin-x64": {
1018
-
"version": "0.18.20",
1019
-
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz",
1020
-
"integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==",
1021
-
"cpu": [
1022
-
"x64"
1023
-
],
1024
-
"dev": true,
1025
-
"optional": true,
1026
-
"os": [
1027
-
"darwin"
1028
-
],
1029
-
"engines": {
1030
-
"node": ">=12"
1031
-
}
1032
-
},
1033
-
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/freebsd-arm64": {
1034
-
"version": "0.18.20",
1035
-
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz",
1036
-
"integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==",
1037
-
"cpu": [
1038
-
"arm64"
1039
-
],
1040
-
"dev": true,
1041
-
"optional": true,
1042
-
"os": [
1043
-
"freebsd"
1044
-
],
1045
-
"engines": {
1046
-
"node": ">=12"
1047
-
}
1048
-
},
1049
-
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/freebsd-x64": {
1050
-
"version": "0.18.20",
1051
-
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz",
1052
-
"integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==",
1053
-
"cpu": [
1054
-
"x64"
1055
-
],
1056
-
"dev": true,
1057
-
"optional": true,
1058
-
"os": [
1059
-
"freebsd"
1060
-
],
1061
-
"engines": {
1062
-
"node": ">=12"
1063
-
}
1064
-
},
1065
-
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-arm": {
1066
-
"version": "0.18.20",
1067
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz",
1068
-
"integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==",
1069
-
"cpu": [
1070
-
"arm"
1071
-
],
1072
-
"dev": true,
1073
-
"optional": true,
1074
-
"os": [
1075
-
"linux"
1076
-
],
1077
-
"engines": {
1078
-
"node": ">=12"
1079
-
}
1080
-
},
1081
-
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-arm64": {
1082
-
"version": "0.18.20",
1083
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz",
1084
-
"integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==",
1085
-
"cpu": [
1086
-
"arm64"
1087
-
],
1088
-
"dev": true,
1089
-
"optional": true,
1090
-
"os": [
1091
-
"linux"
1092
-
],
1093
-
"engines": {
1094
-
"node": ">=12"
1095
-
}
1096
-
},
1097
-
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-ia32": {
1098
-
"version": "0.18.20",
1099
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz",
1100
-
"integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==",
1101
-
"cpu": [
1102
-
"ia32"
1103
-
],
1104
-
"dev": true,
1105
-
"optional": true,
1106
-
"os": [
1107
-
"linux"
1108
-
],
1109
-
"engines": {
1110
-
"node": ">=12"
1111
-
}
1112
-
},
1113
-
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-loong64": {
1114
-
"version": "0.18.20",
1115
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz",
1116
-
"integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==",
1117
-
"cpu": [
1118
-
"loong64"
1119
-
],
1120
-
"dev": true,
1121
-
"optional": true,
1122
-
"os": [
1123
-
"linux"
1124
-
],
1125
-
"engines": {
1126
-
"node": ">=12"
1127
-
}
1128
-
},
1129
-
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-mips64el": {
1130
-
"version": "0.18.20",
1131
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz",
1132
-
"integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==",
1133
-
"cpu": [
1134
-
"mips64el"
1135
-
],
1136
-
"dev": true,
1137
-
"optional": true,
1138
-
"os": [
1139
-
"linux"
1140
-
],
1141
-
"engines": {
1142
-
"node": ">=12"
1143
-
}
1144
-
},
1145
-
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-ppc64": {
1146
-
"version": "0.18.20",
1147
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz",
1148
-
"integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==",
1149
-
"cpu": [
1150
-
"ppc64"
1151
-
],
1152
-
"dev": true,
1153
-
"optional": true,
1154
-
"os": [
1155
-
"linux"
1156
-
],
1157
-
"engines": {
1158
-
"node": ">=12"
1159
-
}
1160
-
},
1161
-
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-riscv64": {
1162
-
"version": "0.18.20",
1163
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz",
1164
-
"integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==",
1165
-
"cpu": [
1166
-
"riscv64"
1167
-
],
1168
-
"dev": true,
1169
-
"optional": true,
1170
-
"os": [
1171
-
"linux"
1172
-
],
1173
-
"engines": {
1174
-
"node": ">=12"
1175
-
}
1176
-
},
1177
-
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-s390x": {
1178
-
"version": "0.18.20",
1179
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz",
1180
-
"integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==",
1181
-
"cpu": [
1182
-
"s390x"
1183
-
],
1184
-
"dev": true,
1185
-
"optional": true,
1186
-
"os": [
1187
-
"linux"
1188
-
],
1189
-
"engines": {
1190
-
"node": ">=12"
1191
-
}
1192
-
},
1193
717
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-x64": {
1194
718
"version": "0.18.20",
1195
719
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz",
···
1206
730
"node": ">=12"
1207
731
}
1208
732
},
1209
-
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/netbsd-x64": {
1210
-
"version": "0.18.20",
1211
-
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz",
1212
-
"integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==",
1213
-
"cpu": [
1214
-
"x64"
1215
-
],
1216
-
"dev": true,
1217
-
"optional": true,
1218
-
"os": [
1219
-
"netbsd"
1220
-
],
1221
-
"engines": {
1222
-
"node": ">=12"
1223
-
}
1224
-
},
1225
-
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/openbsd-x64": {
1226
-
"version": "0.18.20",
1227
-
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz",
1228
-
"integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==",
1229
-
"cpu": [
1230
-
"x64"
1231
-
],
1232
-
"dev": true,
1233
-
"optional": true,
1234
-
"os": [
1235
-
"openbsd"
1236
-
],
1237
-
"engines": {
1238
-
"node": ">=12"
1239
-
}
1240
-
},
1241
-
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/sunos-x64": {
1242
-
"version": "0.18.20",
1243
-
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz",
1244
-
"integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==",
1245
-
"cpu": [
1246
-
"x64"
1247
-
],
1248
-
"dev": true,
1249
-
"optional": true,
1250
-
"os": [
1251
-
"sunos"
1252
-
],
1253
-
"engines": {
1254
-
"node": ">=12"
1255
-
}
1256
-
},
1257
-
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-arm64": {
1258
-
"version": "0.18.20",
1259
-
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz",
1260
-
"integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==",
1261
-
"cpu": [
1262
-
"arm64"
1263
-
],
1264
-
"dev": true,
1265
-
"optional": true,
1266
-
"os": [
1267
-
"win32"
1268
-
],
1269
-
"engines": {
1270
-
"node": ">=12"
1271
-
}
1272
-
},
1273
-
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-ia32": {
1274
-
"version": "0.18.20",
1275
-
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz",
1276
-
"integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==",
1277
-
"cpu": [
1278
-
"ia32"
1279
-
],
1280
-
"dev": true,
1281
-
"optional": true,
1282
-
"os": [
1283
-
"win32"
1284
-
],
1285
-
"engines": {
1286
-
"node": ">=12"
1287
-
}
1288
-
},
1289
-
"node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-x64": {
1290
-
"version": "0.18.20",
1291
-
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz",
1292
-
"integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==",
1293
-
"cpu": [
1294
-
"x64"
1295
-
],
1296
-
"dev": true,
1297
-
"optional": true,
1298
-
"os": [
1299
-
"win32"
1300
-
],
1301
-
"engines": {
1302
-
"node": ">=12"
1303
-
}
1304
-
},
1305
733
"node_modules/@esbuild-kit/core-utils/node_modules/esbuild": {
1306
734
"version": "0.18.20",
1307
735
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz",
···
1371
799
"esbuild": "*"
1372
800
}
1373
801
},
1374
-
"node_modules/@esbuild/aix-ppc64": {
1375
-
"version": "0.25.4",
1376
-
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz",
1377
-
"integrity": "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==",
1378
-
"cpu": [
1379
-
"ppc64"
1380
-
],
1381
-
"dev": true,
1382
-
"optional": true,
1383
-
"os": [
1384
-
"aix"
1385
-
],
1386
-
"engines": {
1387
-
"node": ">=18"
1388
-
}
1389
-
},
1390
-
"node_modules/@esbuild/android-arm": {
1391
-
"version": "0.25.4",
1392
-
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz",
1393
-
"integrity": "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==",
1394
-
"cpu": [
1395
-
"arm"
1396
-
],
1397
-
"dev": true,
1398
-
"optional": true,
1399
-
"os": [
1400
-
"android"
1401
-
],
1402
-
"engines": {
1403
-
"node": ">=18"
1404
-
}
1405
-
},
1406
-
"node_modules/@esbuild/android-arm64": {
1407
-
"version": "0.25.4",
1408
-
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz",
1409
-
"integrity": "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==",
1410
-
"cpu": [
1411
-
"arm64"
1412
-
],
1413
-
"dev": true,
1414
-
"optional": true,
1415
-
"os": [
1416
-
"android"
1417
-
],
1418
-
"engines": {
1419
-
"node": ">=18"
1420
-
}
1421
-
},
1422
-
"node_modules/@esbuild/android-x64": {
1423
-
"version": "0.25.4",
1424
-
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz",
1425
-
"integrity": "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==",
1426
-
"cpu": [
1427
-
"x64"
1428
-
],
1429
-
"dev": true,
1430
-
"optional": true,
1431
-
"os": [
1432
-
"android"
1433
-
],
1434
-
"engines": {
1435
-
"node": ">=18"
1436
-
}
1437
-
},
1438
-
"node_modules/@esbuild/darwin-x64": {
1439
-
"version": "0.25.4",
1440
-
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz",
1441
-
"integrity": "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==",
1442
-
"cpu": [
1443
-
"x64"
1444
-
],
1445
-
"dev": true,
1446
-
"optional": true,
1447
-
"os": [
1448
-
"darwin"
1449
-
],
1450
-
"engines": {
1451
-
"node": ">=18"
1452
-
}
1453
-
},
1454
-
"node_modules/@esbuild/freebsd-arm64": {
1455
-
"version": "0.25.4",
1456
-
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz",
1457
-
"integrity": "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==",
1458
-
"cpu": [
1459
-
"arm64"
1460
-
],
1461
-
"dev": true,
1462
-
"optional": true,
1463
-
"os": [
1464
-
"freebsd"
1465
-
],
1466
-
"engines": {
1467
-
"node": ">=18"
1468
-
}
1469
-
},
1470
-
"node_modules/@esbuild/freebsd-x64": {
1471
-
"version": "0.25.4",
1472
-
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz",
1473
-
"integrity": "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==",
1474
-
"cpu": [
1475
-
"x64"
1476
-
],
1477
-
"dev": true,
1478
-
"optional": true,
1479
-
"os": [
1480
-
"freebsd"
1481
-
],
1482
-
"engines": {
1483
-
"node": ">=18"
1484
-
}
1485
-
},
1486
-
"node_modules/@esbuild/linux-arm": {
1487
-
"version": "0.25.4",
1488
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz",
1489
-
"integrity": "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==",
1490
-
"cpu": [
1491
-
"arm"
1492
-
],
1493
-
"dev": true,
1494
-
"optional": true,
1495
-
"os": [
1496
-
"linux"
1497
-
],
1498
-
"engines": {
1499
-
"node": ">=18"
1500
-
}
1501
-
},
1502
-
"node_modules/@esbuild/linux-arm64": {
1503
-
"version": "0.25.4",
1504
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz",
1505
-
"integrity": "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==",
1506
-
"cpu": [
1507
-
"arm64"
1508
-
],
1509
-
"dev": true,
1510
-
"optional": true,
1511
-
"os": [
1512
-
"linux"
1513
-
],
1514
-
"engines": {
1515
-
"node": ">=18"
1516
-
}
1517
-
},
1518
-
"node_modules/@esbuild/linux-ia32": {
1519
-
"version": "0.25.4",
1520
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz",
1521
-
"integrity": "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==",
1522
-
"cpu": [
1523
-
"ia32"
1524
-
],
1525
-
"dev": true,
1526
-
"optional": true,
1527
-
"os": [
1528
-
"linux"
1529
-
],
1530
-
"engines": {
1531
-
"node": ">=18"
1532
-
}
1533
-
},
1534
-
"node_modules/@esbuild/linux-loong64": {
1535
-
"version": "0.25.4",
1536
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz",
1537
-
"integrity": "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==",
1538
-
"cpu": [
1539
-
"loong64"
1540
-
],
1541
-
"dev": true,
1542
-
"optional": true,
1543
-
"os": [
1544
-
"linux"
1545
-
],
1546
-
"engines": {
1547
-
"node": ">=18"
1548
-
}
1549
-
},
1550
-
"node_modules/@esbuild/linux-mips64el": {
1551
-
"version": "0.25.4",
1552
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz",
1553
-
"integrity": "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==",
1554
-
"cpu": [
1555
-
"mips64el"
1556
-
],
1557
-
"dev": true,
1558
-
"optional": true,
1559
-
"os": [
1560
-
"linux"
1561
-
],
1562
-
"engines": {
1563
-
"node": ">=18"
1564
-
}
1565
-
},
1566
-
"node_modules/@esbuild/linux-ppc64": {
1567
-
"version": "0.25.4",
1568
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz",
1569
-
"integrity": "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==",
1570
-
"cpu": [
1571
-
"ppc64"
1572
-
],
1573
-
"dev": true,
1574
-
"optional": true,
1575
-
"os": [
1576
-
"linux"
1577
-
],
1578
-
"engines": {
1579
-
"node": ">=18"
1580
-
}
1581
-
},
1582
-
"node_modules/@esbuild/linux-riscv64": {
1583
-
"version": "0.25.4",
1584
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz",
1585
-
"integrity": "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==",
1586
-
"cpu": [
1587
-
"riscv64"
1588
-
],
1589
-
"dev": true,
1590
-
"optional": true,
1591
-
"os": [
1592
-
"linux"
1593
-
],
1594
-
"engines": {
1595
-
"node": ">=18"
1596
-
}
1597
-
},
1598
-
"node_modules/@esbuild/linux-s390x": {
1599
-
"version": "0.25.4",
1600
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz",
1601
-
"integrity": "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==",
1602
-
"cpu": [
1603
-
"s390x"
1604
-
],
1605
-
"dev": true,
1606
-
"optional": true,
1607
-
"os": [
1608
-
"linux"
1609
-
],
1610
-
"engines": {
1611
-
"node": ">=18"
1612
-
}
1613
-
},
1614
802
"node_modules/@esbuild/linux-x64": {
1615
803
"version": "0.25.4",
1616
804
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz",
···
1628
816
"node": ">=18"
1629
817
}
1630
818
},
1631
-
"node_modules/@esbuild/netbsd-arm64": {
1632
-
"version": "0.25.4",
1633
-
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz",
1634
-
"integrity": "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==",
1635
-
"cpu": [
1636
-
"arm64"
1637
-
],
1638
-
"dev": true,
1639
-
"optional": true,
1640
-
"os": [
1641
-
"netbsd"
1642
-
],
1643
-
"engines": {
1644
-
"node": ">=18"
1645
-
}
1646
-
},
1647
-
"node_modules/@esbuild/netbsd-x64": {
1648
-
"version": "0.25.4",
1649
-
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz",
1650
-
"integrity": "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==",
1651
-
"cpu": [
1652
-
"x64"
1653
-
],
1654
-
"dev": true,
1655
-
"optional": true,
1656
-
"os": [
1657
-
"netbsd"
1658
-
],
1659
-
"engines": {
1660
-
"node": ">=18"
1661
-
}
1662
-
},
1663
-
"node_modules/@esbuild/openbsd-arm64": {
1664
-
"version": "0.25.4",
1665
-
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz",
1666
-
"integrity": "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==",
1667
-
"cpu": [
1668
-
"arm64"
1669
-
],
1670
-
"dev": true,
1671
-
"optional": true,
1672
-
"os": [
1673
-
"openbsd"
1674
-
],
1675
-
"engines": {
1676
-
"node": ">=18"
1677
-
}
1678
-
},
1679
-
"node_modules/@esbuild/openbsd-x64": {
1680
-
"version": "0.25.4",
1681
-
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz",
1682
-
"integrity": "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==",
1683
-
"cpu": [
1684
-
"x64"
1685
-
],
1686
-
"dev": true,
1687
-
"optional": true,
1688
-
"os": [
1689
-
"openbsd"
1690
-
],
1691
-
"engines": {
1692
-
"node": ">=18"
1693
-
}
1694
-
},
1695
-
"node_modules/@esbuild/sunos-x64": {
1696
-
"version": "0.25.4",
1697
-
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz",
1698
-
"integrity": "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==",
1699
-
"cpu": [
1700
-
"x64"
1701
-
],
1702
-
"dev": true,
1703
-
"optional": true,
1704
-
"os": [
1705
-
"sunos"
1706
-
],
1707
-
"engines": {
1708
-
"node": ">=18"
1709
-
}
1710
-
},
1711
-
"node_modules/@esbuild/win32-arm64": {
1712
-
"version": "0.25.4",
1713
-
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz",
1714
-
"integrity": "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==",
1715
-
"cpu": [
1716
-
"arm64"
1717
-
],
1718
-
"dev": true,
1719
-
"optional": true,
1720
-
"os": [
1721
-
"win32"
1722
-
],
1723
-
"engines": {
1724
-
"node": ">=18"
1725
-
}
1726
-
},
1727
-
"node_modules/@esbuild/win32-ia32": {
1728
-
"version": "0.25.4",
1729
-
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz",
1730
-
"integrity": "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==",
1731
-
"cpu": [
1732
-
"ia32"
1733
-
],
1734
-
"dev": true,
1735
-
"optional": true,
1736
-
"os": [
1737
-
"win32"
1738
-
],
1739
-
"engines": {
1740
-
"node": ">=18"
1741
-
}
1742
-
},
1743
-
"node_modules/@esbuild/win32-x64": {
1744
-
"version": "0.25.4",
1745
-
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz",
1746
-
"integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==",
1747
-
"cpu": [
1748
-
"x64"
1749
-
],
1750
-
"dev": true,
1751
-
"optional": true,
1752
-
"os": [
1753
-
"win32"
1754
-
],
1755
-
"engines": {
1756
-
"node": ">=18"
1757
-
}
1758
-
},
1759
819
"node_modules/@eslint-community/eslint-utils": {
1760
-
"version": "4.9.0",
1761
-
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz",
1762
-
"integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==",
820
+
"version": "4.7.0",
821
+
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz",
822
+
"integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==",
1763
823
"dev": true,
824
+
"license": "MIT",
1764
825
"dependencies": {
1765
826
"eslint-visitor-keys": "^3.4.3"
1766
827
},
···
1774
835
"eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
1775
836
}
1776
837
},
1777
-
"node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
1778
-
"version": "3.4.3",
1779
-
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
1780
-
"integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
1781
-
"dev": true,
1782
-
"engines": {
1783
-
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
1784
-
},
1785
-
"funding": {
1786
-
"url": "https://opencollective.com/eslint"
1787
-
}
1788
-
},
1789
838
"node_modules/@eslint-community/regexpp": {
1790
-
"version": "4.12.2",
1791
-
"resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
1792
-
"integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
839
+
"version": "4.10.0",
840
+
"resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz",
841
+
"integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==",
1793
842
"dev": true,
1794
843
"engines": {
1795
844
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
1796
845
}
1797
846
},
1798
-
"node_modules/@eslint/config-array": {
1799
-
"version": "0.21.1",
1800
-
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz",
1801
-
"integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==",
1802
-
"dev": true,
1803
-
"dependencies": {
1804
-
"@eslint/object-schema": "^2.1.7",
1805
-
"debug": "^4.3.1",
1806
-
"minimatch": "^3.1.2"
1807
-
},
1808
-
"engines": {
1809
-
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1810
-
}
1811
-
},
1812
-
"node_modules/@eslint/config-helpers": {
1813
-
"version": "0.4.2",
1814
-
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
1815
-
"integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
1816
-
"dev": true,
1817
-
"dependencies": {
1818
-
"@eslint/core": "^0.17.0"
1819
-
},
1820
-
"engines": {
1821
-
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1822
-
}
1823
-
},
1824
-
"node_modules/@eslint/core": {
1825
-
"version": "0.17.0",
1826
-
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
1827
-
"integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
1828
-
"dev": true,
1829
-
"dependencies": {
1830
-
"@types/json-schema": "^7.0.15"
1831
-
},
1832
-
"engines": {
1833
-
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1834
-
}
1835
-
},
1836
847
"node_modules/@eslint/eslintrc": {
1837
-
"version": "3.3.1",
1838
-
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz",
1839
-
"integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==",
848
+
"version": "2.1.4",
849
+
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
850
+
"integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
1840
851
"dev": true,
1841
852
"dependencies": {
1842
853
"ajv": "^6.12.4",
1843
854
"debug": "^4.3.2",
1844
-
"espree": "^10.0.1",
1845
-
"globals": "^14.0.0",
855
+
"espree": "^9.6.0",
856
+
"globals": "^13.19.0",
1846
857
"ignore": "^5.2.0",
1847
858
"import-fresh": "^3.2.1",
1848
859
"js-yaml": "^4.1.0",
···
1850
861
"strip-json-comments": "^3.1.1"
1851
862
},
1852
863
"engines": {
1853
-
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
864
+
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
1854
865
},
1855
866
"funding": {
1856
867
"url": "https://opencollective.com/eslint"
1857
868
}
1858
869
},
1859
870
"node_modules/@eslint/js": {
1860
-
"version": "9.39.1",
1861
-
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz",
1862
-
"integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==",
871
+
"version": "8.57.0",
872
+
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz",
873
+
"integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==",
1863
874
"dev": true,
1864
875
"engines": {
1865
-
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1866
-
},
1867
-
"funding": {
1868
-
"url": "https://eslint.org/donate"
1869
-
}
1870
-
},
1871
-
"node_modules/@eslint/object-schema": {
1872
-
"version": "2.1.7",
1873
-
"resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
1874
-
"integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
1875
-
"dev": true,
1876
-
"engines": {
1877
-
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1878
-
}
1879
-
},
1880
-
"node_modules/@eslint/plugin-kit": {
1881
-
"version": "0.4.1",
1882
-
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
1883
-
"integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
1884
-
"dev": true,
1885
-
"dependencies": {
1886
-
"@eslint/core": "^0.17.0",
1887
-
"levn": "^0.4.1"
1888
-
},
1889
-
"engines": {
1890
-
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
876
+
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
1891
877
}
1892
878
},
1893
879
"node_modules/@fastify/busboy": {
···
2031
1017
"hono": "^4"
2032
1018
}
2033
1019
},
2034
-
"node_modules/@humanfs/core": {
2035
-
"version": "0.19.1",
2036
-
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
2037
-
"integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
2038
-
"dev": true,
2039
-
"engines": {
2040
-
"node": ">=18.18.0"
2041
-
}
2042
-
},
2043
-
"node_modules/@humanfs/node": {
2044
-
"version": "0.16.7",
2045
-
"resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
2046
-
"integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
1020
+
"node_modules/@humanwhocodes/config-array": {
1021
+
"version": "0.11.14",
1022
+
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
1023
+
"integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==",
2047
1024
"dev": true,
2048
1025
"dependencies": {
2049
-
"@humanfs/core": "^0.19.1",
2050
-
"@humanwhocodes/retry": "^0.4.0"
1026
+
"@humanwhocodes/object-schema": "^2.0.2",
1027
+
"debug": "^4.3.1",
1028
+
"minimatch": "^3.0.5"
2051
1029
},
2052
1030
"engines": {
2053
-
"node": ">=18.18.0"
1031
+
"node": ">=10.10.0"
2054
1032
}
2055
1033
},
2056
1034
"node_modules/@humanwhocodes/module-importer": {
···
2066
1044
"url": "https://github.com/sponsors/nzakas"
2067
1045
}
2068
1046
},
2069
-
"node_modules/@humanwhocodes/retry": {
2070
-
"version": "0.4.3",
2071
-
"resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
2072
-
"integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
2073
-
"dev": true,
2074
-
"engines": {
2075
-
"node": ">=18.18"
2076
-
},
2077
-
"funding": {
2078
-
"type": "github",
2079
-
"url": "https://github.com/sponsors/nzakas"
2080
-
}
1047
+
"node_modules/@humanwhocodes/object-schema": {
1048
+
"version": "2.0.3",
1049
+
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
1050
+
"integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
1051
+
"dev": true
2081
1052
},
2082
1053
"node_modules/@img/colour": {
2083
1054
"version": "1.0.0",
···
2726
1697
}
2727
1698
},
2728
1699
"node_modules/@next/bundle-analyzer": {
2729
-
"version": "16.0.3",
2730
-
"resolved": "https://registry.npmjs.org/@next/bundle-analyzer/-/bundle-analyzer-16.0.3.tgz",
2731
-
"integrity": "sha512-6Xo8f8/ZXtASfTPa6TH1aUn+xDg9Pkyl1YHVxu+89cVdLH7MnYjxv3rPOfEJ9BwCZCU2q4Flyw5MwltfD2pGbA==",
1700
+
"version": "15.3.2",
1701
+
"resolved": "https://registry.npmjs.org/@next/bundle-analyzer/-/bundle-analyzer-15.3.2.tgz",
1702
+
"integrity": "sha512-zY5O1PNKNxWEjaFX8gKzm77z2oL0cnj+m5aiqNBgay9LPLCDO13Cf+FJONeNq/nJjeXptwHFT9EMmTecF9U4Iw==",
1703
+
"license": "MIT",
2732
1704
"dependencies": {
2733
1705
"webpack-bundle-analyzer": "4.10.1"
2734
1706
}
2735
1707
},
2736
1708
"node_modules/@next/env": {
2737
-
"version": "16.0.3",
2738
-
"resolved": "https://registry.npmjs.org/@next/env/-/env-16.0.3.tgz",
2739
-
"integrity": "sha512-IqgtY5Vwsm14mm/nmQaRMmywCU+yyMIYfk3/MHZ2ZTJvwVbBn3usZnjMi1GacrMVzVcAxJShTCpZlPs26EdEjQ=="
1709
+
"version": "15.5.3",
1710
+
"resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.3.tgz",
1711
+
"integrity": "sha512-RSEDTRqyihYXygx/OJXwvVupfr9m04+0vH8vyy0HfZ7keRto6VX9BbEk0J2PUk0VGy6YhklJUSrgForov5F9pw==",
1712
+
"license": "MIT"
2740
1713
},
2741
1714
"node_modules/@next/eslint-plugin-next": {
2742
-
"version": "16.0.3",
2743
-
"resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.0.3.tgz",
2744
-
"integrity": "sha512-6sPWmZetzFWMsz7Dhuxsdmbu3fK+/AxKRtj7OB0/3OZAI2MHB/v2FeYh271LZ9abvnM1WIwWc/5umYjx0jo5sQ==",
1715
+
"version": "15.5.3",
1716
+
"resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.5.3.tgz",
1717
+
"integrity": "sha512-SdhaKdko6dpsSr0DldkESItVrnPYB1NS2NpShCSX5lc7SSQmLZt5Mug6t2xbiuVWEVDLZSuIAoQyYVBYp0dR5g==",
2745
1718
"dev": true,
1719
+
"license": "MIT",
2746
1720
"dependencies": {
2747
1721
"fast-glob": "3.3.1"
2748
1722
}
···
2752
1726
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz",
2753
1727
"integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==",
2754
1728
"dev": true,
1729
+
"license": "MIT",
2755
1730
"dependencies": {
2756
1731
"@nodelib/fs.stat": "^2.0.2",
2757
1732
"@nodelib/fs.walk": "^1.2.3",
···
2768
1743
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
2769
1744
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
2770
1745
"dev": true,
1746
+
"license": "ISC",
2771
1747
"dependencies": {
2772
1748
"is-glob": "^4.0.1"
2773
1749
},
···
2776
1752
}
2777
1753
},
2778
1754
"node_modules/@next/mdx": {
2779
-
"version": "16.0.3",
2780
-
"resolved": "https://registry.npmjs.org/@next/mdx/-/mdx-16.0.3.tgz",
2781
-
"integrity": "sha512-uVl2JSEGAjBV+EVnpt1cZN88SK3lJ2n7Fc+iqTsgVx2g9+Y6ru+P6nuUgXd38OHPUIwzL6k2V1u4iV3kwuTySQ==",
1755
+
"version": "15.3.2",
1756
+
"resolved": "https://registry.npmjs.org/@next/mdx/-/mdx-15.3.2.tgz",
1757
+
"integrity": "sha512-D6lSSbVzn1EiPwrBKG5QzXClcgdqiNCL8a3/6oROinzgZnYSxbVmnfs0UrqygtGSOmgW7sdJJSEOy555DoAwvw==",
1758
+
"license": "MIT",
2782
1759
"dependencies": {
2783
1760
"source-map": "^0.7.0"
2784
1761
},
···
2804
1781
}
2805
1782
},
2806
1783
"node_modules/@next/swc-darwin-arm64": {
2807
-
"version": "16.0.3",
2808
-
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.0.3.tgz",
2809
-
"integrity": "sha512-MOnbd92+OByu0p6QBAzq1ahVWzF6nyfiH07dQDez4/Nku7G249NjxDVyEfVhz8WkLiOEU+KFVnqtgcsfP2nLXg==",
1784
+
"version": "15.5.3",
1785
+
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.3.tgz",
1786
+
"integrity": "sha512-nzbHQo69+au9wJkGKTU9lP7PXv0d1J5ljFpvb+LnEomLtSbJkbZyEs6sbF3plQmiOB2l9OBtN2tNSvCH1nQ9Jg==",
2810
1787
"cpu": [
2811
1788
"arm64"
2812
1789
],
1790
+
"license": "MIT",
2813
1791
"optional": true,
2814
1792
"os": [
2815
1793
"darwin"
···
2819
1797
}
2820
1798
},
2821
1799
"node_modules/@next/swc-darwin-x64": {
2822
-
"version": "16.0.3",
2823
-
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.0.3.tgz",
2824
-
"integrity": "sha512-i70C4O1VmbTivYdRlk+5lj9xRc2BlK3oUikt3yJeHT1unL4LsNtN7UiOhVanFdc7vDAgZn1tV/9mQwMkWOJvHg==",
1800
+
"version": "15.5.3",
1801
+
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.3.tgz",
1802
+
"integrity": "sha512-w83w4SkOOhekJOcA5HBvHyGzgV1W/XvOfpkrxIse4uPWhYTTRwtGEM4v/jiXwNSJvfRvah0H8/uTLBKRXlef8g==",
2825
1803
"cpu": [
2826
1804
"x64"
2827
1805
],
1806
+
"license": "MIT",
2828
1807
"optional": true,
2829
1808
"os": [
2830
1809
"darwin"
···
2834
1813
}
2835
1814
},
2836
1815
"node_modules/@next/swc-linux-arm64-gnu": {
2837
-
"version": "16.0.3",
2838
-
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.0.3.tgz",
2839
-
"integrity": "sha512-O88gCZ95sScwD00mn/AtalyCoykhhlokxH/wi1huFK+rmiP5LAYVs/i2ruk7xST6SuXN4NI5y4Xf5vepb2jf6A==",
1816
+
"version": "15.5.3",
1817
+
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.3.tgz",
1818
+
"integrity": "sha512-+m7pfIs0/yvgVu26ieaKrifV8C8yiLe7jVp9SpcIzg7XmyyNE7toC1fy5IOQozmr6kWl/JONC51osih2RyoXRw==",
2840
1819
"cpu": [
2841
1820
"arm64"
2842
1821
],
1822
+
"license": "MIT",
2843
1823
"optional": true,
2844
1824
"os": [
2845
1825
"linux"
···
2849
1829
}
2850
1830
},
2851
1831
"node_modules/@next/swc-linux-arm64-musl": {
2852
-
"version": "16.0.3",
2853
-
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.0.3.tgz",
2854
-
"integrity": "sha512-CEErFt78S/zYXzFIiv18iQCbRbLgBluS8z1TNDQoyPi8/Jr5qhR3e8XHAIxVxPBjDbEMITprqELVc5KTfFj0gg==",
1832
+
"version": "15.5.3",
1833
+
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.3.tgz",
1834
+
"integrity": "sha512-u3PEIzuguSenoZviZJahNLgCexGFhso5mxWCrrIMdvpZn6lkME5vc/ADZG8UUk5K1uWRy4hqSFECrON6UKQBbQ==",
2855
1835
"cpu": [
2856
1836
"arm64"
2857
1837
],
1838
+
"license": "MIT",
2858
1839
"optional": true,
2859
1840
"os": [
2860
1841
"linux"
···
2864
1845
}
2865
1846
},
2866
1847
"node_modules/@next/swc-linux-x64-gnu": {
2867
-
"version": "16.0.3",
2868
-
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.0.3.tgz",
2869
-
"integrity": "sha512-Tc3i+nwt6mQ+Dwzcri/WNDj56iWdycGVh5YwwklleClzPzz7UpfaMw1ci7bLl6GRYMXhWDBfe707EXNjKtiswQ==",
1848
+
"version": "15.5.3",
1849
+
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.3.tgz",
1850
+
"integrity": "sha512-lDtOOScYDZxI2BENN9m0pfVPJDSuUkAD1YXSvlJF0DKwZt0WlA7T7o3wrcEr4Q+iHYGzEaVuZcsIbCps4K27sA==",
2870
1851
"cpu": [
2871
1852
"x64"
2872
1853
],
1854
+
"license": "MIT",
2873
1855
"optional": true,
2874
1856
"os": [
2875
1857
"linux"
···
2879
1861
}
2880
1862
},
2881
1863
"node_modules/@next/swc-linux-x64-musl": {
2882
-
"version": "16.0.3",
2883
-
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.0.3.tgz",
2884
-
"integrity": "sha512-zTh03Z/5PBBPdTurgEtr6nY0vI9KR9Ifp/jZCcHlODzwVOEKcKRBtQIGrkc7izFgOMuXDEJBmirwpGqdM/ZixA==",
1864
+
"version": "15.5.3",
1865
+
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.3.tgz",
1866
+
"integrity": "sha512-9vWVUnsx9PrY2NwdVRJ4dUURAQ8Su0sLRPqcCCxtX5zIQUBES12eRVHq6b70bbfaVaxIDGJN2afHui0eDm+cLg==",
2885
1867
"cpu": [
2886
1868
"x64"
2887
1869
],
1870
+
"license": "MIT",
2888
1871
"optional": true,
2889
1872
"os": [
2890
1873
"linux"
···
2894
1877
}
2895
1878
},
2896
1879
"node_modules/@next/swc-win32-arm64-msvc": {
2897
-
"version": "16.0.3",
2898
-
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.0.3.tgz",
2899
-
"integrity": "sha512-Jc1EHxtZovcJcg5zU43X3tuqzl/sS+CmLgjRP28ZT4vk869Ncm2NoF8qSTaL99gh6uOzgM99Shct06pSO6kA6g==",
1880
+
"version": "15.5.3",
1881
+
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.3.tgz",
1882
+
"integrity": "sha512-1CU20FZzY9LFQigRi6jM45oJMU3KziA5/sSG+dXeVaTm661snQP6xu3ykGxxwU5sLG3sh14teO/IOEPVsQMRfA==",
2900
1883
"cpu": [
2901
1884
"arm64"
2902
1885
],
1886
+
"license": "MIT",
2903
1887
"optional": true,
2904
1888
"os": [
2905
1889
"win32"
···
2909
1893
}
2910
1894
},
2911
1895
"node_modules/@next/swc-win32-x64-msvc": {
2912
-
"version": "16.0.3",
2913
-
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.0.3.tgz",
2914
-
"integrity": "sha512-N7EJ6zbxgIYpI/sWNzpVKRMbfEGgsWuOIvzkML7wxAAZhPk1Msxuo/JDu1PKjWGrAoOLaZcIX5s+/pF5LIbBBg==",
1896
+
"version": "15.5.3",
1897
+
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.3.tgz",
1898
+
"integrity": "sha512-JMoLAq3n3y5tKXPQwCK5c+6tmwkuFDa2XAxz8Wm4+IVthdBZdZGh+lmiLUHg9f9IDwIQpUjp+ysd6OkYTyZRZw==",
2915
1899
"cpu": [
2916
1900
"x64"
2917
1901
],
1902
+
"license": "MIT",
2918
1903
"optional": true,
2919
1904
"os": [
2920
1905
"win32"
···
7012
5997
"dev": true,
7013
5998
"license": "MIT"
7014
5999
},
6000
+
"node_modules/@rushstack/eslint-patch": {
6001
+
"version": "1.10.3",
6002
+
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.3.tgz",
6003
+
"integrity": "sha512-qC/xYId4NMebE6w/V33Fh9gWxLgURiNYgVNObbJl2LZv0GUUItCcCqC5axQSwRaAgaxl2mELq1rMzlswaQ0Zxg==",
6004
+
"dev": true
6005
+
},
7015
6006
"node_modules/@shikijs/core": {
7016
6007
"version": "3.8.1",
7017
6008
"resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.8.1.tgz",
···
7606
6597
"@types/unist": "*"
7607
6598
}
7608
6599
},
7609
-
"node_modules/@types/json-schema": {
7610
-
"version": "7.0.15",
7611
-
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
7612
-
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
7613
-
"dev": true
7614
-
},
7615
6600
"node_modules/@types/json5": {
7616
6601
"version": "0.0.29",
7617
6602
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
···
7738
6723
"integrity": "sha512-B34A7uot1Cv0XtaHRYDATltAdKx0BvVKNgYNqE4WjtPUa4VQJM7kxeXcVKaH+KS+kCmZ+6w+QaUdcljiheiBJA=="
7739
6724
},
7740
6725
"node_modules/@types/react": {
7741
-
"version": "19.2.6",
7742
-
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.6.tgz",
7743
-
"integrity": "sha512-p/jUvulfgU7oKtj6Xpk8cA2Y1xKTtICGpJYeJXz2YVO2UcvjQgeRMLDGfDeqeRW2Ta+0QNFwcc8X3GH8SxZz6w==",
6726
+
"version": "19.1.3",
6727
+
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.3.tgz",
6728
+
"integrity": "sha512-dLWQ+Z0CkIvK1J8+wrDPwGxEYFA4RAyHoZPxHVGspYmFVnwGSNT24cGIhFJrtfRnWVuW8X7NO52gCXmhkVUWGQ==",
6729
+
"license": "MIT",
7744
6730
"dependencies": {
7745
-
"csstype": "^3.2.2"
6731
+
"csstype": "^3.0.2"
7746
6732
}
7747
6733
},
7748
6734
"node_modules/@types/react-dom": {
7749
-
"version": "19.2.3",
7750
-
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
7751
-
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
6735
+
"version": "19.1.3",
6736
+
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.3.tgz",
6737
+
"integrity": "sha512-rJXC08OG0h3W6wDMFxQrZF00Kq6qQvw0djHRdzl3U5DnIERz0MRce3WVc7IS6JYBwtaP/DwYtRRjVlvivNveKg==",
7752
6738
"devOptional": true,
6739
+
"license": "MIT",
7753
6740
"peerDependencies": {
7754
-
"@types/react": "^19.2.0"
6741
+
"@types/react": "^19.0.0"
7755
6742
}
7756
6743
},
7757
6744
"node_modules/@types/shimmer": {
···
7789
6776
}
7790
6777
},
7791
6778
"node_modules/@typescript-eslint/eslint-plugin": {
7792
-
"version": "8.47.0",
7793
-
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.47.0.tgz",
7794
-
"integrity": "sha512-fe0rz9WJQ5t2iaLfdbDc9T80GJy0AeO453q8C3YCilnGozvOyCG5t+EZtg7j7D88+c3FipfP/x+wzGnh1xp8ZA==",
6779
+
"version": "8.32.0",
6780
+
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.0.tgz",
6781
+
"integrity": "sha512-/jU9ettcntkBFmWUzzGgsClEi2ZFiikMX5eEQsmxIAWMOn4H3D4rvHssstmAHGVvrYnaMqdWWWg0b5M6IN/MTQ==",
7795
6782
"dev": true,
6783
+
"license": "MIT",
7796
6784
"dependencies": {
7797
6785
"@eslint-community/regexpp": "^4.10.0",
7798
-
"@typescript-eslint/scope-manager": "8.47.0",
7799
-
"@typescript-eslint/type-utils": "8.47.0",
7800
-
"@typescript-eslint/utils": "8.47.0",
7801
-
"@typescript-eslint/visitor-keys": "8.47.0",
6786
+
"@typescript-eslint/scope-manager": "8.32.0",
6787
+
"@typescript-eslint/type-utils": "8.32.0",
6788
+
"@typescript-eslint/utils": "8.32.0",
6789
+
"@typescript-eslint/visitor-keys": "8.32.0",
7802
6790
"graphemer": "^1.4.0",
7803
-
"ignore": "^7.0.0",
6791
+
"ignore": "^5.3.1",
7804
6792
"natural-compare": "^1.4.0",
7805
6793
"ts-api-utils": "^2.1.0"
7806
6794
},
···
7812
6800
"url": "https://opencollective.com/typescript-eslint"
7813
6801
},
7814
6802
"peerDependencies": {
7815
-
"@typescript-eslint/parser": "^8.47.0",
6803
+
"@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
7816
6804
"eslint": "^8.57.0 || ^9.0.0",
7817
-
"typescript": ">=4.8.4 <6.0.0"
7818
-
}
7819
-
},
7820
-
"node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
7821
-
"version": "7.0.5",
7822
-
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
7823
-
"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
7824
-
"dev": true,
7825
-
"engines": {
7826
-
"node": ">= 4"
6805
+
"typescript": ">=4.8.4 <5.9.0"
7827
6806
}
7828
6807
},
7829
6808
"node_modules/@typescript-eslint/parser": {
7830
-
"version": "8.47.0",
7831
-
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.47.0.tgz",
7832
-
"integrity": "sha512-lJi3PfxVmo0AkEY93ecfN+r8SofEqZNGByvHAI3GBLrvt1Cw6H5k1IM02nSzu0RfUafr2EvFSw0wAsZgubNplQ==",
6809
+
"version": "8.32.0",
6810
+
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.32.0.tgz",
6811
+
"integrity": "sha512-B2MdzyWxCE2+SqiZHAjPphft+/2x2FlO9YBx7eKE1BCb+rqBlQdhtAEhzIEdozHd55DXPmxBdpMygFJjfjjA9A==",
7833
6812
"dev": true,
6813
+
"license": "MIT",
7834
6814
"dependencies": {
7835
-
"@typescript-eslint/scope-manager": "8.47.0",
7836
-
"@typescript-eslint/types": "8.47.0",
7837
-
"@typescript-eslint/typescript-estree": "8.47.0",
7838
-
"@typescript-eslint/visitor-keys": "8.47.0",
6815
+
"@typescript-eslint/scope-manager": "8.32.0",
6816
+
"@typescript-eslint/types": "8.32.0",
6817
+
"@typescript-eslint/typescript-estree": "8.32.0",
6818
+
"@typescript-eslint/visitor-keys": "8.32.0",
7839
6819
"debug": "^4.3.4"
7840
6820
},
7841
6821
"engines": {
···
7847
6827
},
7848
6828
"peerDependencies": {
7849
6829
"eslint": "^8.57.0 || ^9.0.0",
7850
-
"typescript": ">=4.8.4 <6.0.0"
7851
-
}
7852
-
},
7853
-
"node_modules/@typescript-eslint/project-service": {
7854
-
"version": "8.47.0",
7855
-
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.47.0.tgz",
7856
-
"integrity": "sha512-2X4BX8hUeB5JcA1TQJ7GjcgulXQ+5UkNb0DL8gHsHUHdFoiCTJoYLTpib3LtSDPZsRET5ygN4qqIWrHyYIKERA==",
7857
-
"dev": true,
7858
-
"dependencies": {
7859
-
"@typescript-eslint/tsconfig-utils": "^8.47.0",
7860
-
"@typescript-eslint/types": "^8.47.0",
7861
-
"debug": "^4.3.4"
7862
-
},
7863
-
"engines": {
7864
-
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
7865
-
},
7866
-
"funding": {
7867
-
"type": "opencollective",
7868
-
"url": "https://opencollective.com/typescript-eslint"
7869
-
},
7870
-
"peerDependencies": {
7871
-
"typescript": ">=4.8.4 <6.0.0"
6830
+
"typescript": ">=4.8.4 <5.9.0"
7872
6831
}
7873
6832
},
7874
6833
"node_modules/@typescript-eslint/scope-manager": {
7875
-
"version": "8.47.0",
7876
-
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.47.0.tgz",
7877
-
"integrity": "sha512-a0TTJk4HXMkfpFkL9/WaGTNuv7JWfFTQFJd6zS9dVAjKsojmv9HT55xzbEpnZoY+VUb+YXLMp+ihMLz/UlZfDg==",
6834
+
"version": "8.32.0",
6835
+
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.32.0.tgz",
6836
+
"integrity": "sha512-jc/4IxGNedXkmG4mx4nJTILb6TMjL66D41vyeaPWvDUmeYQzF3lKtN15WsAeTr65ce4mPxwopPSo1yUUAWw0hQ==",
7878
6837
"dev": true,
6838
+
"license": "MIT",
7879
6839
"dependencies": {
7880
-
"@typescript-eslint/types": "8.47.0",
7881
-
"@typescript-eslint/visitor-keys": "8.47.0"
6840
+
"@typescript-eslint/types": "8.32.0",
6841
+
"@typescript-eslint/visitor-keys": "8.32.0"
7882
6842
},
7883
6843
"engines": {
7884
6844
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
···
7888
6848
"url": "https://opencollective.com/typescript-eslint"
7889
6849
}
7890
6850
},
7891
-
"node_modules/@typescript-eslint/tsconfig-utils": {
7892
-
"version": "8.47.0",
7893
-
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.47.0.tgz",
7894
-
"integrity": "sha512-ybUAvjy4ZCL11uryalkKxuT3w3sXJAuWhOoGS3T/Wu+iUu1tGJmk5ytSY8gbdACNARmcYEB0COksD2j6hfGK2g==",
7895
-
"dev": true,
7896
-
"engines": {
7897
-
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
7898
-
},
7899
-
"funding": {
7900
-
"type": "opencollective",
7901
-
"url": "https://opencollective.com/typescript-eslint"
7902
-
},
7903
-
"peerDependencies": {
7904
-
"typescript": ">=4.8.4 <6.0.0"
7905
-
}
7906
-
},
7907
6851
"node_modules/@typescript-eslint/type-utils": {
7908
-
"version": "8.47.0",
7909
-
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.47.0.tgz",
7910
-
"integrity": "sha512-QC9RiCmZ2HmIdCEvhd1aJELBlD93ErziOXXlHEZyuBo3tBiAZieya0HLIxp+DoDWlsQqDawyKuNEhORyku+P8A==",
6852
+
"version": "8.32.0",
6853
+
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.32.0.tgz",
6854
+
"integrity": "sha512-t2vouuYQKEKSLtJaa5bB4jHeha2HJczQ6E5IXPDPgIty9EqcJxpr1QHQ86YyIPwDwxvUmLfP2YADQ5ZY4qddZg==",
7911
6855
"dev": true,
6856
+
"license": "MIT",
7912
6857
"dependencies": {
7913
-
"@typescript-eslint/types": "8.47.0",
7914
-
"@typescript-eslint/typescript-estree": "8.47.0",
7915
-
"@typescript-eslint/utils": "8.47.0",
6858
+
"@typescript-eslint/typescript-estree": "8.32.0",
6859
+
"@typescript-eslint/utils": "8.32.0",
7916
6860
"debug": "^4.3.4",
7917
6861
"ts-api-utils": "^2.1.0"
7918
6862
},
···
7925
6869
},
7926
6870
"peerDependencies": {
7927
6871
"eslint": "^8.57.0 || ^9.0.0",
7928
-
"typescript": ">=4.8.4 <6.0.0"
6872
+
"typescript": ">=4.8.4 <5.9.0"
7929
6873
}
7930
6874
},
7931
6875
"node_modules/@typescript-eslint/types": {
7932
-
"version": "8.47.0",
7933
-
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.47.0.tgz",
7934
-
"integrity": "sha512-nHAE6bMKsizhA2uuYZbEbmp5z2UpffNrPEqiKIeN7VsV6UY/roxanWfoRrf6x/k9+Obf+GQdkm0nPU+vnMXo9A==",
6876
+
"version": "8.32.0",
6877
+
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.32.0.tgz",
6878
+
"integrity": "sha512-O5Id6tGadAZEMThM6L9HmVf5hQUXNSxLVKeGJYWNhhVseps/0LddMkp7//VDkzwJ69lPL0UmZdcZwggj9akJaA==",
7935
6879
"dev": true,
6880
+
"license": "MIT",
7936
6881
"engines": {
7937
6882
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
7938
6883
},
···
7942
6887
}
7943
6888
},
7944
6889
"node_modules/@typescript-eslint/typescript-estree": {
7945
-
"version": "8.47.0",
7946
-
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.47.0.tgz",
7947
-
"integrity": "sha512-k6ti9UepJf5NpzCjH31hQNLHQWupTRPhZ+KFF8WtTuTpy7uHPfeg2NM7cP27aCGajoEplxJDFVCEm9TGPYyiVg==",
6890
+
"version": "8.32.0",
6891
+
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.32.0.tgz",
6892
+
"integrity": "sha512-pU9VD7anSCOIoBFnhTGfOzlVFQIA1XXiQpH/CezqOBaDppRwTglJzCC6fUQGpfwey4T183NKhF1/mfatYmjRqQ==",
7948
6893
"dev": true,
6894
+
"license": "MIT",
7949
6895
"dependencies": {
7950
-
"@typescript-eslint/project-service": "8.47.0",
7951
-
"@typescript-eslint/tsconfig-utils": "8.47.0",
7952
-
"@typescript-eslint/types": "8.47.0",
7953
-
"@typescript-eslint/visitor-keys": "8.47.0",
6896
+
"@typescript-eslint/types": "8.32.0",
6897
+
"@typescript-eslint/visitor-keys": "8.32.0",
7954
6898
"debug": "^4.3.4",
7955
6899
"fast-glob": "^3.3.2",
7956
6900
"is-glob": "^4.0.3",
···
7966
6910
"url": "https://opencollective.com/typescript-eslint"
7967
6911
},
7968
6912
"peerDependencies": {
7969
-
"typescript": ">=4.8.4 <6.0.0"
6913
+
"typescript": ">=4.8.4 <5.9.0"
7970
6914
}
7971
6915
},
7972
6916
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
7973
-
"version": "2.0.2",
7974
-
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
7975
-
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
6917
+
"version": "2.0.1",
6918
+
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
6919
+
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
7976
6920
"dev": true,
6921
+
"license": "MIT",
7977
6922
"dependencies": {
7978
6923
"balanced-match": "^1.0.0"
7979
6924
}
···
7983
6928
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
7984
6929
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
7985
6930
"dev": true,
6931
+
"license": "ISC",
7986
6932
"dependencies": {
7987
6933
"brace-expansion": "^2.0.1"
7988
6934
},
···
7994
6940
}
7995
6941
},
7996
6942
"node_modules/@typescript-eslint/utils": {
7997
-
"version": "8.47.0",
7998
-
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.47.0.tgz",
7999
-
"integrity": "sha512-g7XrNf25iL4TJOiPqatNuaChyqt49a/onq5YsJ9+hXeugK+41LVg7AxikMfM02PC6jbNtZLCJj6AUcQXJS/jGQ==",
6943
+
"version": "8.32.0",
6944
+
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.32.0.tgz",
6945
+
"integrity": "sha512-8S9hXau6nQ/sYVtC3D6ISIDoJzS1NsCK+gluVhLN2YkBPX+/1wkwyUiDKnxRh15579WoOIyVWnoyIf3yGI9REw==",
8000
6946
"dev": true,
6947
+
"license": "MIT",
8001
6948
"dependencies": {
8002
6949
"@eslint-community/eslint-utils": "^4.7.0",
8003
-
"@typescript-eslint/scope-manager": "8.47.0",
8004
-
"@typescript-eslint/types": "8.47.0",
8005
-
"@typescript-eslint/typescript-estree": "8.47.0"
6950
+
"@typescript-eslint/scope-manager": "8.32.0",
6951
+
"@typescript-eslint/types": "8.32.0",
6952
+
"@typescript-eslint/typescript-estree": "8.32.0"
8006
6953
},
8007
6954
"engines": {
8008
6955
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
···
8013
6960
},
8014
6961
"peerDependencies": {
8015
6962
"eslint": "^8.57.0 || ^9.0.0",
8016
-
"typescript": ">=4.8.4 <6.0.0"
6963
+
"typescript": ">=4.8.4 <5.9.0"
8017
6964
}
8018
6965
},
8019
6966
"node_modules/@typescript-eslint/visitor-keys": {
8020
-
"version": "8.47.0",
8021
-
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.47.0.tgz",
8022
-
"integrity": "sha512-SIV3/6eftCy1bNzCQoPmbWsRLujS8t5iDIZ4spZOBHqrM+yfX2ogg8Tt3PDTAVKw3sSCiUgg30uOAvK2r9zGjQ==",
6967
+
"version": "8.32.0",
6968
+
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.32.0.tgz",
6969
+
"integrity": "sha512-1rYQTCLFFzOI5Nl0c8LUpJT8HxpwVRn9E4CkMsYfuN6ctmQqExjSTzzSk0Tz2apmXy7WU6/6fyaZVVA/thPN+w==",
8023
6970
"dev": true,
6971
+
"license": "MIT",
8024
6972
"dependencies": {
8025
-
"@typescript-eslint/types": "8.47.0",
8026
-
"eslint-visitor-keys": "^4.2.1"
6973
+
"@typescript-eslint/types": "8.32.0",
6974
+
"eslint-visitor-keys": "^4.2.0"
8027
6975
},
8028
6976
"engines": {
8029
6977
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
···
8031
6979
"funding": {
8032
6980
"type": "opencollective",
8033
6981
"url": "https://opencollective.com/typescript-eslint"
6982
+
}
6983
+
},
6984
+
"node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
6985
+
"version": "4.2.0",
6986
+
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
6987
+
"integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
6988
+
"dev": true,
6989
+
"license": "Apache-2.0",
6990
+
"engines": {
6991
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
6992
+
},
6993
+
"funding": {
6994
+
"url": "https://opencollective.com/eslint"
8034
6995
}
8035
6996
},
8036
6997
"node_modules/@ungap/structured-clone": {
···
8206
7167
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
8207
7168
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
8208
7169
"dev": true,
7170
+
"license": "MIT",
8209
7171
"dependencies": {
8210
7172
"fast-deep-equal": "^3.1.3",
8211
7173
"fast-uri": "^3.0.1",
···
8302
7264
"license": "MIT"
8303
7265
},
8304
7266
"node_modules/array-includes": {
8305
-
"version": "3.1.9",
8306
-
"resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz",
8307
-
"integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==",
7267
+
"version": "3.1.8",
7268
+
"resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz",
7269
+
"integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==",
8308
7270
"dev": true,
8309
7271
"dependencies": {
8310
-
"call-bind": "^1.0.8",
8311
-
"call-bound": "^1.0.4",
7272
+
"call-bind": "^1.0.7",
8312
7273
"define-properties": "^1.2.1",
8313
-
"es-abstract": "^1.24.0",
8314
-
"es-object-atoms": "^1.1.1",
8315
-
"get-intrinsic": "^1.3.0",
8316
-
"is-string": "^1.1.1",
8317
-
"math-intrinsics": "^1.1.0"
7274
+
"es-abstract": "^1.23.2",
7275
+
"es-object-atoms": "^1.0.0",
7276
+
"get-intrinsic": "^1.2.4",
7277
+
"is-string": "^1.0.7"
8318
7278
},
8319
7279
"engines": {
8320
7280
"node": ">= 0.4"
···
8345
7305
}
8346
7306
},
8347
7307
"node_modules/array.prototype.findlastindex": {
8348
-
"version": "1.2.6",
8349
-
"resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz",
8350
-
"integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==",
7308
+
"version": "1.2.5",
7309
+
"resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz",
7310
+
"integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==",
8351
7311
"dev": true,
8352
7312
"dependencies": {
8353
-
"call-bind": "^1.0.8",
8354
-
"call-bound": "^1.0.4",
7313
+
"call-bind": "^1.0.7",
8355
7314
"define-properties": "^1.2.1",
8356
-
"es-abstract": "^1.23.9",
7315
+
"es-abstract": "^1.23.2",
8357
7316
"es-errors": "^1.3.0",
8358
-
"es-object-atoms": "^1.1.1",
8359
-
"es-shim-unscopables": "^1.1.0"
7317
+
"es-object-atoms": "^1.0.0",
7318
+
"es-shim-unscopables": "^1.0.2"
8360
7319
},
8361
7320
"engines": {
8362
7321
"node": ">= 0.4"
···
8366
7325
}
8367
7326
},
8368
7327
"node_modules/array.prototype.flat": {
8369
-
"version": "1.3.3",
8370
-
"resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz",
8371
-
"integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==",
7328
+
"version": "1.3.2",
7329
+
"resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz",
7330
+
"integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==",
8372
7331
"dev": true,
8373
7332
"dependencies": {
8374
-
"call-bind": "^1.0.8",
8375
-
"define-properties": "^1.2.1",
8376
-
"es-abstract": "^1.23.5",
8377
-
"es-shim-unscopables": "^1.0.2"
7333
+
"call-bind": "^1.0.2",
7334
+
"define-properties": "^1.2.0",
7335
+
"es-abstract": "^1.22.1",
7336
+
"es-shim-unscopables": "^1.0.0"
8378
7337
},
8379
7338
"engines": {
8380
7339
"node": ">= 0.4"
···
8585
7544
}
8586
7545
]
8587
7546
},
8588
-
"node_modules/baseline-browser-mapping": {
8589
-
"version": "2.8.30",
8590
-
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.30.tgz",
8591
-
"integrity": "sha512-aTUKW4ptQhS64+v2d6IkPzymEzzhw+G0bA1g3uBRV3+ntkH+svttKseW5IOR4Ed6NUVKqnY7qT3dKvzQ7io4AA==",
8592
-
"dev": true,
8593
-
"bin": {
8594
-
"baseline-browser-mapping": "dist/cli.js"
8595
-
}
8596
-
},
8597
7547
"node_modules/bignumber.js": {
8598
7548
"version": "9.3.1",
8599
7549
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz",
···
8712
7662
"node": ">=8"
8713
7663
}
8714
7664
},
8715
-
"node_modules/browserslist": {
8716
-
"version": "4.28.0",
8717
-
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.0.tgz",
8718
-
"integrity": "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==",
8719
-
"dev": true,
8720
-
"funding": [
8721
-
{
8722
-
"type": "opencollective",
8723
-
"url": "https://opencollective.com/browserslist"
8724
-
},
8725
-
{
8726
-
"type": "tidelift",
8727
-
"url": "https://tidelift.com/funding/github/npm/browserslist"
8728
-
},
8729
-
{
8730
-
"type": "github",
8731
-
"url": "https://github.com/sponsors/ai"
8732
-
}
8733
-
],
8734
-
"dependencies": {
8735
-
"baseline-browser-mapping": "^2.8.25",
8736
-
"caniuse-lite": "^1.0.30001754",
8737
-
"electron-to-chromium": "^1.5.249",
8738
-
"node-releases": "^2.0.27",
8739
-
"update-browserslist-db": "^1.1.4"
8740
-
},
8741
-
"bin": {
8742
-
"browserslist": "cli.js"
8743
-
},
8744
-
"engines": {
8745
-
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
8746
-
}
8747
-
},
8748
7665
"node_modules/buffer": {
8749
7666
"version": "6.0.3",
8750
7667
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
···
8848
7765
}
8849
7766
},
8850
7767
"node_modules/caniuse-lite": {
8851
-
"version": "1.0.30001756",
8852
-
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001756.tgz",
8853
-
"integrity": "sha512-4HnCNKbMLkLdhJz3TToeVWHSnfJvPaq6vu/eRP0Ahub/07n484XHhBF5AJoSGHdVrS8tKFauUQz8Bp9P7LVx7A==",
7768
+
"version": "1.0.30001717",
7769
+
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001717.tgz",
7770
+
"integrity": "sha512-auPpttCq6BDEG8ZAuHJIplGw6GODhjw+/11e7IjpnYCxZcW/ONgPs0KVBJ0d1bY3e2+7PRe5RCLyP+PfwVgkYw==",
8854
7771
"funding": [
8855
7772
{
8856
7773
"type": "opencollective",
···
8864
7781
"type": "github",
8865
7782
"url": "https://github.com/sponsors/ai"
8866
7783
}
8867
-
]
7784
+
],
7785
+
"license": "CC-BY-4.0"
8868
7786
},
8869
7787
"node_modules/canonicalize": {
8870
7788
"version": "1.0.8",
···
9187
8105
"node": ">= 0.6"
9188
8106
}
9189
8107
},
9190
-
"node_modules/convert-source-map": {
9191
-
"version": "2.0.0",
9192
-
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
9193
-
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
9194
-
"dev": true
9195
-
},
9196
8108
"node_modules/cookie": {
9197
8109
"version": "0.5.0",
9198
8110
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
···
9244
8156
}
9245
8157
},
9246
8158
"node_modules/cross-spawn": {
9247
-
"version": "7.0.6",
9248
-
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
9249
-
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
8159
+
"version": "7.0.3",
8160
+
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
8161
+
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
9250
8162
"dev": true,
9251
8163
"dependencies": {
9252
8164
"path-key": "^3.1.0",
···
9258
8170
}
9259
8171
},
9260
8172
"node_modules/csstype": {
9261
-
"version": "3.2.3",
9262
-
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
9263
-
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="
8173
+
"version": "3.1.3",
8174
+
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
8175
+
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
9264
8176
},
9265
8177
"node_modules/d": {
9266
8178
"version": "1.0.2",
···
9522
8434
"node": "*"
9523
8435
}
9524
8436
},
8437
+
"node_modules/doctrine": {
8438
+
"version": "3.0.0",
8439
+
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
8440
+
"integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
8441
+
"dev": true,
8442
+
"dependencies": {
8443
+
"esutils": "^2.0.2"
8444
+
},
8445
+
"engines": {
8446
+
"node": ">=6.0.0"
8447
+
}
8448
+
},
9525
8449
"node_modules/dreamopt": {
9526
8450
"version": "0.8.0",
9527
8451
"resolved": "https://registry.npmjs.org/dreamopt/-/dreamopt-0.8.0.tgz",
···
9554
8478
"drizzle-kit": "bin.cjs"
9555
8479
}
9556
8480
},
9557
-
"node_modules/drizzle-kit/node_modules/@esbuild/aix-ppc64": {
9558
-
"version": "0.19.12",
9559
-
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz",
9560
-
"integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==",
9561
-
"cpu": [
9562
-
"ppc64"
9563
-
],
9564
-
"dev": true,
9565
-
"optional": true,
9566
-
"os": [
9567
-
"aix"
9568
-
],
9569
-
"engines": {
9570
-
"node": ">=12"
9571
-
}
9572
-
},
9573
-
"node_modules/drizzle-kit/node_modules/@esbuild/android-arm": {
9574
-
"version": "0.19.12",
9575
-
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz",
9576
-
"integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==",
9577
-
"cpu": [
9578
-
"arm"
9579
-
],
9580
-
"dev": true,
9581
-
"optional": true,
9582
-
"os": [
9583
-
"android"
9584
-
],
9585
-
"engines": {
9586
-
"node": ">=12"
9587
-
}
9588
-
},
9589
-
"node_modules/drizzle-kit/node_modules/@esbuild/android-arm64": {
9590
-
"version": "0.19.12",
9591
-
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz",
9592
-
"integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==",
9593
-
"cpu": [
9594
-
"arm64"
9595
-
],
9596
-
"dev": true,
9597
-
"optional": true,
9598
-
"os": [
9599
-
"android"
9600
-
],
9601
-
"engines": {
9602
-
"node": ">=12"
9603
-
}
9604
-
},
9605
-
"node_modules/drizzle-kit/node_modules/@esbuild/android-x64": {
9606
-
"version": "0.19.12",
9607
-
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz",
9608
-
"integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==",
9609
-
"cpu": [
9610
-
"x64"
9611
-
],
9612
-
"dev": true,
9613
-
"optional": true,
9614
-
"os": [
9615
-
"android"
9616
-
],
9617
-
"engines": {
9618
-
"node": ">=12"
9619
-
}
9620
-
},
9621
-
"node_modules/drizzle-kit/node_modules/@esbuild/darwin-arm64": {
9622
-
"version": "0.19.12",
9623
-
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz",
9624
-
"integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==",
9625
-
"cpu": [
9626
-
"arm64"
9627
-
],
9628
-
"dev": true,
9629
-
"optional": true,
9630
-
"os": [
9631
-
"darwin"
9632
-
],
9633
-
"engines": {
9634
-
"node": ">=12"
9635
-
}
9636
-
},
9637
-
"node_modules/drizzle-kit/node_modules/@esbuild/darwin-x64": {
9638
-
"version": "0.19.12",
9639
-
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz",
9640
-
"integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==",
9641
-
"cpu": [
9642
-
"x64"
9643
-
],
9644
-
"dev": true,
9645
-
"optional": true,
9646
-
"os": [
9647
-
"darwin"
9648
-
],
9649
-
"engines": {
9650
-
"node": ">=12"
9651
-
}
9652
-
},
9653
-
"node_modules/drizzle-kit/node_modules/@esbuild/freebsd-arm64": {
9654
-
"version": "0.19.12",
9655
-
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz",
9656
-
"integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==",
9657
-
"cpu": [
9658
-
"arm64"
9659
-
],
9660
-
"dev": true,
9661
-
"optional": true,
9662
-
"os": [
9663
-
"freebsd"
9664
-
],
9665
-
"engines": {
9666
-
"node": ">=12"
9667
-
}
9668
-
},
9669
-
"node_modules/drizzle-kit/node_modules/@esbuild/freebsd-x64": {
9670
-
"version": "0.19.12",
9671
-
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz",
9672
-
"integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==",
9673
-
"cpu": [
9674
-
"x64"
9675
-
],
9676
-
"dev": true,
9677
-
"optional": true,
9678
-
"os": [
9679
-
"freebsd"
9680
-
],
9681
-
"engines": {
9682
-
"node": ">=12"
9683
-
}
9684
-
},
9685
-
"node_modules/drizzle-kit/node_modules/@esbuild/linux-arm": {
9686
-
"version": "0.19.12",
9687
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz",
9688
-
"integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==",
9689
-
"cpu": [
9690
-
"arm"
9691
-
],
9692
-
"dev": true,
9693
-
"optional": true,
9694
-
"os": [
9695
-
"linux"
9696
-
],
9697
-
"engines": {
9698
-
"node": ">=12"
9699
-
}
9700
-
},
9701
-
"node_modules/drizzle-kit/node_modules/@esbuild/linux-arm64": {
9702
-
"version": "0.19.12",
9703
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz",
9704
-
"integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==",
9705
-
"cpu": [
9706
-
"arm64"
9707
-
],
9708
-
"dev": true,
9709
-
"optional": true,
9710
-
"os": [
9711
-
"linux"
9712
-
],
9713
-
"engines": {
9714
-
"node": ">=12"
9715
-
}
9716
-
},
9717
-
"node_modules/drizzle-kit/node_modules/@esbuild/linux-ia32": {
9718
-
"version": "0.19.12",
9719
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz",
9720
-
"integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==",
9721
-
"cpu": [
9722
-
"ia32"
9723
-
],
9724
-
"dev": true,
9725
-
"optional": true,
9726
-
"os": [
9727
-
"linux"
9728
-
],
9729
-
"engines": {
9730
-
"node": ">=12"
9731
-
}
9732
-
},
9733
-
"node_modules/drizzle-kit/node_modules/@esbuild/linux-loong64": {
9734
-
"version": "0.19.12",
9735
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz",
9736
-
"integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==",
9737
-
"cpu": [
9738
-
"loong64"
9739
-
],
9740
-
"dev": true,
9741
-
"optional": true,
9742
-
"os": [
9743
-
"linux"
9744
-
],
9745
-
"engines": {
9746
-
"node": ">=12"
9747
-
}
9748
-
},
9749
-
"node_modules/drizzle-kit/node_modules/@esbuild/linux-mips64el": {
9750
-
"version": "0.19.12",
9751
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz",
9752
-
"integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==",
9753
-
"cpu": [
9754
-
"mips64el"
9755
-
],
9756
-
"dev": true,
9757
-
"optional": true,
9758
-
"os": [
9759
-
"linux"
9760
-
],
9761
-
"engines": {
9762
-
"node": ">=12"
9763
-
}
9764
-
},
9765
-
"node_modules/drizzle-kit/node_modules/@esbuild/linux-ppc64": {
9766
-
"version": "0.19.12",
9767
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz",
9768
-
"integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==",
9769
-
"cpu": [
9770
-
"ppc64"
9771
-
],
9772
-
"dev": true,
9773
-
"optional": true,
9774
-
"os": [
9775
-
"linux"
9776
-
],
9777
-
"engines": {
9778
-
"node": ">=12"
9779
-
}
9780
-
},
9781
-
"node_modules/drizzle-kit/node_modules/@esbuild/linux-riscv64": {
9782
-
"version": "0.19.12",
9783
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz",
9784
-
"integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==",
9785
-
"cpu": [
9786
-
"riscv64"
9787
-
],
9788
-
"dev": true,
9789
-
"optional": true,
9790
-
"os": [
9791
-
"linux"
9792
-
],
9793
-
"engines": {
9794
-
"node": ">=12"
9795
-
}
9796
-
},
9797
-
"node_modules/drizzle-kit/node_modules/@esbuild/linux-s390x": {
9798
-
"version": "0.19.12",
9799
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz",
9800
-
"integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==",
9801
-
"cpu": [
9802
-
"s390x"
9803
-
],
9804
-
"dev": true,
9805
-
"optional": true,
9806
-
"os": [
9807
-
"linux"
9808
-
],
9809
-
"engines": {
9810
-
"node": ">=12"
9811
-
}
9812
-
},
9813
8481
"node_modules/drizzle-kit/node_modules/@esbuild/linux-x64": {
9814
8482
"version": "0.19.12",
9815
8483
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz",
···
9827
8495
"node": ">=12"
9828
8496
}
9829
8497
},
9830
-
"node_modules/drizzle-kit/node_modules/@esbuild/netbsd-x64": {
9831
-
"version": "0.19.12",
9832
-
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz",
9833
-
"integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==",
9834
-
"cpu": [
9835
-
"x64"
9836
-
],
9837
-
"dev": true,
9838
-
"optional": true,
9839
-
"os": [
9840
-
"netbsd"
9841
-
],
9842
-
"engines": {
9843
-
"node": ">=12"
9844
-
}
9845
-
},
9846
-
"node_modules/drizzle-kit/node_modules/@esbuild/openbsd-x64": {
9847
-
"version": "0.19.12",
9848
-
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz",
9849
-
"integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==",
9850
-
"cpu": [
9851
-
"x64"
9852
-
],
9853
-
"dev": true,
9854
-
"optional": true,
9855
-
"os": [
9856
-
"openbsd"
9857
-
],
9858
-
"engines": {
9859
-
"node": ">=12"
9860
-
}
9861
-
},
9862
-
"node_modules/drizzle-kit/node_modules/@esbuild/sunos-x64": {
9863
-
"version": "0.19.12",
9864
-
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz",
9865
-
"integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==",
9866
-
"cpu": [
9867
-
"x64"
9868
-
],
9869
-
"dev": true,
9870
-
"optional": true,
9871
-
"os": [
9872
-
"sunos"
9873
-
],
9874
-
"engines": {
9875
-
"node": ">=12"
9876
-
}
9877
-
},
9878
-
"node_modules/drizzle-kit/node_modules/@esbuild/win32-arm64": {
9879
-
"version": "0.19.12",
9880
-
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz",
9881
-
"integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==",
9882
-
"cpu": [
9883
-
"arm64"
9884
-
],
9885
-
"dev": true,
9886
-
"optional": true,
9887
-
"os": [
9888
-
"win32"
9889
-
],
9890
-
"engines": {
9891
-
"node": ">=12"
9892
-
}
9893
-
},
9894
-
"node_modules/drizzle-kit/node_modules/@esbuild/win32-ia32": {
9895
-
"version": "0.19.12",
9896
-
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz",
9897
-
"integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==",
9898
-
"cpu": [
9899
-
"ia32"
9900
-
],
9901
-
"dev": true,
9902
-
"optional": true,
9903
-
"os": [
9904
-
"win32"
9905
-
],
9906
-
"engines": {
9907
-
"node": ">=12"
9908
-
}
9909
-
},
9910
-
"node_modules/drizzle-kit/node_modules/@esbuild/win32-x64": {
9911
-
"version": "0.19.12",
9912
-
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz",
9913
-
"integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==",
9914
-
"cpu": [
9915
-
"x64"
9916
-
],
9917
-
"dev": true,
9918
-
"optional": true,
9919
-
"os": [
9920
-
"win32"
9921
-
],
9922
-
"engines": {
9923
-
"node": ">=12"
9924
-
}
9925
-
},
9926
8498
"node_modules/drizzle-kit/node_modules/esbuild": {
9927
8499
"version": "0.19.12",
9928
8500
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz",
···
10106
8678
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
10107
8679
"license": "MIT"
10108
8680
},
10109
-
"node_modules/electron-to-chromium": {
10110
-
"version": "1.5.258",
10111
-
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.258.tgz",
10112
-
"integrity": "sha512-rHUggNV5jKQ0sSdWwlaRDkFc3/rRJIVnOSe9yR4zrR07m3ZxhP4N27Hlg8VeJGGYgFTxK5NqDmWI4DSH72vIJg==",
10113
-
"dev": true
10114
-
},
10115
8681
"node_modules/emoji-regex": {
10116
8682
"version": "9.2.2",
10117
8683
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
···
10165
8731
}
10166
8732
},
10167
8733
"node_modules/es-abstract": {
10168
-
"version": "1.24.0",
10169
-
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz",
10170
-
"integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==",
8734
+
"version": "1.23.9",
8735
+
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz",
8736
+
"integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==",
10171
8737
"dev": true,
8738
+
"license": "MIT",
10172
8739
"dependencies": {
10173
8740
"array-buffer-byte-length": "^1.0.2",
10174
8741
"arraybuffer.prototype.slice": "^1.0.4",
10175
8742
"available-typed-arrays": "^1.0.7",
10176
8743
"call-bind": "^1.0.8",
10177
-
"call-bound": "^1.0.4",
8744
+
"call-bound": "^1.0.3",
10178
8745
"data-view-buffer": "^1.0.2",
10179
8746
"data-view-byte-length": "^1.0.2",
10180
8747
"data-view-byte-offset": "^1.0.1",
10181
8748
"es-define-property": "^1.0.1",
10182
8749
"es-errors": "^1.3.0",
10183
-
"es-object-atoms": "^1.1.1",
8750
+
"es-object-atoms": "^1.0.0",
10184
8751
"es-set-tostringtag": "^2.1.0",
10185
8752
"es-to-primitive": "^1.3.0",
10186
8753
"function.prototype.name": "^1.1.8",
10187
-
"get-intrinsic": "^1.3.0",
10188
-
"get-proto": "^1.0.1",
8754
+
"get-intrinsic": "^1.2.7",
8755
+
"get-proto": "^1.0.0",
10189
8756
"get-symbol-description": "^1.1.0",
10190
8757
"globalthis": "^1.0.4",
10191
8758
"gopd": "^1.2.0",
···
10197
8764
"is-array-buffer": "^3.0.5",
10198
8765
"is-callable": "^1.2.7",
10199
8766
"is-data-view": "^1.0.2",
10200
-
"is-negative-zero": "^2.0.3",
10201
8767
"is-regex": "^1.2.1",
10202
-
"is-set": "^2.0.3",
10203
8768
"is-shared-array-buffer": "^1.0.4",
10204
8769
"is-string": "^1.1.1",
10205
8770
"is-typed-array": "^1.1.15",
10206
-
"is-weakref": "^1.1.1",
8771
+
"is-weakref": "^1.1.0",
10207
8772
"math-intrinsics": "^1.1.0",
10208
-
"object-inspect": "^1.13.4",
8773
+
"object-inspect": "^1.13.3",
10209
8774
"object-keys": "^1.1.1",
10210
8775
"object.assign": "^4.1.7",
10211
8776
"own-keys": "^1.0.1",
10212
-
"regexp.prototype.flags": "^1.5.4",
8777
+
"regexp.prototype.flags": "^1.5.3",
10213
8778
"safe-array-concat": "^1.1.3",
10214
8779
"safe-push-apply": "^1.0.0",
10215
8780
"safe-regex-test": "^1.1.0",
10216
8781
"set-proto": "^1.0.0",
10217
-
"stop-iteration-iterator": "^1.1.0",
10218
8782
"string.prototype.trim": "^1.2.10",
10219
8783
"string.prototype.trimend": "^1.0.9",
10220
8784
"string.prototype.trimstart": "^1.0.8",
···
10223
8787
"typed-array-byte-offset": "^1.0.4",
10224
8788
"typed-array-length": "^1.0.7",
10225
8789
"unbox-primitive": "^1.1.0",
10226
-
"which-typed-array": "^1.1.19"
8790
+
"which-typed-array": "^1.1.18"
10227
8791
},
10228
8792
"engines": {
10229
8793
"node": ">= 0.4"
···
10306
8870
}
10307
8871
},
10308
8872
"node_modules/es-shim-unscopables": {
10309
-
"version": "1.1.0",
10310
-
"resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz",
10311
-
"integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==",
8873
+
"version": "1.0.2",
8874
+
"resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz",
8875
+
"integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==",
10312
8876
"dev": true,
10313
8877
"dependencies": {
10314
-
"hasown": "^2.0.2"
10315
-
},
10316
-
"engines": {
10317
-
"node": ">= 0.4"
8878
+
"hasown": "^2.0.0"
10318
8879
}
10319
8880
},
10320
8881
"node_modules/es-to-primitive": {
···
10470
9031
"esbuild": ">=0.12 <1"
10471
9032
}
10472
9033
},
10473
-
"node_modules/esbuild/node_modules/@esbuild/darwin-arm64": {
10474
-
"version": "0.25.4",
10475
-
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz",
10476
-
"integrity": "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==",
10477
-
"cpu": [
10478
-
"arm64"
10479
-
],
10480
-
"dev": true,
10481
-
"optional": true,
10482
-
"os": [
10483
-
"darwin"
10484
-
],
10485
-
"engines": {
10486
-
"node": ">=18"
10487
-
}
10488
-
},
10489
9034
"node_modules/escalade": {
10490
-
"version": "3.2.0",
10491
-
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
10492
-
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
9035
+
"version": "3.1.2",
9036
+
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
9037
+
"integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
10493
9038
"engines": {
10494
9039
"node": ">=6"
10495
9040
}
···
10512
9057
}
10513
9058
},
10514
9059
"node_modules/eslint": {
10515
-
"version": "9.39.1",
10516
-
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz",
10517
-
"integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
9060
+
"version": "8.57.0",
9061
+
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
9062
+
"integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==",
10518
9063
"dev": true,
10519
9064
"dependencies": {
10520
-
"@eslint-community/eslint-utils": "^4.8.0",
10521
-
"@eslint-community/regexpp": "^4.12.1",
10522
-
"@eslint/config-array": "^0.21.1",
10523
-
"@eslint/config-helpers": "^0.4.2",
10524
-
"@eslint/core": "^0.17.0",
10525
-
"@eslint/eslintrc": "^3.3.1",
10526
-
"@eslint/js": "9.39.1",
10527
-
"@eslint/plugin-kit": "^0.4.1",
10528
-
"@humanfs/node": "^0.16.6",
9065
+
"@eslint-community/eslint-utils": "^4.2.0",
9066
+
"@eslint-community/regexpp": "^4.6.1",
9067
+
"@eslint/eslintrc": "^2.1.4",
9068
+
"@eslint/js": "8.57.0",
9069
+
"@humanwhocodes/config-array": "^0.11.14",
10529
9070
"@humanwhocodes/module-importer": "^1.0.1",
10530
-
"@humanwhocodes/retry": "^0.4.2",
10531
-
"@types/estree": "^1.0.6",
9071
+
"@nodelib/fs.walk": "^1.2.8",
9072
+
"@ungap/structured-clone": "^1.2.0",
10532
9073
"ajv": "^6.12.4",
10533
9074
"chalk": "^4.0.0",
10534
-
"cross-spawn": "^7.0.6",
9075
+
"cross-spawn": "^7.0.2",
10535
9076
"debug": "^4.3.2",
9077
+
"doctrine": "^3.0.0",
10536
9078
"escape-string-regexp": "^4.0.0",
10537
-
"eslint-scope": "^8.4.0",
10538
-
"eslint-visitor-keys": "^4.2.1",
10539
-
"espree": "^10.4.0",
10540
-
"esquery": "^1.5.0",
9079
+
"eslint-scope": "^7.2.2",
9080
+
"eslint-visitor-keys": "^3.4.3",
9081
+
"espree": "^9.6.1",
9082
+
"esquery": "^1.4.2",
10541
9083
"esutils": "^2.0.2",
10542
9084
"fast-deep-equal": "^3.1.3",
10543
-
"file-entry-cache": "^8.0.0",
9085
+
"file-entry-cache": "^6.0.1",
10544
9086
"find-up": "^5.0.0",
10545
9087
"glob-parent": "^6.0.2",
9088
+
"globals": "^13.19.0",
9089
+
"graphemer": "^1.4.0",
10546
9090
"ignore": "^5.2.0",
10547
9091
"imurmurhash": "^0.1.4",
10548
9092
"is-glob": "^4.0.0",
9093
+
"is-path-inside": "^3.0.3",
9094
+
"js-yaml": "^4.1.0",
10549
9095
"json-stable-stringify-without-jsonify": "^1.0.1",
9096
+
"levn": "^0.4.1",
10550
9097
"lodash.merge": "^4.6.2",
10551
9098
"minimatch": "^3.1.2",
10552
9099
"natural-compare": "^1.4.0",
10553
-
"optionator": "^0.9.3"
9100
+
"optionator": "^0.9.3",
9101
+
"strip-ansi": "^6.0.1",
9102
+
"text-table": "^0.2.0"
10554
9103
},
10555
9104
"bin": {
10556
9105
"eslint": "bin/eslint.js"
10557
9106
},
10558
9107
"engines": {
10559
-
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
9108
+
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
10560
9109
},
10561
9110
"funding": {
10562
-
"url": "https://eslint.org/donate"
10563
-
},
10564
-
"peerDependencies": {
10565
-
"jiti": "*"
10566
-
},
10567
-
"peerDependenciesMeta": {
10568
-
"jiti": {
10569
-
"optional": true
10570
-
}
9111
+
"url": "https://opencollective.com/eslint"
10571
9112
}
10572
9113
},
10573
9114
"node_modules/eslint-config-next": {
10574
-
"version": "16.0.3",
10575
-
"resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.0.3.tgz",
10576
-
"integrity": "sha512-5F6qDjcZldf0Y0ZbqvWvap9xzYUxyDf7/of37aeyhvkrQokj/4bT1JYWZdlWUr283aeVa+s52mPq9ogmGg+5dw==",
9115
+
"version": "15.5.3",
9116
+
"resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.5.3.tgz",
9117
+
"integrity": "sha512-e6j+QhQFOr5pfsc8VJbuTD9xTXJaRvMHYjEeLPA2pFkheNlgPLCkxdvhxhfuM4KGcqSZj2qEnpHisdTVs3BxuQ==",
10577
9118
"dev": true,
9119
+
"license": "MIT",
10578
9120
"dependencies": {
10579
-
"@next/eslint-plugin-next": "16.0.3",
9121
+
"@next/eslint-plugin-next": "15.5.3",
9122
+
"@rushstack/eslint-patch": "^1.10.3",
9123
+
"@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0",
9124
+
"@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0",
10580
9125
"eslint-import-resolver-node": "^0.3.6",
10581
9126
"eslint-import-resolver-typescript": "^3.5.2",
10582
-
"eslint-plugin-import": "^2.32.0",
9127
+
"eslint-plugin-import": "^2.31.0",
10583
9128
"eslint-plugin-jsx-a11y": "^6.10.0",
10584
9129
"eslint-plugin-react": "^7.37.0",
10585
-
"eslint-plugin-react-hooks": "^7.0.0",
10586
-
"globals": "16.4.0",
10587
-
"typescript-eslint": "^8.46.0"
9130
+
"eslint-plugin-react-hooks": "^5.0.0"
10588
9131
},
10589
9132
"peerDependencies": {
10590
-
"eslint": ">=9.0.0",
9133
+
"eslint": "^7.23.0 || ^8.0.0 || ^9.0.0",
10591
9134
"typescript": ">=3.3.1"
10592
9135
},
10593
9136
"peerDependenciesMeta": {
10594
9137
"typescript": {
10595
9138
"optional": true
10596
9139
}
10597
-
}
10598
-
},
10599
-
"node_modules/eslint-config-next/node_modules/globals": {
10600
-
"version": "16.4.0",
10601
-
"resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz",
10602
-
"integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==",
10603
-
"dev": true,
10604
-
"engines": {
10605
-
"node": ">=18"
10606
-
},
10607
-
"funding": {
10608
-
"url": "https://github.com/sponsors/sindresorhus"
10609
9140
}
10610
9141
},
10611
9142
"node_modules/eslint-import-resolver-node": {
···
10654
9185
}
10655
9186
},
10656
9187
"node_modules/eslint-module-utils": {
10657
-
"version": "2.12.1",
10658
-
"resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz",
10659
-
"integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==",
9188
+
"version": "2.12.0",
9189
+
"resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz",
9190
+
"integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==",
10660
9191
"dev": true,
9192
+
"license": "MIT",
10661
9193
"dependencies": {
10662
9194
"debug": "^3.2.7"
10663
9195
},
···
10680
9212
}
10681
9213
},
10682
9214
"node_modules/eslint-plugin-import": {
10683
-
"version": "2.32.0",
10684
-
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz",
10685
-
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
9215
+
"version": "2.31.0",
9216
+
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz",
9217
+
"integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==",
10686
9218
"dev": true,
9219
+
"license": "MIT",
10687
9220
"dependencies": {
10688
9221
"@rtsao/scc": "^1.1.0",
10689
-
"array-includes": "^3.1.9",
10690
-
"array.prototype.findlastindex": "^1.2.6",
10691
-
"array.prototype.flat": "^1.3.3",
10692
-
"array.prototype.flatmap": "^1.3.3",
9222
+
"array-includes": "^3.1.8",
9223
+
"array.prototype.findlastindex": "^1.2.5",
9224
+
"array.prototype.flat": "^1.3.2",
9225
+
"array.prototype.flatmap": "^1.3.2",
10693
9226
"debug": "^3.2.7",
10694
9227
"doctrine": "^2.1.0",
10695
9228
"eslint-import-resolver-node": "^0.3.9",
10696
-
"eslint-module-utils": "^2.12.1",
9229
+
"eslint-module-utils": "^2.12.0",
10697
9230
"hasown": "^2.0.2",
10698
-
"is-core-module": "^2.16.1",
9231
+
"is-core-module": "^2.15.1",
10699
9232
"is-glob": "^4.0.3",
10700
9233
"minimatch": "^3.1.2",
10701
9234
"object.fromentries": "^2.0.8",
10702
9235
"object.groupby": "^1.0.3",
10703
-
"object.values": "^1.2.1",
9236
+
"object.values": "^1.2.0",
10704
9237
"semver": "^6.3.1",
10705
-
"string.prototype.trimend": "^1.0.9",
9238
+
"string.prototype.trimend": "^1.0.8",
10706
9239
"tsconfig-paths": "^3.15.0"
10707
9240
},
10708
9241
"engines": {
···
10806
9339
}
10807
9340
},
10808
9341
"node_modules/eslint-plugin-react-hooks": {
10809
-
"version": "7.0.1",
10810
-
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz",
10811
-
"integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==",
9342
+
"version": "5.2.0",
9343
+
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz",
9344
+
"integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==",
10812
9345
"dev": true,
10813
-
"dependencies": {
10814
-
"@babel/core": "^7.24.4",
10815
-
"@babel/parser": "^7.24.4",
10816
-
"hermes-parser": "^0.25.1",
10817
-
"zod": "^3.25.0 || ^4.0.0",
10818
-
"zod-validation-error": "^3.5.0 || ^4.0.0"
10819
-
},
9346
+
"license": "MIT",
10820
9347
"engines": {
10821
-
"node": ">=18"
9348
+
"node": ">=10"
10822
9349
},
10823
9350
"peerDependencies": {
10824
9351
"eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
10825
9352
}
10826
9353
},
10827
-
"node_modules/eslint-plugin-react-hooks/node_modules/zod": {
10828
-
"version": "4.1.12",
10829
-
"resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz",
10830
-
"integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==",
10831
-
"dev": true,
10832
-
"funding": {
10833
-
"url": "https://github.com/sponsors/colinhacks"
10834
-
}
10835
-
},
10836
-
"node_modules/eslint-plugin-react-hooks/node_modules/zod-validation-error": {
10837
-
"version": "4.0.2",
10838
-
"resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz",
10839
-
"integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==",
10840
-
"dev": true,
10841
-
"engines": {
10842
-
"node": ">=18.0.0"
10843
-
},
10844
-
"peerDependencies": {
10845
-
"zod": "^3.25.0 || ^4.0.0"
10846
-
}
10847
-
},
10848
9354
"node_modules/eslint-plugin-react/node_modules/doctrine": {
10849
9355
"version": "2.1.0",
10850
9356
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
···
10887
9393
}
10888
9394
},
10889
9395
"node_modules/eslint-scope": {
10890
-
"version": "8.4.0",
10891
-
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
10892
-
"integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
9396
+
"version": "7.2.2",
9397
+
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
9398
+
"integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
10893
9399
"dev": true,
10894
9400
"dependencies": {
10895
9401
"esrecurse": "^4.3.0",
10896
9402
"estraverse": "^5.2.0"
10897
9403
},
10898
9404
"engines": {
10899
-
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
9405
+
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
10900
9406
},
10901
9407
"funding": {
10902
9408
"url": "https://opencollective.com/eslint"
10903
9409
}
10904
9410
},
10905
9411
"node_modules/eslint-visitor-keys": {
10906
-
"version": "4.2.1",
10907
-
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
10908
-
"integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
9412
+
"version": "3.4.3",
9413
+
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
9414
+
"integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
10909
9415
"dev": true,
10910
9416
"engines": {
10911
-
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
9417
+
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
10912
9418
},
10913
9419
"funding": {
10914
9420
"url": "https://opencollective.com/eslint"
···
10930
9436
}
10931
9437
},
10932
9438
"node_modules/espree": {
10933
-
"version": "10.4.0",
10934
-
"resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
10935
-
"integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
9439
+
"version": "9.6.1",
9440
+
"resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
9441
+
"integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
10936
9442
"dev": true,
10937
9443
"dependencies": {
10938
-
"acorn": "^8.15.0",
9444
+
"acorn": "^8.9.0",
10939
9445
"acorn-jsx": "^5.3.2",
10940
-
"eslint-visitor-keys": "^4.2.1"
9446
+
"eslint-visitor-keys": "^3.4.1"
10941
9447
},
10942
9448
"engines": {
10943
-
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
9449
+
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
10944
9450
},
10945
9451
"funding": {
10946
9452
"url": "https://opencollective.com/eslint"
···
11296
9802
}
11297
9803
},
11298
9804
"node_modules/fast-uri": {
11299
-
"version": "3.1.0",
11300
-
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
11301
-
"integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
9805
+
"version": "3.0.5",
9806
+
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.5.tgz",
9807
+
"integrity": "sha512-5JnBCWpFlMo0a3ciDy/JckMzzv1U9coZrIhedq+HXxxUfDTAiS0LA8OKVao4G9BxmCVck/jtA5r3KAtRWEyD8Q==",
11302
9808
"dev": true,
11303
9809
"funding": [
11304
9810
{
···
11309
9815
"type": "opencollective",
11310
9816
"url": "https://opencollective.com/fastify"
11311
9817
}
11312
-
]
9818
+
],
9819
+
"license": "BSD-3-Clause"
11313
9820
},
11314
9821
"node_modules/fastq": {
11315
9822
"version": "1.17.1",
···
11357
9864
}
11358
9865
},
11359
9866
"node_modules/file-entry-cache": {
11360
-
"version": "8.0.0",
11361
-
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
11362
-
"integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
9867
+
"version": "6.0.1",
9868
+
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
9869
+
"integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
11363
9870
"dev": true,
11364
9871
"dependencies": {
11365
-
"flat-cache": "^4.0.0"
9872
+
"flat-cache": "^3.0.4"
11366
9873
},
11367
9874
"engines": {
11368
-
"node": ">=16.0.0"
9875
+
"node": "^10.12.0 || >=12.0.0"
11369
9876
}
11370
9877
},
11371
9878
"node_modules/fill-range": {
···
11430
9937
}
11431
9938
},
11432
9939
"node_modules/flat-cache": {
11433
-
"version": "4.0.1",
11434
-
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
11435
-
"integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
9940
+
"version": "3.2.0",
9941
+
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
9942
+
"integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
11436
9943
"dev": true,
11437
9944
"dependencies": {
11438
9945
"flatted": "^3.2.9",
11439
-
"keyv": "^4.5.4"
9946
+
"keyv": "^4.5.3",
9947
+
"rimraf": "^3.0.2"
11440
9948
},
11441
9949
"engines": {
11442
-
"node": ">=16"
9950
+
"node": "^10.12.0 || >=12.0.0"
11443
9951
}
11444
9952
},
11445
9953
"node_modules/flatted": {
11446
-
"version": "3.3.3",
11447
-
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
11448
-
"integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
9954
+
"version": "3.3.1",
9955
+
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz",
9956
+
"integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
11449
9957
"dev": true
11450
9958
},
11451
9959
"node_modules/follow-redirects": {
···
11647
10155
"node": ">=14"
11648
10156
}
11649
10157
},
11650
-
"node_modules/gensync": {
11651
-
"version": "1.0.0-beta.2",
11652
-
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
11653
-
"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
11654
-
"dev": true,
11655
-
"engines": {
11656
-
"node": ">=6.9.0"
11657
-
}
11658
-
},
11659
10158
"node_modules/get-caller-file": {
11660
10159
"version": "2.0.5",
11661
10160
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
···
11817
10316
}
11818
10317
},
11819
10318
"node_modules/globals": {
11820
-
"version": "14.0.0",
11821
-
"resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
11822
-
"integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
10319
+
"version": "13.24.0",
10320
+
"resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
10321
+
"integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
11823
10322
"dev": true,
10323
+
"dependencies": {
10324
+
"type-fest": "^0.20.2"
10325
+
},
11824
10326
"engines": {
11825
-
"node": ">=18"
10327
+
"node": ">=8"
11826
10328
},
11827
10329
"funding": {
11828
10330
"url": "https://github.com/sponsors/sindresorhus"
···
12282
10784
"integrity": "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==",
12283
10785
"dev": true
12284
10786
},
12285
-
"node_modules/hermes-estree": {
12286
-
"version": "0.25.1",
12287
-
"resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz",
12288
-
"integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==",
12289
-
"dev": true
12290
-
},
12291
-
"node_modules/hermes-parser": {
12292
-
"version": "0.25.1",
12293
-
"resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz",
12294
-
"integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==",
12295
-
"dev": true,
12296
-
"dependencies": {
12297
-
"hermes-estree": "0.25.1"
12298
-
}
12299
-
},
12300
10787
"node_modules/hono": {
12301
10788
"version": "4.7.11",
12302
10789
"resolved": "https://registry.npmjs.org/hono/-/hono-4.7.11.tgz",
···
12401
10888
}
12402
10889
},
12403
10890
"node_modules/import-fresh": {
12404
-
"version": "3.3.1",
12405
-
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
12406
-
"integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
10891
+
"version": "3.3.0",
10892
+
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
10893
+
"integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
12407
10894
"dev": true,
12408
10895
"dependencies": {
12409
10896
"parent-module": "^1.0.0",
···
12888
11375
"url": "https://github.com/sponsors/ljharb"
12889
11376
}
12890
11377
},
12891
-
"node_modules/is-negative-zero": {
12892
-
"version": "2.0.3",
12893
-
"resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz",
12894
-
"integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==",
12895
-
"dev": true,
12896
-
"engines": {
12897
-
"node": ">= 0.4"
12898
-
},
12899
-
"funding": {
12900
-
"url": "https://github.com/sponsors/ljharb"
12901
-
}
12902
-
},
12903
11378
"node_modules/is-number": {
12904
11379
"version": "7.0.0",
12905
11380
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
···
12924
11399
},
12925
11400
"funding": {
12926
11401
"url": "https://github.com/sponsors/ljharb"
11402
+
}
11403
+
},
11404
+
"node_modules/is-path-inside": {
11405
+
"version": "3.0.3",
11406
+
"resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
11407
+
"integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
11408
+
"dev": true,
11409
+
"engines": {
11410
+
"node": ">=8"
12927
11411
}
12928
11412
},
12929
11413
"node_modules/is-plain-obj": {
···
13182
11666
"license": "MIT"
13183
11667
},
13184
11668
"node_modules/js-yaml": {
13185
-
"version": "4.1.1",
13186
-
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
13187
-
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
11669
+
"version": "4.1.0",
11670
+
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
11671
+
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
13188
11672
"dev": true,
13189
11673
"dependencies": {
13190
11674
"argparse": "^2.0.1"
13191
11675
},
13192
11676
"bin": {
13193
11677
"js-yaml": "bin/js-yaml.js"
13194
-
}
13195
-
},
13196
-
"node_modules/jsesc": {
13197
-
"version": "3.1.0",
13198
-
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
13199
-
"integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
13200
-
"dev": true,
13201
-
"bin": {
13202
-
"jsesc": "bin/jsesc"
13203
-
},
13204
-
"engines": {
13205
-
"node": ">=6"
13206
11678
}
13207
11679
},
13208
11680
"node_modules/json-bigint": {
···
13241
11713
"version": "1.0.0",
13242
11714
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
13243
11715
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
13244
-
"dev": true
11716
+
"dev": true,
11717
+
"license": "MIT"
13245
11718
},
13246
11719
"node_modules/json-stable-stringify-without-jsonify": {
13247
11720
"version": "1.0.1",
···
15108
13581
}
15109
13582
},
15110
13583
"node_modules/next": {
15111
-
"version": "16.0.3",
15112
-
"resolved": "https://registry.npmjs.org/next/-/next-16.0.3.tgz",
15113
-
"integrity": "sha512-Ka0/iNBblPFcIubTA1Jjh6gvwqfjrGq1Y2MTI5lbjeLIAfmC+p5bQmojpRZqgHHVu5cG4+qdIiwXiBSm/8lZ3w==",
13584
+
"version": "15.5.3",
13585
+
"resolved": "https://registry.npmjs.org/next/-/next-15.5.3.tgz",
13586
+
"integrity": "sha512-r/liNAx16SQj4D+XH/oI1dlpv9tdKJ6cONYPwwcCC46f2NjpaRWY+EKCzULfgQYV6YKXjHBchff2IZBSlZmJNw==",
13587
+
"license": "MIT",
15114
13588
"dependencies": {
15115
-
"@next/env": "16.0.3",
13589
+
"@next/env": "15.5.3",
15116
13590
"@swc/helpers": "0.5.15",
15117
13591
"caniuse-lite": "^1.0.30001579",
15118
13592
"postcss": "8.4.31",
···
15122
13596
"next": "dist/bin/next"
15123
13597
},
15124
13598
"engines": {
15125
-
"node": ">=20.9.0"
13599
+
"node": "^18.18.0 || ^19.8.0 || >= 20.0.0"
15126
13600
},
15127
13601
"optionalDependencies": {
15128
-
"@next/swc-darwin-arm64": "16.0.3",
15129
-
"@next/swc-darwin-x64": "16.0.3",
15130
-
"@next/swc-linux-arm64-gnu": "16.0.3",
15131
-
"@next/swc-linux-arm64-musl": "16.0.3",
15132
-
"@next/swc-linux-x64-gnu": "16.0.3",
15133
-
"@next/swc-linux-x64-musl": "16.0.3",
15134
-
"@next/swc-win32-arm64-msvc": "16.0.3",
15135
-
"@next/swc-win32-x64-msvc": "16.0.3",
15136
-
"sharp": "^0.34.4"
13602
+
"@next/swc-darwin-arm64": "15.5.3",
13603
+
"@next/swc-darwin-x64": "15.5.3",
13604
+
"@next/swc-linux-arm64-gnu": "15.5.3",
13605
+
"@next/swc-linux-arm64-musl": "15.5.3",
13606
+
"@next/swc-linux-x64-gnu": "15.5.3",
13607
+
"@next/swc-linux-x64-musl": "15.5.3",
13608
+
"@next/swc-win32-arm64-msvc": "15.5.3",
13609
+
"@next/swc-win32-x64-msvc": "15.5.3",
13610
+
"sharp": "^0.34.3"
15137
13611
},
15138
13612
"peerDependencies": {
15139
13613
"@opentelemetry/api": "^1.1.0",
···
15257
13731
"node-gyp-build-optional-packages-optional": "optional.js",
15258
13732
"node-gyp-build-optional-packages-test": "build-test.js"
15259
13733
}
15260
-
},
15261
-
"node_modules/node-releases": {
15262
-
"version": "2.0.27",
15263
-
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
15264
-
"integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
15265
-
"dev": true
15266
13734
},
15267
13735
"node_modules/normalize-path": {
15268
13736
"version": "3.0.0",
···
15631
14099
"dev": true,
15632
14100
"engines": {
15633
14101
"node": ">=8"
14102
+
}
14103
+
},
14104
+
"node_modules/path-is-absolute": {
14105
+
"version": "1.0.1",
14106
+
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
14107
+
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
14108
+
"dev": true,
14109
+
"engines": {
14110
+
"node": ">=0.10.0"
15634
14111
}
15635
14112
},
15636
14113
"node_modules/path-key": {
···
16321
14798
}
16322
14799
},
16323
14800
"node_modules/react": {
16324
-
"version": "19.2.0",
16325
-
"resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
16326
-
"integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
14801
+
"version": "19.1.1",
14802
+
"resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz",
14803
+
"integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==",
14804
+
"license": "MIT",
16327
14805
"engines": {
16328
14806
"node": ">=0.10.0"
16329
14807
}
···
16442
14920
}
16443
14921
},
16444
14922
"node_modules/react-dom": {
16445
-
"version": "19.2.0",
16446
-
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz",
16447
-
"integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==",
14923
+
"version": "19.1.1",
14924
+
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz",
14925
+
"integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==",
14926
+
"license": "MIT",
16448
14927
"dependencies": {
16449
-
"scheduler": "^0.27.0"
14928
+
"scheduler": "^0.26.0"
16450
14929
},
16451
14930
"peerDependencies": {
16452
-
"react": "^19.2.0"
14931
+
"react": "^19.1.1"
16453
14932
}
16454
14933
},
16455
14934
"node_modules/react-is": {
···
16962
15441
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
16963
15442
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
16964
15443
"dev": true,
15444
+
"license": "MIT",
16965
15445
"engines": {
16966
15446
"node": ">=0.10.0"
16967
15447
}
···
17033
15513
"node": ">=0.10.0"
17034
15514
}
17035
15515
},
15516
+
"node_modules/rimraf": {
15517
+
"version": "3.0.2",
15518
+
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
15519
+
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
15520
+
"deprecated": "Rimraf versions prior to v4 are no longer supported",
15521
+
"dev": true,
15522
+
"dependencies": {
15523
+
"glob": "^7.1.3"
15524
+
},
15525
+
"bin": {
15526
+
"rimraf": "bin.js"
15527
+
},
15528
+
"funding": {
15529
+
"url": "https://github.com/sponsors/isaacs"
15530
+
}
15531
+
},
15532
+
"node_modules/rimraf/node_modules/glob": {
15533
+
"version": "7.2.3",
15534
+
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
15535
+
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
15536
+
"deprecated": "Glob versions prior to v9 are no longer supported",
15537
+
"dev": true,
15538
+
"dependencies": {
15539
+
"fs.realpath": "^1.0.0",
15540
+
"inflight": "^1.0.4",
15541
+
"inherits": "2",
15542
+
"minimatch": "^3.1.1",
15543
+
"once": "^1.3.0",
15544
+
"path-is-absolute": "^1.0.0"
15545
+
},
15546
+
"engines": {
15547
+
"node": "*"
15548
+
},
15549
+
"funding": {
15550
+
"url": "https://github.com/sponsors/isaacs"
15551
+
}
15552
+
},
17036
15553
"node_modules/rollup-plugin-inject": {
17037
15554
"version": "3.0.2",
17038
15555
"resolved": "https://registry.npmjs.org/rollup-plugin-inject/-/rollup-plugin-inject-3.0.2.tgz",
···
17190
15707
"license": "ISC"
17191
15708
},
17192
15709
"node_modules/scheduler": {
17193
-
"version": "0.27.0",
17194
-
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
17195
-
"integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="
15710
+
"version": "0.26.0",
15711
+
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz",
15712
+
"integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==",
15713
+
"license": "MIT"
17196
15714
},
17197
15715
"node_modules/scmp": {
17198
15716
"version": "2.1.0",
···
17637
16155
"node": ">= 0.8"
17638
16156
}
17639
16157
},
17640
-
"node_modules/stop-iteration-iterator": {
17641
-
"version": "1.1.0",
17642
-
"resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
17643
-
"integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==",
17644
-
"dev": true,
17645
-
"dependencies": {
17646
-
"es-errors": "^1.3.0",
17647
-
"internal-slot": "^1.1.0"
17648
-
},
17649
-
"engines": {
17650
-
"node": ">= 0.4"
17651
-
}
17652
-
},
17653
16158
"node_modules/stoppable": {
17654
16159
"version": "1.1.0",
17655
16160
"resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz",
···
17977
16482
"integrity": "sha512-lDMFv4nKQrSjlkHKAlHVqKrBG4DyFfa9F74cmBZ3Iy3ed8yvWnlWSIdi4IKfSqwmazAohBNwiN64qGx4y5Q3IQ==",
17978
16483
"license": "ISC"
17979
16484
},
16485
+
"node_modules/text-table": {
16486
+
"version": "0.2.0",
16487
+
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
16488
+
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
16489
+
"dev": true
16490
+
},
17980
16491
"node_modules/thread-stream": {
17981
16492
"version": "2.7.0",
17982
16493
"resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.7.0.tgz",
···
18132
16643
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
18133
16644
"integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
18134
16645
"dev": true,
16646
+
"license": "MIT",
18135
16647
"engines": {
18136
16648
"node": ">=18.12"
18137
16649
},
···
18249
16761
"node": ">= 0.8.0"
18250
16762
}
18251
16763
},
16764
+
"node_modules/type-fest": {
16765
+
"version": "0.20.2",
16766
+
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
16767
+
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
16768
+
"dev": true,
16769
+
"engines": {
16770
+
"node": ">=10"
16771
+
},
16772
+
"funding": {
16773
+
"url": "https://github.com/sponsors/sindresorhus"
16774
+
}
16775
+
},
18252
16776
"node_modules/type-is": {
18253
16777
"version": "1.6.18",
18254
16778
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
···
18351
16875
},
18352
16876
"engines": {
18353
16877
"node": ">=14.17"
18354
-
}
18355
-
},
18356
-
"node_modules/typescript-eslint": {
18357
-
"version": "8.47.0",
18358
-
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.47.0.tgz",
18359
-
"integrity": "sha512-Lwe8i2XQ3WoMjua/r1PHrCTpkubPYJCAfOurtn+mtTzqB6jNd+14n9UN1bJ4s3F49x9ixAm0FLflB/JzQ57M8Q==",
18360
-
"dev": true,
18361
-
"dependencies": {
18362
-
"@typescript-eslint/eslint-plugin": "8.47.0",
18363
-
"@typescript-eslint/parser": "8.47.0",
18364
-
"@typescript-eslint/typescript-estree": "8.47.0",
18365
-
"@typescript-eslint/utils": "8.47.0"
18366
-
},
18367
-
"engines": {
18368
-
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
18369
-
},
18370
-
"funding": {
18371
-
"type": "opencollective",
18372
-
"url": "https://opencollective.com/typescript-eslint"
18373
-
},
18374
-
"peerDependencies": {
18375
-
"eslint": "^8.57.0 || ^9.0.0",
18376
-
"typescript": ">=4.8.4 <6.0.0"
18377
16878
}
18378
16879
},
18379
16880
"node_modules/uc.micro": {
···
18547
17048
"node": ">= 0.8"
18548
17049
}
18549
17050
},
18550
-
"node_modules/update-browserslist-db": {
18551
-
"version": "1.1.4",
18552
-
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz",
18553
-
"integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==",
18554
-
"dev": true,
18555
-
"funding": [
18556
-
{
18557
-
"type": "opencollective",
18558
-
"url": "https://opencollective.com/browserslist"
18559
-
},
18560
-
{
18561
-
"type": "tidelift",
18562
-
"url": "https://tidelift.com/funding/github/npm/browserslist"
18563
-
},
18564
-
{
18565
-
"type": "github",
18566
-
"url": "https://github.com/sponsors/ai"
18567
-
}
18568
-
],
18569
-
"dependencies": {
18570
-
"escalade": "^3.2.0",
18571
-
"picocolors": "^1.1.1"
18572
-
},
18573
-
"bin": {
18574
-
"update-browserslist-db": "cli.js"
18575
-
},
18576
-
"peerDependencies": {
18577
-
"browserslist": ">= 4.21.0"
18578
-
}
18579
-
},
18580
17051
"node_modules/use-callback-ref": {
18581
17052
"version": "1.3.3",
18582
17053
"resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz",
···
18986
17457
}
18987
17458
}
18988
17459
},
18989
-
"node_modules/wrangler/node_modules/@esbuild/android-arm": {
18990
-
"version": "0.17.19",
18991
-
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz",
18992
-
"integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==",
18993
-
"cpu": [
18994
-
"arm"
18995
-
],
18996
-
"dev": true,
18997
-
"optional": true,
18998
-
"os": [
18999
-
"android"
19000
-
],
19001
-
"engines": {
19002
-
"node": ">=12"
19003
-
}
19004
-
},
19005
-
"node_modules/wrangler/node_modules/@esbuild/android-arm64": {
19006
-
"version": "0.17.19",
19007
-
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz",
19008
-
"integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==",
19009
-
"cpu": [
19010
-
"arm64"
19011
-
],
19012
-
"dev": true,
19013
-
"optional": true,
19014
-
"os": [
19015
-
"android"
19016
-
],
19017
-
"engines": {
19018
-
"node": ">=12"
19019
-
}
19020
-
},
19021
-
"node_modules/wrangler/node_modules/@esbuild/android-x64": {
19022
-
"version": "0.17.19",
19023
-
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz",
19024
-
"integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==",
19025
-
"cpu": [
19026
-
"x64"
19027
-
],
19028
-
"dev": true,
19029
-
"optional": true,
19030
-
"os": [
19031
-
"android"
19032
-
],
19033
-
"engines": {
19034
-
"node": ">=12"
19035
-
}
19036
-
},
19037
-
"node_modules/wrangler/node_modules/@esbuild/darwin-arm64": {
19038
-
"version": "0.17.19",
19039
-
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz",
19040
-
"integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==",
19041
-
"cpu": [
19042
-
"arm64"
19043
-
],
19044
-
"dev": true,
19045
-
"optional": true,
19046
-
"os": [
19047
-
"darwin"
19048
-
],
19049
-
"engines": {
19050
-
"node": ">=12"
19051
-
}
19052
-
},
19053
-
"node_modules/wrangler/node_modules/@esbuild/darwin-x64": {
19054
-
"version": "0.17.19",
19055
-
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz",
19056
-
"integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==",
19057
-
"cpu": [
19058
-
"x64"
19059
-
],
19060
-
"dev": true,
19061
-
"optional": true,
19062
-
"os": [
19063
-
"darwin"
19064
-
],
19065
-
"engines": {
19066
-
"node": ">=12"
19067
-
}
19068
-
},
19069
-
"node_modules/wrangler/node_modules/@esbuild/freebsd-arm64": {
19070
-
"version": "0.17.19",
19071
-
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz",
19072
-
"integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==",
19073
-
"cpu": [
19074
-
"arm64"
19075
-
],
19076
-
"dev": true,
19077
-
"optional": true,
19078
-
"os": [
19079
-
"freebsd"
19080
-
],
19081
-
"engines": {
19082
-
"node": ">=12"
19083
-
}
19084
-
},
19085
-
"node_modules/wrangler/node_modules/@esbuild/freebsd-x64": {
19086
-
"version": "0.17.19",
19087
-
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz",
19088
-
"integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==",
19089
-
"cpu": [
19090
-
"x64"
19091
-
],
19092
-
"dev": true,
19093
-
"optional": true,
19094
-
"os": [
19095
-
"freebsd"
19096
-
],
19097
-
"engines": {
19098
-
"node": ">=12"
19099
-
}
19100
-
},
19101
-
"node_modules/wrangler/node_modules/@esbuild/linux-arm": {
19102
-
"version": "0.17.19",
19103
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz",
19104
-
"integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==",
19105
-
"cpu": [
19106
-
"arm"
19107
-
],
19108
-
"dev": true,
19109
-
"optional": true,
19110
-
"os": [
19111
-
"linux"
19112
-
],
19113
-
"engines": {
19114
-
"node": ">=12"
19115
-
}
19116
-
},
19117
-
"node_modules/wrangler/node_modules/@esbuild/linux-arm64": {
19118
-
"version": "0.17.19",
19119
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz",
19120
-
"integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==",
19121
-
"cpu": [
19122
-
"arm64"
19123
-
],
19124
-
"dev": true,
19125
-
"optional": true,
19126
-
"os": [
19127
-
"linux"
19128
-
],
19129
-
"engines": {
19130
-
"node": ">=12"
19131
-
}
19132
-
},
19133
-
"node_modules/wrangler/node_modules/@esbuild/linux-ia32": {
19134
-
"version": "0.17.19",
19135
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz",
19136
-
"integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==",
19137
-
"cpu": [
19138
-
"ia32"
19139
-
],
19140
-
"dev": true,
19141
-
"optional": true,
19142
-
"os": [
19143
-
"linux"
19144
-
],
19145
-
"engines": {
19146
-
"node": ">=12"
19147
-
}
19148
-
},
19149
-
"node_modules/wrangler/node_modules/@esbuild/linux-loong64": {
19150
-
"version": "0.17.19",
19151
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz",
19152
-
"integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==",
19153
-
"cpu": [
19154
-
"loong64"
19155
-
],
19156
-
"dev": true,
19157
-
"optional": true,
19158
-
"os": [
19159
-
"linux"
19160
-
],
19161
-
"engines": {
19162
-
"node": ">=12"
19163
-
}
19164
-
},
19165
-
"node_modules/wrangler/node_modules/@esbuild/linux-mips64el": {
19166
-
"version": "0.17.19",
19167
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz",
19168
-
"integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==",
19169
-
"cpu": [
19170
-
"mips64el"
19171
-
],
19172
-
"dev": true,
19173
-
"optional": true,
19174
-
"os": [
19175
-
"linux"
19176
-
],
19177
-
"engines": {
19178
-
"node": ">=12"
19179
-
}
19180
-
},
19181
-
"node_modules/wrangler/node_modules/@esbuild/linux-ppc64": {
19182
-
"version": "0.17.19",
19183
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz",
19184
-
"integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==",
19185
-
"cpu": [
19186
-
"ppc64"
19187
-
],
19188
-
"dev": true,
19189
-
"optional": true,
19190
-
"os": [
19191
-
"linux"
19192
-
],
19193
-
"engines": {
19194
-
"node": ">=12"
19195
-
}
19196
-
},
19197
-
"node_modules/wrangler/node_modules/@esbuild/linux-riscv64": {
19198
-
"version": "0.17.19",
19199
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz",
19200
-
"integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==",
19201
-
"cpu": [
19202
-
"riscv64"
19203
-
],
19204
-
"dev": true,
19205
-
"optional": true,
19206
-
"os": [
19207
-
"linux"
19208
-
],
19209
-
"engines": {
19210
-
"node": ">=12"
19211
-
}
19212
-
},
19213
-
"node_modules/wrangler/node_modules/@esbuild/linux-s390x": {
19214
-
"version": "0.17.19",
19215
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz",
19216
-
"integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==",
19217
-
"cpu": [
19218
-
"s390x"
19219
-
],
19220
-
"dev": true,
19221
-
"optional": true,
19222
-
"os": [
19223
-
"linux"
19224
-
],
19225
-
"engines": {
19226
-
"node": ">=12"
19227
-
}
19228
-
},
19229
17460
"node_modules/wrangler/node_modules/@esbuild/linux-x64": {
19230
17461
"version": "0.17.19",
19231
17462
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz",
···
19237
17468
"optional": true,
19238
17469
"os": [
19239
17470
"linux"
19240
-
],
19241
-
"engines": {
19242
-
"node": ">=12"
19243
-
}
19244
-
},
19245
-
"node_modules/wrangler/node_modules/@esbuild/netbsd-x64": {
19246
-
"version": "0.17.19",
19247
-
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz",
19248
-
"integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==",
19249
-
"cpu": [
19250
-
"x64"
19251
-
],
19252
-
"dev": true,
19253
-
"optional": true,
19254
-
"os": [
19255
-
"netbsd"
19256
-
],
19257
-
"engines": {
19258
-
"node": ">=12"
19259
-
}
19260
-
},
19261
-
"node_modules/wrangler/node_modules/@esbuild/openbsd-x64": {
19262
-
"version": "0.17.19",
19263
-
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz",
19264
-
"integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==",
19265
-
"cpu": [
19266
-
"x64"
19267
-
],
19268
-
"dev": true,
19269
-
"optional": true,
19270
-
"os": [
19271
-
"openbsd"
19272
-
],
19273
-
"engines": {
19274
-
"node": ">=12"
19275
-
}
19276
-
},
19277
-
"node_modules/wrangler/node_modules/@esbuild/sunos-x64": {
19278
-
"version": "0.17.19",
19279
-
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz",
19280
-
"integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==",
19281
-
"cpu": [
19282
-
"x64"
19283
-
],
19284
-
"dev": true,
19285
-
"optional": true,
19286
-
"os": [
19287
-
"sunos"
19288
-
],
19289
-
"engines": {
19290
-
"node": ">=12"
19291
-
}
19292
-
},
19293
-
"node_modules/wrangler/node_modules/@esbuild/win32-arm64": {
19294
-
"version": "0.17.19",
19295
-
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz",
19296
-
"integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==",
19297
-
"cpu": [
19298
-
"arm64"
19299
-
],
19300
-
"dev": true,
19301
-
"optional": true,
19302
-
"os": [
19303
-
"win32"
19304
-
],
19305
-
"engines": {
19306
-
"node": ">=12"
19307
-
}
19308
-
},
19309
-
"node_modules/wrangler/node_modules/@esbuild/win32-ia32": {
19310
-
"version": "0.17.19",
19311
-
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz",
19312
-
"integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==",
19313
-
"cpu": [
19314
-
"ia32"
19315
-
],
19316
-
"dev": true,
19317
-
"optional": true,
19318
-
"os": [
19319
-
"win32"
19320
-
],
19321
-
"engines": {
19322
-
"node": ">=12"
19323
-
}
19324
-
},
19325
-
"node_modules/wrangler/node_modules/@esbuild/win32-x64": {
19326
-
"version": "0.17.19",
19327
-
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz",
19328
-
"integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==",
19329
-
"cpu": [
19330
-
"x64"
19331
-
],
19332
-
"dev": true,
19333
-
"optional": true,
19334
-
"os": [
19335
-
"win32"
19336
17471
],
19337
17472
"engines": {
19338
17473
"node": ">=12"
+11
-11
package.json
+11
-11
package.json
···
31
31
"@hono/node-server": "^1.14.3",
32
32
"@mdx-js/loader": "^3.1.0",
33
33
"@mdx-js/react": "^3.1.0",
34
-
"@next/bundle-analyzer": "16.0.3",
35
-
"@next/mdx": "16.0.3",
34
+
"@next/bundle-analyzer": "^15.3.2",
35
+
"@next/mdx": "15.3.2",
36
36
"@radix-ui/react-dialog": "^1.1.15",
37
37
"@radix-ui/react-dropdown-menu": "^2.1.16",
38
38
"@radix-ui/react-popover": "^1.1.15",
···
61
61
"linkifyjs": "^4.2.0",
62
62
"luxon": "^3.7.2",
63
63
"multiformats": "^13.3.2",
64
-
"next": "16.0.3",
64
+
"next": "^15.5.3",
65
65
"pg": "^8.16.3",
66
66
"prosemirror-commands": "^1.5.2",
67
67
"prosemirror-inputrules": "^1.4.0",
···
69
69
"prosemirror-model": "^1.21.0",
70
70
"prosemirror-schema-basic": "^1.2.2",
71
71
"prosemirror-state": "^1.4.3",
72
-
"react": "19.2.0",
72
+
"react": "^19.1.1",
73
73
"react-aria-components": "^1.8.0",
74
74
"react-day-picker": "^9.3.0",
75
-
"react-dom": "19.2.0",
75
+
"react-dom": "^19.1.1",
76
76
"react-use-measure": "^2.1.1",
77
77
"redlock": "^5.0.0-beta.2",
78
78
"rehype-parse": "^9.0.0",
···
102
102
"@types/katex": "^0.16.7",
103
103
"@types/luxon": "^3.7.1",
104
104
"@types/node": "^22.15.17",
105
-
"@types/react": "19.2.6",
106
-
"@types/react-dom": "19.2.3",
105
+
"@types/react": "19.1.3",
106
+
"@types/react-dom": "19.1.3",
107
107
"@types/uuid": "^10.0.0",
108
108
"drizzle-kit": "^0.21.2",
109
109
"esbuild": "^0.25.4",
110
-
"eslint": "^9.39.1",
111
-
"eslint-config-next": "16.0.3",
110
+
"eslint": "8.57.0",
111
+
"eslint-config-next": "^15.5.3",
112
112
"postcss": "^8.4.38",
113
113
"prettier": "3.2.5",
114
114
"supabase": "^1.187.3",
···
120
120
"overrides": {
121
121
"ajv": "^8.17.1",
122
122
"whatwg-url": "^14.0.0",
123
-
"@types/react": "19.2.6",
124
-
"@types/react-dom": "19.2.3"
123
+
"@types/react": "19.1.3",
124
+
"@types/react-dom": "19.1.3"
125
125
}
126
126
}
+4
-47
src/notifications.ts
+4
-47
src/notifications.ts
···
11
11
12
12
export type NotificationData =
13
13
| { type: "comment"; comment_uri: string; parent_uri?: string }
14
-
| { type: "subscribe"; subscription_uri: string }
15
-
| { type: "quote"; bsky_post_uri: string; document_uri: string };
14
+
| { type: "subscribe"; subscription_uri: string };
16
15
17
16
export type HydratedNotification =
18
17
| HydratedCommentNotification
19
-
| HydratedSubscribeNotification
20
-
| HydratedQuoteNotification;
18
+
| HydratedSubscribeNotification;
21
19
export async function hydrateNotifications(
22
20
notifications: NotificationRow[],
23
21
): Promise<Array<HydratedNotification>> {
24
22
// Call all hydrators in parallel
25
-
const [commentNotifications, subscribeNotifications, quoteNotifications] = await Promise.all([
23
+
const [commentNotifications, subscribeNotifications] = await Promise.all([
26
24
hydrateCommentNotifications(notifications),
27
25
hydrateSubscribeNotifications(notifications),
28
-
hydrateQuoteNotifications(notifications),
29
26
]);
30
27
31
28
// Combine all hydrated notifications
32
-
const allHydrated = [...commentNotifications, ...subscribeNotifications, ...quoteNotifications];
29
+
const allHydrated = [...commentNotifications, ...subscribeNotifications];
33
30
34
31
// Sort by created_at to maintain order
35
32
allHydrated.sort(
···
122
119
subscriptionData: subscriptions?.find(
123
120
(s) => s.uri === notification.data.subscription_uri,
124
121
)!,
125
-
}));
126
-
}
127
-
128
-
export type HydratedQuoteNotification = Awaited<
129
-
ReturnType<typeof hydrateQuoteNotifications>
130
-
>[0];
131
-
132
-
async function hydrateQuoteNotifications(notifications: NotificationRow[]) {
133
-
const quoteNotifications = notifications.filter(
134
-
(n): n is NotificationRow & { data: ExtractNotificationType<"quote"> } =>
135
-
(n.data as NotificationData)?.type === "quote",
136
-
);
137
-
138
-
if (quoteNotifications.length === 0) {
139
-
return [];
140
-
}
141
-
142
-
// Fetch bsky post data and document data
143
-
const bskyPostUris = quoteNotifications.map((n) => n.data.bsky_post_uri);
144
-
const documentUris = quoteNotifications.map((n) => n.data.document_uri);
145
-
146
-
const { data: bskyPosts } = await supabaseServerClient
147
-
.from("bsky_posts")
148
-
.select("*")
149
-
.in("uri", bskyPostUris);
150
-
151
-
const { data: documents } = await supabaseServerClient
152
-
.from("documents")
153
-
.select("*, documents_in_publications(publications(*))")
154
-
.in("uri", documentUris);
155
-
156
-
return quoteNotifications.map((notification) => ({
157
-
id: notification.id,
158
-
recipient: notification.recipient,
159
-
created_at: notification.created_at,
160
-
type: "quote" as const,
161
-
bsky_post_uri: notification.data.bsky_post_uri,
162
-
document_uri: notification.data.document_uri,
163
-
bskyPost: bskyPosts?.find((p) => p.uri === notification.data.bsky_post_uri)!,
164
-
document: documents?.find((d) => d.uri === notification.data.document_uri)!,
165
122
}));
166
123
}
167
124
-1
src/utils/codeLanguageStorage.ts
-1
src/utils/codeLanguageStorage.ts
···
1
-
export const LAST_USED_CODE_LANGUAGE_KEY = "lastUsedCodeLanguage";
+3
-3
src/utils/getCurrentDeploymentDomain.ts
+3
-3
src/utils/getCurrentDeploymentDomain.ts
···
1
-
import { headers } from "next/headers";
2
-
export async function getCurrentDeploymentDomain() {
3
-
const headersList = await headers();
1
+
import { headers, type UnsafeUnwrappedHeaders } from "next/headers";
2
+
export function getCurrentDeploymentDomain() {
3
+
const headersList = (headers() as unknown as UnsafeUnwrappedHeaders);
4
4
const hostname = headersList.get("x-forwarded-host");
5
5
let protocol = headersList.get("x-forwarded-proto");
6
6
return `${protocol}://${hostname}/`;
-50
src/utils/getPublicationMetadataFromLeafletData.ts
-50
src/utils/getPublicationMetadataFromLeafletData.ts
···
1
-
import { GetLeafletDataReturnType } from "app/api/rpc/[command]/get_leaflet_data";
2
-
import { Json } from "supabase/database.types";
3
-
4
-
export function getPublicationMetadataFromLeafletData(
5
-
data?: GetLeafletDataReturnType["result"]["data"],
6
-
) {
7
-
if (!data) return null;
8
-
9
-
let pubData:
10
-
| {
11
-
description: string;
12
-
title: string;
13
-
leaflet: string;
14
-
doc: string | null;
15
-
publications: {
16
-
identity_did: string;
17
-
name: string;
18
-
indexed_at: string;
19
-
record: Json | null;
20
-
uri: string;
21
-
} | null;
22
-
documents: {
23
-
data: Json;
24
-
indexed_at: string;
25
-
uri: string;
26
-
} | null;
27
-
}
28
-
| undefined
29
-
| null =
30
-
data?.leaflets_in_publications?.[0] ||
31
-
data?.permission_token_rights[0].entity_sets?.permission_tokens?.find(
32
-
(p) => p.leaflets_in_publications?.length,
33
-
)?.leaflets_in_publications?.[0];
34
-
35
-
// If not found, check for standalone documents
36
-
let standaloneDoc =
37
-
data?.leaflets_to_documents?.[0] ||
38
-
data?.permission_token_rights[0].entity_sets?.permission_tokens.find(
39
-
(p) => p.leaflets_to_documents?.length,
40
-
)?.leaflets_to_documents?.[0];
41
-
if (!pubData && standaloneDoc) {
42
-
// Transform standalone document data to match the expected format
43
-
pubData = {
44
-
...standaloneDoc,
45
-
publications: null, // No publication for standalone docs
46
-
doc: standaloneDoc.document,
47
-
};
48
-
}
49
-
return pubData;
50
-
}
+16
src/utils/isBot.ts
+16
src/utils/isBot.ts
···
1
+
import { cookies, headers, type UnsafeUnwrappedHeaders } from "next/headers";
2
+
export function getIsBot() {
3
+
const userAgent =
4
+
(headers() as unknown as UnsafeUnwrappedHeaders).get("user-agent") || "";
5
+
const botPatterns = [
6
+
/bot/i,
7
+
/crawler/i,
8
+
/spider/i,
9
+
/googlebot/i,
10
+
/bingbot/i,
11
+
/yahoo/i,
12
+
// Add more patterns as needed
13
+
];
14
+
15
+
return botPatterns.some((pattern) => pattern.test(userAgent));
16
+
}
-15
supabase/migrations/20251122220118_add_cascade_on_update_to_pt_relations.sql
-15
supabase/migrations/20251122220118_add_cascade_on_update_to_pt_relations.sql
···
1
-
alter table "public"."permission_token_on_homepage" drop constraint "permission_token_creator_token_fkey";
2
-
3
-
alter table "public"."leaflets_in_publications" drop constraint "leaflets_in_publications_leaflet_fkey";
4
-
5
-
alter table "public"."leaflets_in_publications" drop column "archived";
6
-
7
-
alter table "public"."permission_token_on_homepage" drop column "archived";
8
-
9
-
alter table "public"."permission_token_on_homepage" add constraint "permission_token_on_homepage_token_fkey" FOREIGN KEY (token) REFERENCES permission_tokens(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
10
-
11
-
alter table "public"."permission_token_on_homepage" validate constraint "permission_token_on_homepage_token_fkey";
12
-
13
-
alter table "public"."leaflets_in_publications" add constraint "leaflets_in_publications_leaflet_fkey" FOREIGN KEY (leaflet) REFERENCES permission_tokens(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
14
-
15
-
alter table "public"."leaflets_in_publications" validate constraint "leaflets_in_publications_leaflet_fkey";
-2
supabase/migrations/20251124214105_add_back_archived_cols.sql
-2
supabase/migrations/20251124214105_add_back_archived_cols.sql
+5
-14
tsconfig.json
+5
-14
tsconfig.json
···
1
1
{
2
2
"compilerOptions": {
3
-
"lib": [
4
-
"dom",
5
-
"dom.iterable",
6
-
"esnext"
7
-
],
8
-
"types": [
9
-
"@cloudflare/workers-types"
10
-
],
3
+
"lib": ["dom", "dom.iterable", "esnext"],
4
+
"types": ["@cloudflare/workers-types"],
11
5
"baseUrl": ".",
12
6
"allowJs": true,
13
7
"skipLibCheck": true,
···
21
15
"moduleResolution": "node",
22
16
"resolveJsonModule": true,
23
17
"isolatedModules": true,
24
-
"jsx": "react-jsx",
18
+
"jsx": "preserve",
25
19
"plugins": [
26
20
{
27
21
"name": "next"
···
36
30
"**/*.js",
37
31
"**/*.ts",
38
32
"**/*.tsx",
39
-
"**/*.mdx",
40
-
".next/dev/types/**/*.ts"
33
+
"**/*.mdx"
41
34
],
42
-
"exclude": [
43
-
"node_modules"
44
-
]
35
+
"exclude": ["node_modules"]
45
36
}