@sprig-and-prose/tutorial-svelte 0.2.3 → 0.2.4
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/package.json +1 -1
- package/src/kit/Tutorial.svelte +56 -6
- package/src/lib/createTutorialLoad.js +69 -0
package/package.json
CHANGED
package/src/kit/Tutorial.svelte
CHANGED
|
@@ -18,6 +18,9 @@
|
|
|
18
18
|
| {
|
|
19
19
|
kind: 'stub';
|
|
20
20
|
firstPagePath: string;
|
|
21
|
+
title?: string;
|
|
22
|
+
pages?: Array<{ title: string; path: string }>;
|
|
23
|
+
introContent?: string | null;
|
|
21
24
|
}
|
|
22
25
|
| {
|
|
23
26
|
kind: 'error';
|
|
@@ -240,8 +243,28 @@
|
|
|
240
243
|
</div>
|
|
241
244
|
{:else if data.kind === 'stub'}
|
|
242
245
|
<div class="stub-page">
|
|
243
|
-
|
|
244
|
-
|
|
246
|
+
{#if data.title}
|
|
247
|
+
<h1>{data.title}</h1>
|
|
248
|
+
{/if}
|
|
249
|
+
{#if data.introContent}
|
|
250
|
+
<div class="stub-intro">
|
|
251
|
+
{@html data.introContent}
|
|
252
|
+
</div>
|
|
253
|
+
{/if}
|
|
254
|
+
{#if data.pages && data.pages.length > 0}
|
|
255
|
+
<h2 class="stub-toc-header">Table of contents</h2>
|
|
256
|
+
<ol class="stub-toc-list">
|
|
257
|
+
{#each data.pages as page}
|
|
258
|
+
<li class="stub-toc-item">
|
|
259
|
+
<a href={page.path} class="stub-toc-link">{page.title}</a>
|
|
260
|
+
</li>
|
|
261
|
+
{/each}
|
|
262
|
+
</ol>
|
|
263
|
+
{/if}
|
|
264
|
+
<nav class="tutorial-nav">
|
|
265
|
+
<span class="nav-link nav-previous nav-placeholder"></span>
|
|
266
|
+
<a href={data.firstPagePath} class="nav-link nav-next">Next</a>
|
|
267
|
+
</nav>
|
|
245
268
|
</div>
|
|
246
269
|
{:else if data.kind === 'error'}
|
|
247
270
|
<div class="error-state">
|
|
@@ -306,13 +329,40 @@
|
|
|
306
329
|
.stub-page {
|
|
307
330
|
max-width: 70ch;
|
|
308
331
|
margin: 0 auto;
|
|
309
|
-
padding:
|
|
310
|
-
|
|
332
|
+
padding: 2rem 1rem;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.stub-page h1 {
|
|
336
|
+
margin: 0;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.stub-intro {
|
|
340
|
+
margin: 2rem 0;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
.stub-toc-header {
|
|
344
|
+
margin: 2rem 0 1rem 0;
|
|
345
|
+
font-size: 1.25rem;
|
|
346
|
+
font-weight: 600;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.stub-toc-list {
|
|
350
|
+
padding-left: 1.5rem;
|
|
351
|
+
margin: 0 0 2rem 0;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
.stub-toc-item {
|
|
355
|
+
margin-bottom: 1rem;
|
|
311
356
|
}
|
|
312
357
|
|
|
313
|
-
.stub-
|
|
358
|
+
.stub-toc-link {
|
|
314
359
|
color: var(--text-color);
|
|
315
|
-
text-decoration:
|
|
360
|
+
text-decoration: none;
|
|
361
|
+
font-size: 1.125rem;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.stub-toc-link:hover {
|
|
365
|
+
color: var(--text-secondary);
|
|
316
366
|
}
|
|
317
367
|
|
|
318
368
|
.error-state {
|
|
@@ -125,10 +125,79 @@ export function createTutorialLoad({ routeBase, modules, renderMarkdown, enableD
|
|
|
125
125
|
// Handle stub route (segment root)
|
|
126
126
|
if (resolved.type === 'stub') {
|
|
127
127
|
const firstPagePath = `${routeBase}/${resolved.segmentPath}/1`;
|
|
128
|
+
|
|
129
|
+
// Load and parse the segment file to get title and pages
|
|
130
|
+
const module = modules[resolved.filePath];
|
|
131
|
+
let content = '';
|
|
132
|
+
|
|
133
|
+
// Handle different module formats (eager vs lazy loading)
|
|
134
|
+
if (typeof module === 'function') {
|
|
135
|
+
// Lazy-loaded module - await it
|
|
136
|
+
const loaded = await module();
|
|
137
|
+
content = typeof loaded === 'string' ? loaded : loaded?.default || String(loaded?.default || '');
|
|
138
|
+
} else if (typeof module === 'string') {
|
|
139
|
+
content = module;
|
|
140
|
+
} else if (module?.default) {
|
|
141
|
+
content = typeof module.default === 'string' ? module.default : String(module.default);
|
|
142
|
+
} else if (typeof module === 'object' && 'default' in module) {
|
|
143
|
+
content = String(module.default);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const { pages, segmentTitle } = parseSegment(content);
|
|
147
|
+
|
|
148
|
+
// Extract content between H1 and first H2
|
|
149
|
+
let introContent = '';
|
|
150
|
+
const lines = content.split('\n');
|
|
151
|
+
let h1Index = -1;
|
|
152
|
+
let firstH2Index = -1;
|
|
153
|
+
let inCodeBlock = false;
|
|
154
|
+
|
|
155
|
+
for (let i = 0; i < lines.length; i++) {
|
|
156
|
+
const line = lines[i];
|
|
157
|
+
const trimmed = line.trim();
|
|
158
|
+
const rawTrimmed = line.trimStart();
|
|
159
|
+
|
|
160
|
+
// Check for fenced code blocks
|
|
161
|
+
if (/^[`~]{3,}/.test(rawTrimmed)) {
|
|
162
|
+
inCodeBlock = !inCodeBlock;
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Find H1
|
|
167
|
+
if (h1Index === -1 && !inCodeBlock && trimmed.startsWith('# ') && !trimmed.startsWith('##')) {
|
|
168
|
+
h1Index = i;
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Find first H2
|
|
173
|
+
if (h1Index !== -1 && firstH2Index === -1 && !inCodeBlock && trimmed.startsWith('## ') && !trimmed.startsWith('###')) {
|
|
174
|
+
firstH2Index = i;
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Extract content between H1 and first H2
|
|
180
|
+
if (h1Index !== -1 && firstH2Index !== -1 && firstH2Index > h1Index + 1) {
|
|
181
|
+
const introLines = lines.slice(h1Index + 1, firstH2Index);
|
|
182
|
+
const introText = introLines.join('\n').trim();
|
|
183
|
+
if (introText) {
|
|
184
|
+
introContent = await renderMarkdown(introText);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Build pages array with paths
|
|
189
|
+
const pagesWithPaths = pages.map((page, index) => ({
|
|
190
|
+
title: page.title,
|
|
191
|
+
path: `${routeBase}/${resolved.segmentPath}/${index + 1}`,
|
|
192
|
+
}));
|
|
193
|
+
|
|
128
194
|
return {
|
|
129
195
|
kind: 'stub',
|
|
130
196
|
segmentPath: resolved.segmentPath,
|
|
131
197
|
firstPagePath,
|
|
198
|
+
title: segmentTitle || '',
|
|
199
|
+
pages: pagesWithPaths,
|
|
200
|
+
introContent: introContent || null,
|
|
132
201
|
};
|
|
133
202
|
}
|
|
134
203
|
|