@raystack/chronicle 0.6.1 → 0.7.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/dist/cli/index.js +174 -3
- package/package.json +1 -1
- package/src/lib/config.ts +5 -0
- package/src/lib/source.ts +12 -3
- package/src/pages/LandingPage.module.css +137 -24
- package/src/pages/LandingPage.tsx +23 -7
- package/src/server/entry-server.tsx +4 -1
- package/src/server/vite-config.ts +3 -0
- package/src/themes/paper/ChapterNav.module.css +23 -12
- package/src/themes/paper/ChapterNav.tsx +1 -17
- package/src/themes/paper/Layout.module.css +57 -16
- package/src/themes/paper/Layout.tsx +71 -17
- package/src/themes/paper/Page.module.css +89 -37
- package/src/themes/paper/Page.tsx +89 -53
- package/src/themes/paper/ReaderModeContext.tsx +28 -0
- package/src/themes/paper/ReadingProgress.tsx +1 -0
- package/src/themes/paper/fonts/DepartureMono-Regular.woff2 +0 -0
- package/src/themes/registry.ts +1 -1
- package/src/types/config.ts +1 -0
- package/src/types/content.ts +1 -0
package/dist/cli/index.js
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
2
4
|
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
function __accessProp(key) {
|
|
8
|
+
return this[key];
|
|
9
|
+
}
|
|
10
|
+
var __toESMCache_node;
|
|
11
|
+
var __toESMCache_esm;
|
|
12
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
13
|
+
var canCache = mod != null && typeof mod === "object";
|
|
14
|
+
if (canCache) {
|
|
15
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
16
|
+
var cached = cache.get(mod);
|
|
17
|
+
if (cached)
|
|
18
|
+
return cached;
|
|
19
|
+
}
|
|
20
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
21
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
22
|
+
for (let key of __getOwnPropNames(mod))
|
|
23
|
+
if (!__hasOwnProp.call(to, key))
|
|
24
|
+
__defProp(to, key, {
|
|
25
|
+
get: __accessProp.bind(mod, key),
|
|
26
|
+
enumerable: true
|
|
27
|
+
});
|
|
28
|
+
if (canCache)
|
|
29
|
+
cache.set(mod, to);
|
|
30
|
+
return to;
|
|
31
|
+
};
|
|
32
|
+
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
3
33
|
var __returnValue = (v) => v;
|
|
4
34
|
function __exportSetter(name, newValue) {
|
|
5
35
|
this[name] = __returnValue.bind(null, newValue);
|
|
@@ -33,11 +63,146 @@ var init_remark_strip_md_extensions = __esm(() => {
|
|
|
33
63
|
remark_strip_md_extensions_default = remarkStripMdExtensions;
|
|
34
64
|
});
|
|
35
65
|
|
|
36
|
-
//
|
|
66
|
+
// ../../node_modules/.bun/reading-time@1.5.0/node_modules/reading-time/lib/reading-time.js
|
|
67
|
+
var require_reading_time = __commonJS((exports, module) => {
|
|
68
|
+
/*!
|
|
69
|
+
* reading-time
|
|
70
|
+
* Copyright (c) Nicolas Gryman <ngryman@gmail.com>
|
|
71
|
+
* MIT Licensed
|
|
72
|
+
*/
|
|
73
|
+
function codeIsInRanges(number, arrayOfRanges) {
|
|
74
|
+
return arrayOfRanges.some(([lowerBound, upperBound]) => lowerBound <= number && number <= upperBound);
|
|
75
|
+
}
|
|
76
|
+
function isCJK(c) {
|
|
77
|
+
if (typeof c !== "string") {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
const charCode = c.charCodeAt(0);
|
|
81
|
+
return codeIsInRanges(charCode, [
|
|
82
|
+
[12352, 12447],
|
|
83
|
+
[19968, 40959],
|
|
84
|
+
[44032, 55203],
|
|
85
|
+
[131072, 191456]
|
|
86
|
+
]);
|
|
87
|
+
}
|
|
88
|
+
function isAnsiWordBound(c) {
|
|
89
|
+
return `
|
|
90
|
+
\r `.includes(c);
|
|
91
|
+
}
|
|
92
|
+
function isPunctuation(c) {
|
|
93
|
+
if (typeof c !== "string") {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
const charCode = c.charCodeAt(0);
|
|
97
|
+
return codeIsInRanges(charCode, [
|
|
98
|
+
[33, 47],
|
|
99
|
+
[58, 64],
|
|
100
|
+
[91, 96],
|
|
101
|
+
[123, 126],
|
|
102
|
+
[12288, 12351],
|
|
103
|
+
[65280, 65519]
|
|
104
|
+
]);
|
|
105
|
+
}
|
|
106
|
+
function readingTime(text, options = {}) {
|
|
107
|
+
let words = 0, start = 0, end = text.length - 1;
|
|
108
|
+
const wordsPerMinute = options.wordsPerMinute || 200;
|
|
109
|
+
const isWordBound = options.wordBound || isAnsiWordBound;
|
|
110
|
+
while (isWordBound(text[start]))
|
|
111
|
+
start++;
|
|
112
|
+
while (isWordBound(text[end]))
|
|
113
|
+
end--;
|
|
114
|
+
const normalizedText = `${text}
|
|
115
|
+
`;
|
|
116
|
+
for (let i = start;i <= end; i++) {
|
|
117
|
+
if (isCJK(normalizedText[i]) || !isWordBound(normalizedText[i]) && (isWordBound(normalizedText[i + 1]) || isCJK(normalizedText[i + 1]))) {
|
|
118
|
+
words++;
|
|
119
|
+
}
|
|
120
|
+
if (isCJK(normalizedText[i])) {
|
|
121
|
+
while (i <= end && (isPunctuation(normalizedText[i + 1]) || isWordBound(normalizedText[i + 1]))) {
|
|
122
|
+
i++;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const minutes = words / wordsPerMinute;
|
|
127
|
+
const time = Math.round(minutes * 60 * 1000);
|
|
128
|
+
const displayed = Math.ceil(minutes.toFixed(2));
|
|
129
|
+
return {
|
|
130
|
+
text: displayed + " min read",
|
|
131
|
+
minutes,
|
|
132
|
+
time,
|
|
133
|
+
words
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
module.exports = readingTime;
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// ../../node_modules/.bun/reading-time@1.5.0/node_modules/reading-time/lib/stream.js
|
|
140
|
+
var require_stream = __commonJS((exports, module) => {
|
|
141
|
+
/*!
|
|
142
|
+
* reading-time
|
|
143
|
+
* Copyright (c) Nicolas Gryman <ngryman@gmail.com>
|
|
144
|
+
* MIT Licensed
|
|
145
|
+
*/
|
|
146
|
+
var readingTime = require_reading_time();
|
|
147
|
+
var Transform = __require("stream").Transform;
|
|
148
|
+
var util = __require("util");
|
|
149
|
+
function ReadingTimeStream(options) {
|
|
150
|
+
if (!(this instanceof ReadingTimeStream)) {
|
|
151
|
+
return new ReadingTimeStream(options);
|
|
152
|
+
}
|
|
153
|
+
Transform.call(this, { objectMode: true });
|
|
154
|
+
this.options = options || {};
|
|
155
|
+
this.stats = {
|
|
156
|
+
minutes: 0,
|
|
157
|
+
time: 0,
|
|
158
|
+
words: 0
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
util.inherits(ReadingTimeStream, Transform);
|
|
162
|
+
ReadingTimeStream.prototype._transform = function(chunk, encoding, callback) {
|
|
163
|
+
const stats = readingTime(chunk.toString(encoding), this.options);
|
|
164
|
+
this.stats.minutes += stats.minutes;
|
|
165
|
+
this.stats.time += stats.time;
|
|
166
|
+
this.stats.words += stats.words;
|
|
167
|
+
callback();
|
|
168
|
+
};
|
|
169
|
+
ReadingTimeStream.prototype._flush = function(callback) {
|
|
170
|
+
this.stats.text = Math.ceil(this.stats.minutes.toFixed(2)) + " min read";
|
|
171
|
+
this.push(this.stats);
|
|
172
|
+
callback();
|
|
173
|
+
};
|
|
174
|
+
module.exports = ReadingTimeStream;
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// ../../node_modules/.bun/reading-time@1.5.0/node_modules/reading-time/index.js
|
|
178
|
+
var require_reading_time2 = __commonJS((exports, module) => {
|
|
179
|
+
exports.default = module.exports = require_reading_time();
|
|
180
|
+
module.exports.readingTimeStream = require_stream();
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// ../../node_modules/.bun/remark-reading-time@2.1.0/node_modules/remark-reading-time/index.js
|
|
37
184
|
import { visit as visit2 } from "unist-util-visit";
|
|
185
|
+
function readingTime({
|
|
186
|
+
attribute = "readingTime"
|
|
187
|
+
} = {}) {
|
|
188
|
+
return function(info, file) {
|
|
189
|
+
let text = "";
|
|
190
|
+
visit2(info, ["text", "code"], (node) => {
|
|
191
|
+
text += node.value;
|
|
192
|
+
});
|
|
193
|
+
file.data[attribute] = import_reading_time.default(text);
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
var import_reading_time;
|
|
197
|
+
var init_remark_reading_time = __esm(() => {
|
|
198
|
+
import_reading_time = __toESM(require_reading_time2(), 1);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// src/lib/remark-unused-directives.ts
|
|
202
|
+
import { visit as visit3 } from "unist-util-visit";
|
|
38
203
|
var remarkUnusedDirectives = () => {
|
|
39
204
|
return (tree) => {
|
|
40
|
-
|
|
205
|
+
visit3(tree, ["textDirective"], (node) => {
|
|
41
206
|
const directive = node;
|
|
42
207
|
if (!directive.data) {
|
|
43
208
|
const hasAttributes = directive.attributes && Object.keys(directive.attributes).length > 0;
|
|
@@ -105,6 +270,7 @@ async function createViteConfig(options) {
|
|
|
105
270
|
mdx({
|
|
106
271
|
default: defineFumadocsConfig({
|
|
107
272
|
mdxOptions: {
|
|
273
|
+
valueToExport: ["readingTime"],
|
|
108
274
|
remarkPlugins: [
|
|
109
275
|
remarkDirective,
|
|
110
276
|
[remarkDirectiveAdmonition, {
|
|
@@ -126,7 +292,8 @@ async function createViteConfig(options) {
|
|
|
126
292
|
}],
|
|
127
293
|
remark_unused_directives_default,
|
|
128
294
|
remark_strip_md_extensions_default,
|
|
129
|
-
remarkMdxMermaid
|
|
295
|
+
remarkMdxMermaid,
|
|
296
|
+
readingTime
|
|
130
297
|
]
|
|
131
298
|
}
|
|
132
299
|
})
|
|
@@ -183,6 +350,7 @@ async function createViteConfig(options) {
|
|
|
183
350
|
}
|
|
184
351
|
var init_vite_config = __esm(() => {
|
|
185
352
|
init_remark_strip_md_extensions();
|
|
353
|
+
init_remark_reading_time();
|
|
186
354
|
init_remark_unused_directives();
|
|
187
355
|
});
|
|
188
356
|
|
|
@@ -266,6 +434,7 @@ var dirNameSchema = z.string().min(1).refine((s) => DIR_NAME_PATTERN.test(s) &&
|
|
|
266
434
|
var contentEntrySchema = z.object({
|
|
267
435
|
dir: dirNameSchema,
|
|
268
436
|
label: z.string().min(1),
|
|
437
|
+
description: z.string().optional(),
|
|
269
438
|
icon: z.string().optional()
|
|
270
439
|
});
|
|
271
440
|
var badgeVariantSchema = z.enum([
|
|
@@ -428,6 +597,7 @@ function getLatestContentRoots(config2) {
|
|
|
428
597
|
versionLabel: config2.latest?.label ?? null,
|
|
429
598
|
contentDir: c.dir,
|
|
430
599
|
contentLabel: c.label,
|
|
600
|
+
contentDescription: c.description,
|
|
431
601
|
contentIcon: c.icon,
|
|
432
602
|
fsPath: `content/${c.dir}`,
|
|
433
603
|
urlPrefix: `/${c.dir}`
|
|
@@ -442,6 +612,7 @@ function getVersionContentRoots(config2, versionDir) {
|
|
|
442
612
|
versionLabel: version.label,
|
|
443
613
|
contentDir: c.dir,
|
|
444
614
|
contentLabel: c.label,
|
|
615
|
+
contentDescription: c.description,
|
|
445
616
|
contentIcon: c.icon,
|
|
446
617
|
fsPath: `versions/${version.dir}/${c.dir}`,
|
|
447
618
|
urlPrefix: `/${version.dir}/${c.dir}`
|
package/package.json
CHANGED
package/src/lib/config.ts
CHANGED
|
@@ -35,6 +35,7 @@ export interface ContentRoot {
|
|
|
35
35
|
versionLabel: string | null
|
|
36
36
|
contentDir: string
|
|
37
37
|
contentLabel: string
|
|
38
|
+
contentDescription?: string
|
|
38
39
|
contentIcon?: string
|
|
39
40
|
fsPath: string
|
|
40
41
|
urlPrefix: string
|
|
@@ -46,6 +47,7 @@ export function getLatestContentRoots(config: ChronicleConfig): ContentRoot[] {
|
|
|
46
47
|
versionLabel: config.latest?.label ?? null,
|
|
47
48
|
contentDir: c.dir,
|
|
48
49
|
contentLabel: c.label,
|
|
50
|
+
contentDescription: c.description,
|
|
49
51
|
contentIcon: c.icon,
|
|
50
52
|
fsPath: `content/${c.dir}`,
|
|
51
53
|
urlPrefix: `/${c.dir}`,
|
|
@@ -64,6 +66,7 @@ export function getVersionContentRoots(
|
|
|
64
66
|
versionLabel: version.label,
|
|
65
67
|
contentDir: c.dir,
|
|
66
68
|
contentLabel: c.label,
|
|
69
|
+
contentDescription: c.description,
|
|
67
70
|
contentIcon: c.icon,
|
|
68
71
|
fsPath: `versions/${version.dir}/${c.dir}`,
|
|
69
72
|
urlPrefix: `/${version.dir}/${c.dir}`,
|
|
@@ -79,6 +82,7 @@ export interface VersionDescriptor {
|
|
|
79
82
|
|
|
80
83
|
export interface LandingEntry {
|
|
81
84
|
label: string
|
|
85
|
+
description?: string
|
|
82
86
|
href: string
|
|
83
87
|
contentDir: string
|
|
84
88
|
icon?: string
|
|
@@ -95,6 +99,7 @@ export function getLandingEntries(
|
|
|
95
99
|
|
|
96
100
|
return roots.map((r) => ({
|
|
97
101
|
label: r.contentLabel,
|
|
102
|
+
description: r.contentDescription,
|
|
98
103
|
href: r.urlPrefix,
|
|
99
104
|
contentDir: r.contentDir,
|
|
100
105
|
icon: r.contentIcon,
|
package/src/lib/source.ts
CHANGED
|
@@ -206,6 +206,7 @@ export function extractFrontmatter(page: { data: unknown }, fallbackTitle?: stri
|
|
|
206
206
|
order: d.order as number | undefined,
|
|
207
207
|
icon: d.icon as string | undefined,
|
|
208
208
|
lastModified: d.lastModified as string | undefined,
|
|
209
|
+
_readingTime: d._readingTime as number | undefined,
|
|
209
210
|
};
|
|
210
211
|
}
|
|
211
212
|
|
|
@@ -217,13 +218,20 @@ export function getOriginalPath(page: { data: unknown }): string {
|
|
|
217
218
|
return ((page.data as Record<string, unknown>)._originalPath as string) ?? '';
|
|
218
219
|
}
|
|
219
220
|
|
|
220
|
-
|
|
221
|
+
interface ReadingTime {
|
|
222
|
+
text: string;
|
|
223
|
+
minutes: number;
|
|
224
|
+
words: number;
|
|
225
|
+
time: number;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const ssrModules = import.meta.glob<{ default?: MDXContent; toc?: TableOfContents; readingTime?: ReadingTime }>(
|
|
221
229
|
'../../.content/**/*.{mdx,md}'
|
|
222
230
|
);
|
|
223
231
|
|
|
224
232
|
export async function loadPageModule(
|
|
225
233
|
relativePath: string
|
|
226
|
-
): Promise<{ default: MDXContent | null; toc: TableOfContents }> {
|
|
234
|
+
): Promise<{ default: MDXContent | null; toc: TableOfContents; _readingTime?: number }> {
|
|
227
235
|
if (!relativePath || relativePath.includes('..')) return { default: null, toc: [] };
|
|
228
236
|
const withoutExt = relativePath.replace(/\.(mdx|md)$/, '');
|
|
229
237
|
const key = relativePath.endsWith('.md')
|
|
@@ -232,5 +240,6 @@ export async function loadPageModule(
|
|
|
232
240
|
const loader = ssrModules[key];
|
|
233
241
|
if (!loader) return { default: null, toc: [] };
|
|
234
242
|
const mod = await loader();
|
|
235
|
-
|
|
243
|
+
const minutes = mod.readingTime?.minutes;
|
|
244
|
+
return { default: mod.default ?? null, toc: mod.toc ?? [], _readingTime: minutes != null ? Math.max(1, Math.round(minutes)) : undefined };
|
|
236
245
|
}
|
|
@@ -1,56 +1,169 @@
|
|
|
1
1
|
.root {
|
|
2
2
|
display: flex;
|
|
3
3
|
flex-direction: column;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
padding: var(--rs-space-12) var(--rs-space-9);
|
|
5
|
+
width: 100%;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.header {
|
|
9
|
+
display: flex;
|
|
10
|
+
align-items: center;
|
|
11
|
+
padding-bottom: var(--rs-space-10);
|
|
12
|
+
border-bottom: 0.5px solid var(--rs-color-border-base-primary);
|
|
8
13
|
}
|
|
9
14
|
|
|
10
15
|
.title {
|
|
11
|
-
|
|
12
|
-
font-
|
|
13
|
-
|
|
16
|
+
flex: 1;
|
|
17
|
+
font-family: var(--paper-font-mono, inherit);
|
|
18
|
+
font-size: 64px;
|
|
19
|
+
line-height: 1.1;
|
|
20
|
+
color: var(--rs-color-foreground-accent-primary);
|
|
21
|
+
text-transform: uppercase;
|
|
14
22
|
margin: 0;
|
|
23
|
+
font-weight: var(--rs-font-weight-regular);
|
|
15
24
|
}
|
|
16
25
|
|
|
17
26
|
.description {
|
|
27
|
+
width: 385px;
|
|
28
|
+
flex-shrink: 0;
|
|
29
|
+
font-family: var(--paper-font-body, inherit);
|
|
18
30
|
font-size: var(--rs-font-size-regular);
|
|
19
|
-
|
|
31
|
+
line-height: 1.4;
|
|
32
|
+
color: var(--rs-color-foreground-base-primary);
|
|
20
33
|
margin: 0;
|
|
21
34
|
}
|
|
22
35
|
|
|
23
36
|
.grid {
|
|
24
|
-
display:
|
|
25
|
-
|
|
26
|
-
gap: var(--rs-space-
|
|
37
|
+
display: flex;
|
|
38
|
+
flex-wrap: wrap;
|
|
39
|
+
gap: var(--rs-space-7);
|
|
40
|
+
margin-top: var(--rs-space-9);
|
|
27
41
|
}
|
|
28
42
|
|
|
29
43
|
.card {
|
|
30
44
|
display: flex;
|
|
31
45
|
flex-direction: column;
|
|
32
|
-
gap: var(--rs-space-
|
|
33
|
-
padding: var(--rs-space-
|
|
34
|
-
border: 1px solid var(--rs-color-border-base-primary);
|
|
35
|
-
border-radius: var(--rs-radius-3);
|
|
46
|
+
gap: var(--rs-space-7);
|
|
47
|
+
padding: var(--rs-space-2);
|
|
36
48
|
text-decoration: none;
|
|
37
49
|
color: inherit;
|
|
38
|
-
|
|
39
|
-
|
|
50
|
+
width: 360px;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.card:hover .cardImage {
|
|
54
|
+
border-color: var(--rs-color-foreground-accent-primary);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.cardImage {
|
|
58
|
+
position: relative;
|
|
59
|
+
display: flex;
|
|
60
|
+
align-items: center;
|
|
61
|
+
justify-content: center;
|
|
62
|
+
width: 100%;
|
|
63
|
+
aspect-ratio: 1.05;
|
|
64
|
+
min-height: 280px;
|
|
65
|
+
max-width: 360px;
|
|
66
|
+
background: var(--rs-color-background-accent-secondary);
|
|
67
|
+
border: 1px solid var(--rs-color-foreground-accent-primary);
|
|
68
|
+
overflow: hidden;
|
|
69
|
+
transition: border-color 0.15s ease;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.cardImage::before {
|
|
73
|
+
content: "";
|
|
74
|
+
position: absolute;
|
|
75
|
+
inset: 0;
|
|
76
|
+
background-image:
|
|
77
|
+
radial-gradient(circle, var(--rs-color-foreground-accent-primary) 1px, transparent 1px);
|
|
78
|
+
background-size: 10px 10px;
|
|
79
|
+
opacity: 0.3;
|
|
80
|
+
pointer-events: none;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.cardImageLabel {
|
|
84
|
+
position: absolute;
|
|
85
|
+
font-family: var(--paper-font-mono, monospace);
|
|
86
|
+
font-size: var(--rs-font-size-micro);
|
|
87
|
+
text-transform: uppercase;
|
|
88
|
+
color: var(--rs-color-foreground-base-secondary);
|
|
89
|
+
background: var(--rs-color-background-accent-secondary);
|
|
90
|
+
padding: var(--rs-space-1);
|
|
91
|
+
white-space: nowrap;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.cardImageLabelTop {
|
|
95
|
+
top: 19px;
|
|
96
|
+
left: -1px;
|
|
97
|
+
writing-mode: vertical-rl;
|
|
98
|
+
transform: rotate(180deg);
|
|
40
99
|
}
|
|
41
100
|
|
|
42
|
-
.
|
|
43
|
-
|
|
44
|
-
|
|
101
|
+
.cardImageLabelRight {
|
|
102
|
+
bottom: 19px;
|
|
103
|
+
right: -1px;
|
|
104
|
+
writing-mode: vertical-rl;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.cardIcon {
|
|
108
|
+
width: 90%;
|
|
109
|
+
height: 90%;
|
|
110
|
+
min-width: 64px;
|
|
111
|
+
min-height: 64px;
|
|
112
|
+
color: var(--rs-color-foreground-accent-primary);
|
|
113
|
+
opacity: 0.3;
|
|
114
|
+
transition: opacity 0.2s ease, transform 0.2s ease;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.card:hover .cardIcon {
|
|
118
|
+
opacity: 0.5;
|
|
119
|
+
transform: scale(1.05);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.card:hover .cardImage {
|
|
123
|
+
background: var(--rs-color-background-accent-primary);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.cardBody {
|
|
127
|
+
display: flex;
|
|
128
|
+
flex-direction: column;
|
|
129
|
+
gap: var(--rs-space-3);
|
|
130
|
+
padding-bottom: var(--rs-space-4);
|
|
45
131
|
}
|
|
46
132
|
|
|
47
133
|
.cardLabel {
|
|
134
|
+
font-family: var(--paper-font-mono, inherit);
|
|
48
135
|
font-size: var(--rs-font-size-large);
|
|
49
|
-
|
|
136
|
+
line-height: 1.4;
|
|
137
|
+
text-transform: uppercase;
|
|
138
|
+
color: var(--rs-color-foreground-base-primary);
|
|
50
139
|
}
|
|
51
140
|
|
|
52
|
-
.
|
|
141
|
+
.cardDescription {
|
|
142
|
+
font-family: var(--paper-font-body, inherit);
|
|
53
143
|
font-size: var(--rs-font-size-small);
|
|
54
|
-
|
|
55
|
-
|
|
144
|
+
line-height: 1.4;
|
|
145
|
+
color: var(--rs-color-foreground-base-primary);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
@media (max-width: 768px) {
|
|
149
|
+
.header {
|
|
150
|
+
flex-direction: column;
|
|
151
|
+
gap: var(--rs-space-5);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.title {
|
|
155
|
+
font-size: var(--rs-font-size-t4);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.description {
|
|
159
|
+
width: 100%;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.card {
|
|
163
|
+
width: 100%;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.cardImage {
|
|
167
|
+
max-width: 100%;
|
|
168
|
+
}
|
|
56
169
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { FolderIcon } from '@heroicons/react/24/outline';
|
|
1
2
|
import { Link as RouterLink } from 'react-router';
|
|
2
3
|
import { getLandingEntries } from '@/lib/config';
|
|
3
4
|
import { usePageContext } from '@/lib/page-context';
|
|
@@ -13,15 +14,30 @@ export function LandingPage() {
|
|
|
13
14
|
|
|
14
15
|
return (
|
|
15
16
|
<div className={styles.root}>
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
<div className={styles.header}>
|
|
18
|
+
<h1 className={styles.title}>{heading}</h1>
|
|
19
|
+
{config.site.description ? (
|
|
20
|
+
<p className={styles.description}>{config.site.description}</p>
|
|
21
|
+
) : null}
|
|
22
|
+
</div>
|
|
20
23
|
<div className={styles.grid}>
|
|
21
|
-
{entries.map((entry) => (
|
|
24
|
+
{entries.map((entry, i) => (
|
|
22
25
|
<RouterLink key={entry.href} to={entry.href} className={styles.card}>
|
|
23
|
-
<
|
|
24
|
-
|
|
26
|
+
<div className={styles.cardImage} aria-hidden='true'>
|
|
27
|
+
<span className={`${styles.cardImageLabel} ${styles.cardImageLabelTop}`}>
|
|
28
|
+
Fig_{String(i + 1).padStart(3, '0')}
|
|
29
|
+
</span>
|
|
30
|
+
<span className={`${styles.cardImageLabel} ${styles.cardImageLabelRight}`}>
|
|
31
|
+
[ {entry.label} ]
|
|
32
|
+
</span>
|
|
33
|
+
<FolderIcon className={styles.cardIcon} />
|
|
34
|
+
</div>
|
|
35
|
+
<div className={styles.cardBody}>
|
|
36
|
+
<span className={styles.cardLabel}>{entry.label}</span>
|
|
37
|
+
{entry.description ? (
|
|
38
|
+
<span className={styles.cardDescription}>{entry.description}</span>
|
|
39
|
+
) : null}
|
|
40
|
+
</div>
|
|
25
41
|
</RouterLink>
|
|
26
42
|
))}
|
|
27
43
|
</div>
|
|
@@ -54,7 +54,10 @@ export default {
|
|
|
54
54
|
const pageData = page
|
|
55
55
|
? {
|
|
56
56
|
slug: pageSlug,
|
|
57
|
-
frontmatter:
|
|
57
|
+
frontmatter: {
|
|
58
|
+
...extractFrontmatter(page, pageSlug[pageSlug.length - 1]),
|
|
59
|
+
_readingTime: mdxModule?._readingTime,
|
|
60
|
+
},
|
|
58
61
|
content: mdxModule?.default
|
|
59
62
|
? React.createElement(mdxModule.default, { components: mdxComponents })
|
|
60
63
|
: null,
|
|
@@ -8,6 +8,7 @@ import path from 'node:path';
|
|
|
8
8
|
import remarkDirective from 'remark-directive';
|
|
9
9
|
import { type InlineConfig } from 'vite';
|
|
10
10
|
import remarkStripMdExtensions from '../lib/remark-strip-md-extensions';
|
|
11
|
+
import remarkReadingTime from 'remark-reading-time';
|
|
11
12
|
import remarkUnusedDirectives from '../lib/remark-unused-directives';
|
|
12
13
|
|
|
13
14
|
function resolveOutputDir(projectRoot: string, preset?: string): string {
|
|
@@ -55,6 +56,7 @@ export async function createViteConfig(
|
|
|
55
56
|
mdx({
|
|
56
57
|
default: defineFumadocsConfig({
|
|
57
58
|
mdxOptions: {
|
|
59
|
+
valueToExport: ['readingTime'],
|
|
58
60
|
remarkPlugins: [
|
|
59
61
|
remarkDirective,
|
|
60
62
|
[remarkDirectiveAdmonition, {
|
|
@@ -77,6 +79,7 @@ export async function createViteConfig(
|
|
|
77
79
|
remarkUnusedDirectives,
|
|
78
80
|
remarkStripMdExtensions,
|
|
79
81
|
remarkMdxMermaid,
|
|
82
|
+
remarkReadingTime,
|
|
80
83
|
],
|
|
81
84
|
},
|
|
82
85
|
}),
|
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
.nav {
|
|
2
2
|
display: flex;
|
|
3
3
|
flex-direction: column;
|
|
4
|
-
gap: var(--rs-space-
|
|
4
|
+
gap: var(--rs-space-8);
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
.chapter {
|
|
8
8
|
display: flex;
|
|
9
9
|
flex-direction: column;
|
|
10
10
|
gap: var(--rs-space-2);
|
|
11
|
+
margin-top: var(--rs-space-8);
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
.chapterLabel {
|
|
15
|
+
font-family: var(--paper-font-mono);
|
|
14
16
|
font-size: var(--rs-font-size-small);
|
|
15
|
-
font-weight:
|
|
17
|
+
font-weight: var(--rs-font-weight-medium);
|
|
18
|
+
line-height: var(--rs-line-height-small);
|
|
19
|
+
letter-spacing: var(--rs-letter-spacing-small);
|
|
16
20
|
text-transform: uppercase;
|
|
17
|
-
|
|
18
|
-
|
|
21
|
+
color: var(--rs-color-foreground-base-secondary);
|
|
22
|
+
padding: 0 var(--rs-space-3);
|
|
19
23
|
white-space: nowrap;
|
|
20
24
|
overflow: hidden;
|
|
21
25
|
text-overflow: ellipsis;
|
|
@@ -27,30 +31,32 @@
|
|
|
27
31
|
margin: 0;
|
|
28
32
|
display: flex;
|
|
29
33
|
flex-direction: column;
|
|
30
|
-
gap: var(--rs-space-1);
|
|
31
|
-
padding-left: var(--rs-space-4);
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
.link {
|
|
35
37
|
display: flex;
|
|
36
38
|
align-items: center;
|
|
37
|
-
gap: var(--rs-space-
|
|
39
|
+
gap: var(--rs-space-3);
|
|
40
|
+
font-family: var(--paper-font-body);
|
|
38
41
|
font-size: var(--rs-font-size-small);
|
|
39
|
-
|
|
42
|
+
font-weight: var(--rs-font-weight-regular);
|
|
43
|
+
line-height: var(--rs-line-height-small);
|
|
44
|
+
letter-spacing: var(--rs-letter-spacing-small);
|
|
45
|
+
color: var(--rs-color-foreground-base-primary);
|
|
40
46
|
text-decoration: none;
|
|
41
|
-
padding: var(--rs-space-
|
|
47
|
+
padding: var(--rs-space-3);
|
|
48
|
+
border-radius: var(--rs-radius-2);
|
|
42
49
|
white-space: nowrap;
|
|
43
50
|
overflow: hidden;
|
|
44
51
|
text-overflow: ellipsis;
|
|
45
52
|
}
|
|
46
53
|
|
|
47
54
|
.link:hover {
|
|
48
|
-
color: var(--rs-color-foreground-
|
|
55
|
+
color: var(--rs-color-foreground-accent-primary);
|
|
49
56
|
}
|
|
50
57
|
|
|
51
58
|
.active {
|
|
52
59
|
color: var(--rs-color-foreground-accent-primary);
|
|
53
|
-
font-weight: 500;
|
|
54
60
|
}
|
|
55
61
|
|
|
56
62
|
.icon {
|
|
@@ -60,9 +66,14 @@
|
|
|
60
66
|
}
|
|
61
67
|
|
|
62
68
|
.subLabel {
|
|
69
|
+
font-family: var(--paper-font-mono);
|
|
63
70
|
font-size: var(--rs-font-size-small);
|
|
64
|
-
font-weight:
|
|
71
|
+
font-weight: var(--rs-font-weight-medium);
|
|
72
|
+
line-height: var(--rs-line-height-small);
|
|
73
|
+
letter-spacing: var(--rs-letter-spacing-small);
|
|
74
|
+
text-transform: uppercase;
|
|
65
75
|
color: var(--rs-color-foreground-base-secondary);
|
|
76
|
+
padding: 0 var(--rs-space-3);
|
|
66
77
|
margin-top: var(--rs-space-3);
|
|
67
78
|
display: block;
|
|
68
79
|
white-space: nowrap;
|