livemark 0.17.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/actions/config/define.d.ts +8 -0
- package/build/components/Layout.js +7 -7
- package/build/components/SearchDialog.js +2 -3
- package/build/components/SidebarArticles.js +5 -4
- package/build/components/SidebarLinks.js +3 -3
- package/build/components/SidebarSections.js +6 -6
- package/build/components/SiteTitle.d.ts +3 -1
- package/build/components/SiteTitle.js +12 -4
- package/build/content/article.d.ts +6 -0
- package/build/content-collections.js +2 -3
- package/build/helpers/article-icon.d.ts +4 -4
- package/build/helpers/article-icon.js +8 -36
- package/build/helpers/article.d.ts +1 -1
- package/build/helpers/article.js +1 -1
- package/build/helpers/section.d.ts +42 -3
- package/build/helpers/section.js +34 -10
- package/build/helpers/section.unit.js +57 -2
- package/build/hooks/media.d.ts +1 -1
- package/build/hooks/media.js +2 -2
- package/build/models/article.d.ts +1 -1
- package/build/models/article.js +1 -1
- package/build/models/config.d.ts +24 -0
- package/build/models/section.d.ts +16 -0
- package/build/models/section.js +3 -1
- package/package.json +1 -1
- package/source/components/Layout.tsx +7 -7
- package/source/components/SearchDialog.tsx +4 -2
- package/source/components/SidebarArticles.tsx +4 -3
- package/source/components/SidebarLinks.tsx +2 -2
- package/source/components/SidebarSections.tsx +5 -5
- package/source/components/SiteTitle.tsx +12 -4
- package/source/content-collections.ts +1 -2
- package/source/helpers/article-icon.ts +7 -38
- package/source/helpers/article.ts +1 -1
- package/source/helpers/section.ts +27 -8
- package/source/helpers/section.unit.ts +69 -0
- package/source/hooks/media.ts +1 -1
- package/source/models/article.ts +1 -1
- package/source/models/section.ts +2 -0
package/build/models/config.d.ts
CHANGED
|
@@ -120,6 +120,8 @@ export declare const UserConfig: z.ZodObject<{
|
|
|
120
120
|
header: "header";
|
|
121
121
|
sidebar: "sidebar";
|
|
122
122
|
}>>;
|
|
123
|
+
siteTitle: z.ZodOptional<z.ZodString>;
|
|
124
|
+
siteDescription: z.ZodOptional<z.ZodString>;
|
|
123
125
|
type: z.ZodLiteral<"article">;
|
|
124
126
|
prefix: z.ZodString;
|
|
125
127
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -129,6 +131,8 @@ export declare const UserConfig: z.ZodObject<{
|
|
|
129
131
|
header: "header";
|
|
130
132
|
sidebar: "sidebar";
|
|
131
133
|
}>>;
|
|
134
|
+
siteTitle: z.ZodOptional<z.ZodString>;
|
|
135
|
+
siteDescription: z.ZodOptional<z.ZodString>;
|
|
132
136
|
type: z.ZodLiteral<"blog">;
|
|
133
137
|
prefix: z.ZodString;
|
|
134
138
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -138,6 +142,8 @@ export declare const UserConfig: z.ZodObject<{
|
|
|
138
142
|
header: "header";
|
|
139
143
|
sidebar: "sidebar";
|
|
140
144
|
}>>;
|
|
145
|
+
siteTitle: z.ZodOptional<z.ZodString>;
|
|
146
|
+
siteDescription: z.ZodOptional<z.ZodString>;
|
|
141
147
|
type: z.ZodLiteral<"changelog">;
|
|
142
148
|
prefix: z.ZodString;
|
|
143
149
|
source: z.ZodString;
|
|
@@ -149,6 +155,8 @@ export declare const UserConfig: z.ZodObject<{
|
|
|
149
155
|
header: "header";
|
|
150
156
|
sidebar: "sidebar";
|
|
151
157
|
}>>;
|
|
158
|
+
siteTitle: z.ZodOptional<z.ZodString>;
|
|
159
|
+
siteDescription: z.ZodOptional<z.ZodString>;
|
|
152
160
|
type: z.ZodLiteral<"custom">;
|
|
153
161
|
url: z.ZodString;
|
|
154
162
|
}, z.core.$strip>], "type">>>>;
|
|
@@ -203,6 +211,8 @@ export declare const WebsiteConfig: z.ZodObject<{
|
|
|
203
211
|
header: "header";
|
|
204
212
|
sidebar: "sidebar";
|
|
205
213
|
}>>;
|
|
214
|
+
siteTitle: z.ZodOptional<z.ZodString>;
|
|
215
|
+
siteDescription: z.ZodOptional<z.ZodString>;
|
|
206
216
|
type: z.ZodLiteral<"article">;
|
|
207
217
|
prefix: z.ZodString;
|
|
208
218
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -212,6 +222,8 @@ export declare const WebsiteConfig: z.ZodObject<{
|
|
|
212
222
|
header: "header";
|
|
213
223
|
sidebar: "sidebar";
|
|
214
224
|
}>>;
|
|
225
|
+
siteTitle: z.ZodOptional<z.ZodString>;
|
|
226
|
+
siteDescription: z.ZodOptional<z.ZodString>;
|
|
215
227
|
type: z.ZodLiteral<"blog">;
|
|
216
228
|
prefix: z.ZodString;
|
|
217
229
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -221,6 +233,8 @@ export declare const WebsiteConfig: z.ZodObject<{
|
|
|
221
233
|
header: "header";
|
|
222
234
|
sidebar: "sidebar";
|
|
223
235
|
}>>;
|
|
236
|
+
siteTitle: z.ZodOptional<z.ZodString>;
|
|
237
|
+
siteDescription: z.ZodOptional<z.ZodString>;
|
|
224
238
|
type: z.ZodLiteral<"changelog">;
|
|
225
239
|
prefix: z.ZodString;
|
|
226
240
|
source: z.ZodString;
|
|
@@ -232,6 +246,8 @@ export declare const WebsiteConfig: z.ZodObject<{
|
|
|
232
246
|
header: "header";
|
|
233
247
|
sidebar: "sidebar";
|
|
234
248
|
}>>;
|
|
249
|
+
siteTitle: z.ZodOptional<z.ZodString>;
|
|
250
|
+
siteDescription: z.ZodOptional<z.ZodString>;
|
|
235
251
|
type: z.ZodLiteral<"custom">;
|
|
236
252
|
url: z.ZodString;
|
|
237
253
|
}, z.core.$strip>], "type">>>>;
|
|
@@ -352,6 +368,8 @@ export declare const Config: z.ZodObject<{
|
|
|
352
368
|
header: "header";
|
|
353
369
|
sidebar: "sidebar";
|
|
354
370
|
}>>;
|
|
371
|
+
siteTitle: z.ZodOptional<z.ZodString>;
|
|
372
|
+
siteDescription: z.ZodOptional<z.ZodString>;
|
|
355
373
|
type: z.ZodLiteral<"article">;
|
|
356
374
|
prefix: z.ZodString;
|
|
357
375
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -361,6 +379,8 @@ export declare const Config: z.ZodObject<{
|
|
|
361
379
|
header: "header";
|
|
362
380
|
sidebar: "sidebar";
|
|
363
381
|
}>>;
|
|
382
|
+
siteTitle: z.ZodOptional<z.ZodString>;
|
|
383
|
+
siteDescription: z.ZodOptional<z.ZodString>;
|
|
364
384
|
type: z.ZodLiteral<"blog">;
|
|
365
385
|
prefix: z.ZodString;
|
|
366
386
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -370,6 +390,8 @@ export declare const Config: z.ZodObject<{
|
|
|
370
390
|
header: "header";
|
|
371
391
|
sidebar: "sidebar";
|
|
372
392
|
}>>;
|
|
393
|
+
siteTitle: z.ZodOptional<z.ZodString>;
|
|
394
|
+
siteDescription: z.ZodOptional<z.ZodString>;
|
|
373
395
|
type: z.ZodLiteral<"changelog">;
|
|
374
396
|
prefix: z.ZodString;
|
|
375
397
|
source: z.ZodString;
|
|
@@ -381,6 +403,8 @@ export declare const Config: z.ZodObject<{
|
|
|
381
403
|
header: "header";
|
|
382
404
|
sidebar: "sidebar";
|
|
383
405
|
}>>;
|
|
406
|
+
siteTitle: z.ZodOptional<z.ZodString>;
|
|
407
|
+
siteDescription: z.ZodOptional<z.ZodString>;
|
|
384
408
|
type: z.ZodLiteral<"custom">;
|
|
385
409
|
url: z.ZodString;
|
|
386
410
|
}, z.core.$strip>], "type">>>>;
|
|
@@ -8,6 +8,8 @@ export declare const ArticleSection: z.ZodObject<{
|
|
|
8
8
|
header: "header";
|
|
9
9
|
sidebar: "sidebar";
|
|
10
10
|
}>>;
|
|
11
|
+
siteTitle: z.ZodOptional<z.ZodString>;
|
|
12
|
+
siteDescription: z.ZodOptional<z.ZodString>;
|
|
11
13
|
type: z.ZodLiteral<"article">;
|
|
12
14
|
prefix: z.ZodString;
|
|
13
15
|
}, z.core.$strip>;
|
|
@@ -20,6 +22,8 @@ export declare const BlogSection: z.ZodObject<{
|
|
|
20
22
|
header: "header";
|
|
21
23
|
sidebar: "sidebar";
|
|
22
24
|
}>>;
|
|
25
|
+
siteTitle: z.ZodOptional<z.ZodString>;
|
|
26
|
+
siteDescription: z.ZodOptional<z.ZodString>;
|
|
23
27
|
type: z.ZodLiteral<"blog">;
|
|
24
28
|
prefix: z.ZodString;
|
|
25
29
|
}, z.core.$strip>;
|
|
@@ -32,6 +36,8 @@ export declare const ChangelogSection: z.ZodObject<{
|
|
|
32
36
|
header: "header";
|
|
33
37
|
sidebar: "sidebar";
|
|
34
38
|
}>>;
|
|
39
|
+
siteTitle: z.ZodOptional<z.ZodString>;
|
|
40
|
+
siteDescription: z.ZodOptional<z.ZodString>;
|
|
35
41
|
type: z.ZodLiteral<"changelog">;
|
|
36
42
|
prefix: z.ZodString;
|
|
37
43
|
source: z.ZodString;
|
|
@@ -48,6 +54,8 @@ export declare const CustomSection: z.ZodObject<{
|
|
|
48
54
|
header: "header";
|
|
49
55
|
sidebar: "sidebar";
|
|
50
56
|
}>>;
|
|
57
|
+
siteTitle: z.ZodOptional<z.ZodString>;
|
|
58
|
+
siteDescription: z.ZodOptional<z.ZodString>;
|
|
51
59
|
type: z.ZodLiteral<"custom">;
|
|
52
60
|
url: z.ZodString;
|
|
53
61
|
}, z.core.$strip>;
|
|
@@ -64,6 +72,8 @@ export declare const Section: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodD
|
|
|
64
72
|
header: "header";
|
|
65
73
|
sidebar: "sidebar";
|
|
66
74
|
}>>;
|
|
75
|
+
siteTitle: z.ZodOptional<z.ZodString>;
|
|
76
|
+
siteDescription: z.ZodOptional<z.ZodString>;
|
|
67
77
|
type: z.ZodLiteral<"article">;
|
|
68
78
|
prefix: z.ZodString;
|
|
69
79
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -73,6 +83,8 @@ export declare const Section: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodD
|
|
|
73
83
|
header: "header";
|
|
74
84
|
sidebar: "sidebar";
|
|
75
85
|
}>>;
|
|
86
|
+
siteTitle: z.ZodOptional<z.ZodString>;
|
|
87
|
+
siteDescription: z.ZodOptional<z.ZodString>;
|
|
76
88
|
type: z.ZodLiteral<"blog">;
|
|
77
89
|
prefix: z.ZodString;
|
|
78
90
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -82,6 +94,8 @@ export declare const Section: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodD
|
|
|
82
94
|
header: "header";
|
|
83
95
|
sidebar: "sidebar";
|
|
84
96
|
}>>;
|
|
97
|
+
siteTitle: z.ZodOptional<z.ZodString>;
|
|
98
|
+
siteDescription: z.ZodOptional<z.ZodString>;
|
|
85
99
|
type: z.ZodLiteral<"changelog">;
|
|
86
100
|
prefix: z.ZodString;
|
|
87
101
|
source: z.ZodString;
|
|
@@ -93,6 +107,8 @@ export declare const Section: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodD
|
|
|
93
107
|
header: "header";
|
|
94
108
|
sidebar: "sidebar";
|
|
95
109
|
}>>;
|
|
110
|
+
siteTitle: z.ZodOptional<z.ZodString>;
|
|
111
|
+
siteDescription: z.ZodOptional<z.ZodString>;
|
|
96
112
|
type: z.ZodLiteral<"custom">;
|
|
97
113
|
url: z.ZodString;
|
|
98
114
|
}, z.core.$strip>], "type">>;
|
package/build/models/section.js
CHANGED
|
@@ -3,6 +3,8 @@ const BaseSection = z.object({
|
|
|
3
3
|
icon: z.string().optional(),
|
|
4
4
|
title: z.string(),
|
|
5
5
|
position: z.enum(["header", "sidebar"]).default("header"),
|
|
6
|
+
siteTitle: z.string().optional(),
|
|
7
|
+
siteDescription: z.string().optional(),
|
|
6
8
|
});
|
|
7
9
|
export const ArticleSection = BaseSection.extend({
|
|
8
10
|
type: z.literal("article"),
|
|
@@ -34,4 +36,4 @@ export const Section = z.preprocess(input => {
|
|
|
34
36
|
ChangelogSection,
|
|
35
37
|
CustomSection,
|
|
36
38
|
]));
|
|
37
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
39
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VjdGlvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NvdXJjZS9tb2RlbHMvc2VjdGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsQ0FBQyxFQUFFLE1BQU0sS0FBSyxDQUFBO0FBRXZCLE1BQU0sV0FBVyxHQUFHLENBQUMsQ0FBQyxNQUFNLENBQUM7SUFDM0IsSUFBSSxFQUFFLENBQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxRQUFRLEVBQUU7SUFDM0IsS0FBSyxFQUFFLENBQUMsQ0FBQyxNQUFNLEVBQUU7SUFDakIsUUFBUSxFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxRQUFRLEVBQUUsU0FBUyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDO0lBQ3pELFNBQVMsRUFBRSxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsUUFBUSxFQUFFO0lBQ2hDLGVBQWUsRUFBRSxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsUUFBUSxFQUFFO0NBQ3ZDLENBQUMsQ0FBQTtBQUlGLE1BQU0sQ0FBQyxNQUFNLGNBQWMsR0FBRyxXQUFXLENBQUMsTUFBTSxDQUFDO0lBQy9DLElBQUksRUFBRSxDQUFDLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQztJQUMxQixNQUFNLEVBQUUsQ0FBQyxDQUFDLE1BQU0sRUFBRTtDQUNuQixDQUFDLENBQUE7QUFJRixNQUFNLENBQUMsTUFBTSxXQUFXLEdBQUcsV0FBVyxDQUFDLE1BQU0sQ0FBQztJQUM1QyxJQUFJLEVBQUUsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUM7SUFDdkIsTUFBTSxFQUFFLENBQUMsQ0FBQyxNQUFNLEVBQUU7Q0FDbkIsQ0FBQyxDQUFBO0FBSUYsTUFBTSxDQUFDLE1BQU0sZ0JBQWdCLEdBQUcsV0FBVyxDQUFDLE1BQU0sQ0FBQztJQUNqRCxJQUFJLEVBQUUsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxXQUFXLENBQUM7SUFDNUIsTUFBTSxFQUFFLENBQUMsQ0FBQyxNQUFNLEVBQUU7SUFDbEIsTUFBTSxFQUFFLENBQUMsQ0FBQyxNQUFNLEVBQUU7SUFDbEIsT0FBTyxFQUFFLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxRQUFRLEVBQUU7Q0FDaEMsQ0FBQyxDQUFBO0FBTUYsTUFBTSxDQUFDLE1BQU0sYUFBYSxHQUFHLFdBQVcsQ0FBQyxNQUFNLENBQUM7SUFDOUMsSUFBSSxFQUFFLENBQUMsQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDO0lBQ3pCLEdBQUcsRUFBRSxDQUFDLENBQUMsTUFBTSxFQUFFO0NBQ2hCLENBQUMsQ0FBQTtBQVFGLE1BQU0sQ0FBQyxNQUFNLE9BQU8sR0FBRyxDQUFDLENBQUMsVUFBVSxDQUNqQyxLQUFLLENBQUMsRUFBRTtJQUNOLElBQUksS0FBSyxLQUFLLElBQUksSUFBSSxPQUFPLEtBQUssS0FBSyxRQUFRLElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO1FBQ3hFLE9BQU8sS0FBSyxDQUFBO0lBQ2QsQ0FBQztJQUNELE1BQU0sTUFBTSxHQUFHLEtBQWdDLENBQUE7SUFDL0MsT0FBTyxNQUFNLElBQUksTUFBTSxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsR0FBRyxNQUFNLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxDQUFBO0FBQ25FLENBQUMsRUFDRCxDQUFDLENBQUMsa0JBQWtCLENBQUMsTUFBTSxFQUFFO0lBQzNCLGNBQWM7SUFDZCxXQUFXO0lBQ1gsZ0JBQWdCO0lBQ2hCLGFBQWE7Q0FDZCxDQUFDLENBQ0gsQ0FBQSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IHogfSBmcm9tIFwiem9kXCJcblxuY29uc3QgQmFzZVNlY3Rpb24gPSB6Lm9iamVjdCh7XG4gIGljb246IHouc3RyaW5nKCkub3B0aW9uYWwoKSxcbiAgdGl0bGU6IHouc3RyaW5nKCksXG4gIHBvc2l0aW9uOiB6LmVudW0oW1wiaGVhZGVyXCIsIFwic2lkZWJhclwiXSkuZGVmYXVsdChcImhlYWRlclwiKSxcbiAgc2l0ZVRpdGxlOiB6LnN0cmluZygpLm9wdGlvbmFsKCksXG4gIHNpdGVEZXNjcmlwdGlvbjogei5zdHJpbmcoKS5vcHRpb25hbCgpLFxufSlcblxuLyoqIERlZmF1bHQgc2VjdGlvbjogcmVuZGVycyBhcnRpY2xlcy4gKi9cbmV4cG9ydCB0eXBlIEFydGljbGVTZWN0aW9uID0gei5pbmZlcjx0eXBlb2YgQXJ0aWNsZVNlY3Rpb24+XG5leHBvcnQgY29uc3QgQXJ0aWNsZVNlY3Rpb24gPSBCYXNlU2VjdGlvbi5leHRlbmQoe1xuICB0eXBlOiB6LmxpdGVyYWwoXCJhcnRpY2xlXCIpLFxuICBwcmVmaXg6IHouc3RyaW5nKCksXG59KVxuXG4vKiogQmxvZyBzZWN0aW9uOiBkYXRlLXNvcnRlZCBwb3N0cyB3aXRoIHRhZ3MgYW5kIGluZGV4LiAqL1xuZXhwb3J0IHR5cGUgQmxvZ1NlY3Rpb24gPSB6LmluZmVyPHR5cGVvZiBCbG9nU2VjdGlvbj5cbmV4cG9ydCBjb25zdCBCbG9nU2VjdGlvbiA9IEJhc2VTZWN0aW9uLmV4dGVuZCh7XG4gIHR5cGU6IHoubGl0ZXJhbChcImJsb2dcIiksXG4gIHByZWZpeDogei5zdHJpbmcoKSxcbn0pXG5cbi8qKiBDaGFuZ2Vsb2cgc2VjdGlvbjogc2luZ2xlIGZpbGUgb3IgR2l0SHViIHJlbGVhc2VzLCBzcGxpdCBwZXIgdmVyc2lvbi4gKi9cbmV4cG9ydCB0eXBlIENoYW5nZWxvZ1NlY3Rpb24gPSB6LmluZmVyPHR5cGVvZiBDaGFuZ2Vsb2dTZWN0aW9uPlxuZXhwb3J0IGNvbnN0IENoYW5nZWxvZ1NlY3Rpb24gPSBCYXNlU2VjdGlvbi5leHRlbmQoe1xuICB0eXBlOiB6LmxpdGVyYWwoXCJjaGFuZ2Vsb2dcIiksXG4gIHByZWZpeDogei5zdHJpbmcoKSxcbiAgc291cmNlOiB6LnN0cmluZygpLFxuICB2ZXJzaW9uOiB6LmJvb2xlYW4oKS5vcHRpb25hbCgpLFxufSlcblxuLyoqIEN1c3RvbSBzZWN0aW9uOiByZW5kZXJzIGFzIGEgbGluayB0byBhbiBhcmJpdHJhcnkgVVJMIOKAlCBhYnNvbHV0ZVxuICogIChgaHR0cHM6Ly/igKZgKSBvciBpbnRlcm5hbCAoYC9jaGFuZ2Vsb2dgKS4gSW50ZXJuYWwgVVJMcyBwYXJ0aWNpcGF0ZVxuICogIGluIGFjdGl2ZS1zdGF0ZSBoaWdobGlnaHRpbmcgbGlrZSByb3V0ZWQgc2VjdGlvbnMuICovXG5leHBvcnQgdHlwZSBDdXN0b21TZWN0aW9uID0gei5pbmZlcjx0eXBlb2YgQ3VzdG9tU2VjdGlvbj5cbmV4cG9ydCBjb25zdCBDdXN0b21TZWN0aW9uID0gQmFzZVNlY3Rpb24uZXh0ZW5kKHtcbiAgdHlwZTogei5saXRlcmFsKFwiY3VzdG9tXCIpLFxuICB1cmw6IHouc3RyaW5nKCksXG59KVxuXG4vKipcbiAqIEEgc2l0ZSBzZWN0aW9uLiBEaXNjcmltaW5hdGVkIG9uIGB0eXBlYC5cbiAqIFdoZW4gYHR5cGVgIGlzIG9taXR0ZWQsIGl0IGRlZmF1bHRzIHRvIFwiYXJ0aWNsZVwiIHNvIHBsYWluXG4gKiBgeyB0aXRsZSwgcHJlZml4IH1gIGVudHJpZXMga2VlcCB3b3JraW5nLlxuICovXG5leHBvcnQgdHlwZSBTZWN0aW9uID0gei5pbmZlcjx0eXBlb2YgU2VjdGlvbj5cbmV4cG9ydCBjb25zdCBTZWN0aW9uID0gei5wcmVwcm9jZXNzKFxuICBpbnB1dCA9PiB7XG4gICAgaWYgKGlucHV0ID09PSBudWxsIHx8IHR5cGVvZiBpbnB1dCAhPT0gXCJvYmplY3RcIiB8fCBBcnJheS5pc0FycmF5KGlucHV0KSkge1xuICAgICAgcmV0dXJuIGlucHV0XG4gICAgfVxuICAgIGNvbnN0IHJlY29yZCA9IGlucHV0IGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+XG4gICAgcmV0dXJuIFwidHlwZVwiIGluIHJlY29yZCA/IHJlY29yZCA6IHsgLi4ucmVjb3JkLCB0eXBlOiBcImFydGljbGVcIiB9XG4gIH0sXG4gIHouZGlzY3JpbWluYXRlZFVuaW9uKFwidHlwZVwiLCBbXG4gICAgQXJ0aWNsZVNlY3Rpb24sXG4gICAgQmxvZ1NlY3Rpb24sXG4gICAgQ2hhbmdlbG9nU2VjdGlvbixcbiAgICBDdXN0b21TZWN0aW9uLFxuICBdKSxcbilcbiJdfQ==
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
} from "../content/article.ts"
|
|
10
10
|
import { DynamicIcon } from "../helpers/dynamic-icon.tsx"
|
|
11
11
|
import { prefixUrl } from "../helpers/prefix-url.ts"
|
|
12
|
-
import { customSectionActive
|
|
12
|
+
import { customSectionActive } from "../helpers/section.ts"
|
|
13
13
|
import {
|
|
14
14
|
SidebarInset,
|
|
15
15
|
SidebarProvider,
|
|
@@ -47,14 +47,14 @@ export function Layout(props: {
|
|
|
47
47
|
const header = (
|
|
48
48
|
<header className="sticky top-0 z-20 flex h-16 shrink-0 items-center border-b bg-background">
|
|
49
49
|
<div
|
|
50
|
-
className={`flex items-center self-stretch pl-4 ${props.withSidebar ? "pr-4" : "
|
|
50
|
+
className={`flex items-center self-stretch pl-4 ${props.withSidebar ? "pr-4" : "lg:pr-20"}`}
|
|
51
51
|
>
|
|
52
52
|
{props.withSidebar ? (
|
|
53
53
|
<>
|
|
54
|
-
<div className="hidden
|
|
54
|
+
<div className="hidden lg:flex">
|
|
55
55
|
<SidebarTrigger />
|
|
56
56
|
</div>
|
|
57
|
-
<Link to="/" className="
|
|
57
|
+
<Link to="/" className="lg:hidden flex items-center gap-2">
|
|
58
58
|
<SiteTitle />
|
|
59
59
|
</Link>
|
|
60
60
|
</>
|
|
@@ -64,12 +64,12 @@ export function Layout(props: {
|
|
|
64
64
|
</Link>
|
|
65
65
|
)}
|
|
66
66
|
</div>
|
|
67
|
-
<div className="hidden
|
|
67
|
+
<div className="hidden lg:flex flex-1 items-center gap-8 self-stretch px-6 text-sm">
|
|
68
68
|
{sections?.length ? (
|
|
69
69
|
sections
|
|
70
70
|
.filter(section => section.position === "header")
|
|
71
71
|
.map(section => {
|
|
72
|
-
const icon =
|
|
72
|
+
const icon = section.icon
|
|
73
73
|
if (section.type === "custom") {
|
|
74
74
|
const isActive = customSectionActive(section.url, pathname)
|
|
75
75
|
return (
|
|
@@ -151,7 +151,7 @@ export function Layout(props: {
|
|
|
151
151
|
))}
|
|
152
152
|
<Banner />
|
|
153
153
|
</div>
|
|
154
|
-
<div className="
|
|
154
|
+
<div className="lg:hidden ml-auto pr-2">
|
|
155
155
|
<SidebarTrigger />
|
|
156
156
|
</div>
|
|
157
157
|
</header>
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
interface SearchResult {
|
|
16
16
|
title: string
|
|
17
17
|
path: string
|
|
18
|
-
icon
|
|
18
|
+
icon?: string
|
|
19
19
|
snippet: string
|
|
20
20
|
group: string
|
|
21
21
|
}
|
|
@@ -157,7 +157,9 @@ export function SearchDialog(props: {
|
|
|
157
157
|
onSelect={() => handleSelect(result.path)}
|
|
158
158
|
className="py-2.5"
|
|
159
159
|
>
|
|
160
|
-
|
|
160
|
+
{Icon && (
|
|
161
|
+
<Icon className="size-5 shrink-0 mt-0.5 self-start opacity-60" />
|
|
162
|
+
)}
|
|
161
163
|
<div className="flex flex-col gap-0.5 min-w-0">
|
|
162
164
|
<span className="text-base font-medium">
|
|
163
165
|
{result.title}
|
|
@@ -56,7 +56,8 @@ function splatFor(pathname: string) {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
function isActive(articlePathname: string, currentPath: string) {
|
|
59
|
-
const
|
|
59
|
+
const trimmed = currentPath.replace(/^\/|\/$/g, "")
|
|
60
|
+
const normalized = trimmed ? `/${trimmed}/` : "/"
|
|
60
61
|
return articlePathname === normalized
|
|
61
62
|
}
|
|
62
63
|
|
|
@@ -83,7 +84,7 @@ function NavNode(props: {
|
|
|
83
84
|
className={active ? "" : "opacity-75"}
|
|
84
85
|
render={link}
|
|
85
86
|
>
|
|
86
|
-
{!isSub && <Icon className="size-4" />}
|
|
87
|
+
{!isSub && Icon && <Icon className="size-4" />}
|
|
87
88
|
{label}
|
|
88
89
|
</Button>
|
|
89
90
|
</Item>
|
|
@@ -98,7 +99,7 @@ function NavNode(props: {
|
|
|
98
99
|
className={active ? "" : "opacity-75"}
|
|
99
100
|
render={link}
|
|
100
101
|
>
|
|
101
|
-
{!isSub && <Icon className="size-4" />}
|
|
102
|
+
{!isSub && Icon && <Icon className="size-4" />}
|
|
102
103
|
{label}
|
|
103
104
|
</Button>
|
|
104
105
|
<CollapsibleTrigger
|
|
@@ -27,7 +27,7 @@ export function SidebarLinks() {
|
|
|
27
27
|
const hasDesktopItems = links.some(l => l.position === "sidebar")
|
|
28
28
|
|
|
29
29
|
return (
|
|
30
|
-
<SidebarGroup className={hasDesktopItems ? undefined : "
|
|
30
|
+
<SidebarGroup className={hasDesktopItems ? undefined : "lg:hidden"}>
|
|
31
31
|
<SidebarGroupLabel className="uppercase font-mono text-xs tracking-widest">
|
|
32
32
|
Links
|
|
33
33
|
</SidebarGroupLabel>
|
|
@@ -38,7 +38,7 @@ export function SidebarLinks() {
|
|
|
38
38
|
return (
|
|
39
39
|
<SidebarMenuItem
|
|
40
40
|
key={link.url}
|
|
41
|
-
className={mobileOnly ? "
|
|
41
|
+
className={mobileOnly ? "lg:hidden" : undefined}
|
|
42
42
|
>
|
|
43
43
|
<SidebarMenuButton
|
|
44
44
|
className="text-muted-foreground"
|
|
@@ -3,7 +3,7 @@ import { Link, useLocation, useMatch } from "@tanstack/react-router"
|
|
|
3
3
|
import { currentSection, sectionFirstArticle } from "../content/article.ts"
|
|
4
4
|
import { DynamicIcon } from "../helpers/dynamic-icon.tsx"
|
|
5
5
|
import { prefixUrl } from "../helpers/prefix-url.ts"
|
|
6
|
-
import { customSectionActive
|
|
6
|
+
import { customSectionActive } from "../helpers/section.ts"
|
|
7
7
|
import {
|
|
8
8
|
SidebarGroup,
|
|
9
9
|
SidebarGroupContent,
|
|
@@ -32,7 +32,7 @@ export function SidebarSections() {
|
|
|
32
32
|
const hasDesktopItems = sections.some(s => s.position === "sidebar")
|
|
33
33
|
|
|
34
34
|
return (
|
|
35
|
-
<SidebarGroup className={hasDesktopItems ? undefined : "
|
|
35
|
+
<SidebarGroup className={hasDesktopItems ? undefined : "lg:hidden"}>
|
|
36
36
|
<SidebarGroupLabel className="uppercase font-mono text-xs tracking-widest">
|
|
37
37
|
Sections
|
|
38
38
|
</SidebarGroupLabel>
|
|
@@ -40,13 +40,13 @@ export function SidebarSections() {
|
|
|
40
40
|
<SidebarMenu>
|
|
41
41
|
{sections.map(s => {
|
|
42
42
|
const mobileOnly = s.position !== "sidebar"
|
|
43
|
-
const icon =
|
|
43
|
+
const icon = s.icon
|
|
44
44
|
if (s.type === "custom") {
|
|
45
45
|
const active = customSectionActive(s.url, pathname)
|
|
46
46
|
return (
|
|
47
47
|
<SidebarMenuItem
|
|
48
48
|
key={s.url}
|
|
49
|
-
className={mobileOnly ? "
|
|
49
|
+
className={mobileOnly ? "lg:hidden" : undefined}
|
|
50
50
|
>
|
|
51
51
|
<SidebarMenuButton
|
|
52
52
|
isActive={active}
|
|
@@ -67,7 +67,7 @@ export function SidebarSections() {
|
|
|
67
67
|
return (
|
|
68
68
|
<SidebarMenuItem
|
|
69
69
|
key={s.prefix}
|
|
70
|
-
className={mobileOnly ? "
|
|
70
|
+
className={mobileOnly ? "lg:hidden" : undefined}
|
|
71
71
|
>
|
|
72
72
|
<SidebarMenuButton
|
|
73
73
|
isActive={active}
|
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
import { config } from "livemark:virtual"
|
|
2
|
+
import { useLocation } from "@tanstack/react-router"
|
|
2
3
|
import { prefixUrl } from "../helpers/prefix-url.ts"
|
|
4
|
+
import { activeSiteSection } from "../helpers/section.ts"
|
|
3
5
|
import defaultLogo from "../assets/logo.svg"
|
|
4
6
|
|
|
5
|
-
/** Renders the site logo, title, and description in the sidebar header
|
|
7
|
+
/** Renders the site logo, title, and description in the sidebar header.
|
|
8
|
+
* When the current pathname falls inside a section that defines its own
|
|
9
|
+
* `siteTitle` / `siteDescription`, those override the global config. */
|
|
6
10
|
export function SiteTitle() {
|
|
11
|
+
const pathname = useLocation({ select: l => l.pathname })
|
|
12
|
+
const section = activeSiteSection(pathname, config.sections ?? [])
|
|
13
|
+
const title = section?.siteTitle ?? config.title
|
|
14
|
+
const description = section?.siteDescription ?? config.description
|
|
7
15
|
const logo = prefixUrl(config.logo ?? defaultLogo, config.base)
|
|
8
16
|
return (
|
|
9
17
|
<div className="flex items-end gap-2.5 text-sm">
|
|
10
|
-
<img src={logo} alt={
|
|
18
|
+
<img src={logo} alt={title} className="size-6.5 mb-1.25" />
|
|
11
19
|
<div className="flex flex-col gap-0.5 leading-none">
|
|
12
|
-
<span className="font-semibold">{
|
|
13
|
-
<span className="text-xs opacity-80">{
|
|
20
|
+
<span className="font-semibold">{title}</span>
|
|
21
|
+
<span className="text-xs opacity-80">{description}</span>
|
|
14
22
|
</div>
|
|
15
23
|
</div>
|
|
16
24
|
)
|
|
@@ -16,7 +16,6 @@ import remarkMath from "remark-math"
|
|
|
16
16
|
import { loadConfig } from "./actions/config/load.ts"
|
|
17
17
|
import { buildChangelog, cacheIncludeGlob } from "./actions/changelog/build.ts"
|
|
18
18
|
import { Article } from "./models/article.ts"
|
|
19
|
-
import { pickDefaultIcon } from "./helpers/article-icon.ts"
|
|
20
19
|
import slugify from "@sindresorhus/slugify"
|
|
21
20
|
import remarkCustomHeadingId from "./plugins/remark-custom-heading-id.ts"
|
|
22
21
|
import { remarkCallout } from "./plugins/remark-callout.ts"
|
|
@@ -128,7 +127,7 @@ const articles = defineCollection({
|
|
|
128
127
|
const filePath = resolve(config.root, doc._meta.filePath)
|
|
129
128
|
const content = resolveIncludes(doc.content, filePath)
|
|
130
129
|
const title = doc.title ?? extractTitle(content, doc._meta.path)
|
|
131
|
-
const icon = doc.icon
|
|
130
|
+
const icon = doc.icon
|
|
132
131
|
const path = doc.path ?? toPath(doc._meta.filePath)
|
|
133
132
|
// The article's h1 is rendered from `title` by the layout, so strip any
|
|
134
133
|
// leading h1 in the body to avoid two h1s side-by-side (especially when
|
|
@@ -1,43 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { icons } from "lucide-react"
|
|
2
2
|
|
|
3
|
-
/** Resolve a Lucide icon by kebab-case name
|
|
4
|
-
|
|
3
|
+
/** Resolve a Lucide icon by kebab-case name. Returns undefined when no
|
|
4
|
+
* name is given or the name doesn't match a known icon — articles
|
|
5
|
+
* without an explicit `icon` frontmatter render with no icon at all. */
|
|
6
|
+
export function resolveArticleIcon(name: string | undefined) {
|
|
7
|
+
if (!name) return undefined
|
|
5
8
|
const key = toPascalCase(name)
|
|
6
|
-
return key in icons ? icons[key as keyof typeof icons] :
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
/** Curated set of icons used as default fallbacks for articles without one. */
|
|
10
|
-
const defaultIconNames = [
|
|
11
|
-
"binary",
|
|
12
|
-
"blocks",
|
|
13
|
-
"brain-circuit",
|
|
14
|
-
"bug",
|
|
15
|
-
"cloud",
|
|
16
|
-
"code",
|
|
17
|
-
"cpu",
|
|
18
|
-
"database",
|
|
19
|
-
"file-code",
|
|
20
|
-
"git-branch",
|
|
21
|
-
"globe",
|
|
22
|
-
"hard-drive",
|
|
23
|
-
"layers",
|
|
24
|
-
"network",
|
|
25
|
-
"rocket",
|
|
26
|
-
"server",
|
|
27
|
-
"shield",
|
|
28
|
-
"sliders-horizontal",
|
|
29
|
-
"terminal",
|
|
30
|
-
"webhook",
|
|
31
|
-
"zap",
|
|
32
|
-
]
|
|
33
|
-
|
|
34
|
-
/** Deterministic icon name based on article path */
|
|
35
|
-
export function pickDefaultIcon(path: string) {
|
|
36
|
-
let hash = 0
|
|
37
|
-
for (let i = 0; i < path.length; i++) {
|
|
38
|
-
hash = (hash * 31 + path.charCodeAt(i)) | 0
|
|
39
|
-
}
|
|
40
|
-
return defaultIconNames[Math.abs(hash) % defaultIconNames.length]!
|
|
9
|
+
return key in icons ? icons[key as keyof typeof icons] : undefined
|
|
41
10
|
}
|
|
42
11
|
|
|
43
12
|
function toPascalCase(name: string) {
|
|
@@ -1,15 +1,34 @@
|
|
|
1
1
|
import type { Section } from "../models/section.ts"
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
article
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
/** Return the section whose prefix or internal-URL best matches the
|
|
4
|
+
* given pathname. Routed sections (article/blog/changelog) match by
|
|
5
|
+
* `prefix`. Custom sections with internal URLs (`/`-leading, not
|
|
6
|
+
* `//`) match by their URL — with the same exact-`/` guard as
|
|
7
|
+
* `customSectionActive` so a custom URL of `/` only activates on
|
|
8
|
+
* the home page itself. Custom sections with absolute URLs never
|
|
9
|
+
* match. Longest match wins. */
|
|
10
|
+
export function activeSiteSection(pathname: string, sections: Section[]) {
|
|
11
|
+
const current = `/${pathname.replace(/^\/|\/$/g, "")}/`
|
|
12
|
+
let best: Section | undefined
|
|
13
|
+
let bestLen = 0
|
|
14
|
+
for (const s of sections) {
|
|
15
|
+
const prefix = sectionMatchPrefix(s)
|
|
16
|
+
if (!prefix) continue
|
|
17
|
+
if (s.type === "custom" && prefix === "/" && current !== "/") continue
|
|
18
|
+
if (!current.startsWith(prefix)) continue
|
|
19
|
+
if (prefix.length > bestLen) {
|
|
20
|
+
best = s
|
|
21
|
+
bestLen = prefix.length
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return best
|
|
7
25
|
}
|
|
8
26
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
27
|
+
function sectionMatchPrefix(s: Section): string | undefined {
|
|
28
|
+
if (s.type !== "custom") return s.prefix
|
|
29
|
+
if (!s.url.startsWith("/") || s.url.startsWith("//")) return undefined
|
|
30
|
+
const stripped = s.url.split(/[?#]/)[0] ?? ""
|
|
31
|
+
return `/${stripped.replace(/^\/|\/$/g, "")}/`
|
|
13
32
|
}
|
|
14
33
|
|
|
15
34
|
/** Whether a custom section's URL should highlight as active for the
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { describe, expect, it } from "vite-plus/test"
|
|
2
|
+
import type { Section } from "../models/section.ts"
|
|
2
3
|
import {
|
|
4
|
+
activeSiteSection,
|
|
3
5
|
customSectionActive,
|
|
4
6
|
matchSection,
|
|
5
7
|
partitionBySection,
|
|
@@ -111,3 +113,70 @@ describe("customSectionActive", () => {
|
|
|
111
113
|
expect(customSectionActive("/about#top", "/about/team/")).toBe(true)
|
|
112
114
|
})
|
|
113
115
|
})
|
|
116
|
+
|
|
117
|
+
describe("activeSiteSection", () => {
|
|
118
|
+
const docs: Section = {
|
|
119
|
+
type: "article",
|
|
120
|
+
title: "Docs",
|
|
121
|
+
prefix: "/",
|
|
122
|
+
position: "header",
|
|
123
|
+
}
|
|
124
|
+
const blog: Section = {
|
|
125
|
+
type: "blog",
|
|
126
|
+
title: "Blog",
|
|
127
|
+
prefix: "/blog/",
|
|
128
|
+
position: "header",
|
|
129
|
+
}
|
|
130
|
+
const apiCustom: Section = {
|
|
131
|
+
type: "custom",
|
|
132
|
+
title: "API",
|
|
133
|
+
url: "/api/v2",
|
|
134
|
+
position: "header",
|
|
135
|
+
}
|
|
136
|
+
const githubCustom: Section = {
|
|
137
|
+
type: "custom",
|
|
138
|
+
title: "GitHub",
|
|
139
|
+
url: "https://github.com/example/repo",
|
|
140
|
+
position: "header",
|
|
141
|
+
}
|
|
142
|
+
const homeCustom: Section = {
|
|
143
|
+
type: "custom",
|
|
144
|
+
title: "Home",
|
|
145
|
+
url: "/",
|
|
146
|
+
position: "header",
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
it("should return the routed section matching by prefix", () => {
|
|
150
|
+
expect(activeSiteSection("/blog/post/", [docs, blog])?.title).toBe("Blog")
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
it("should return the custom section matching by internal url", () => {
|
|
154
|
+
expect(activeSiteSection("/api/v2/users/", [docs, apiCustom])?.title).toBe(
|
|
155
|
+
"API",
|
|
156
|
+
)
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
it("should pick the longest match among overlapping prefixes", () => {
|
|
160
|
+
expect(activeSiteSection("/blog/post/", [docs, blog])?.title).toBe("Blog")
|
|
161
|
+
expect(activeSiteSection("/api/v2/foo/", [docs, apiCustom])?.title).toBe(
|
|
162
|
+
"API",
|
|
163
|
+
)
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
it("should ignore custom sections with absolute URLs", () => {
|
|
167
|
+
expect(activeSiteSection("/anything/", [githubCustom])).toBeUndefined()
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
it("should match custom URL `/` only on the home page", () => {
|
|
171
|
+
expect(activeSiteSection("/", [homeCustom])?.title).toBe("Home")
|
|
172
|
+
expect(activeSiteSection("/about/", [homeCustom])).toBeUndefined()
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
it("should let routed `/` match descendants while custom `/` does not", () => {
|
|
176
|
+
expect(activeSiteSection("/about/", [docs, homeCustom])?.title).toBe("Docs")
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
it("should return undefined when no section matches", () => {
|
|
180
|
+
expect(activeSiteSection("/anything/", [blog, apiCustom])).toBeUndefined()
|
|
181
|
+
})
|
|
182
|
+
})
|
package/source/hooks/media.ts
CHANGED
package/source/models/article.ts
CHANGED
package/source/models/section.ts
CHANGED
|
@@ -4,6 +4,8 @@ const BaseSection = z.object({
|
|
|
4
4
|
icon: z.string().optional(),
|
|
5
5
|
title: z.string(),
|
|
6
6
|
position: z.enum(["header", "sidebar"]).default("header"),
|
|
7
|
+
siteTitle: z.string().optional(),
|
|
8
|
+
siteDescription: z.string().optional(),
|
|
7
9
|
})
|
|
8
10
|
|
|
9
11
|
/** Default section: renders articles. */
|