astro-tractstack 2.0.19 → 2.0.20
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/index.js +6 -32
- package/package.json +1 -1
- package/templates/src/components/codehooks/BunnyVideoSetup.tsx +1 -4
- package/templates/src/components/codehooks/FeaturedArticleSetup.tsx +0 -4
- package/templates/src/components/codehooks/ListContentSetup.tsx +1 -8
- package/templates/src/components/codehooks/ProductCardSetup.tsx +0 -2
- package/templates/src/components/codehooks/ProductGridSetup.tsx +0 -2
- package/templates/src/components/compositor/Compositor.tsx +3 -6
- package/templates/src/components/compositor/Node.tsx +13 -32
- package/templates/src/components/compositor/NodeWithGuid.tsx +49 -5
- package/templates/src/components/compositor/nodes/Pane.tsx +4 -21
- package/templates/src/components/compositor/nodes/Pane_DesignLibrary.tsx +27 -7
- package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag.tsx +3 -1
- package/templates/src/components/compositor/preview/OgImagePreview.tsx +0 -5
- package/templates/src/components/compositor/preview/PaneSnapshotGenerator.tsx +5 -6
- package/templates/src/components/compositor/preview/PanesPreviewGenerator.tsx +1 -0
- package/templates/src/components/edit/PanelSwitch.tsx +3 -24
- package/templates/src/components/edit/SettingsPanel.tsx +0 -1
- package/templates/src/components/edit/ToolMode.tsx +6 -14
- package/templates/src/components/edit/pane/AddPanePanel.tsx +45 -25
- package/templates/src/components/edit/pane/AddPanePanel_new.tsx +2 -8
- package/templates/src/components/edit/pane/AddPanePanel_paste.tsx +111 -0
- package/templates/src/components/edit/pane/RestylePaneModal.tsx +6 -13
- package/templates/src/components/edit/pane/steps/AiDesignStep.tsx +0 -5
- package/templates/src/components/edit/pane/steps/DesignLibraryStep.tsx +4 -11
- package/templates/src/components/edit/panels/StyleBreakPanel.tsx +1 -3
- package/templates/src/components/edit/panels/StyleElementPanel_update.tsx +0 -6
- package/templates/src/components/edit/panels/StyleImagePanel_update.tsx +0 -3
- package/templates/src/components/edit/panels/StyleLiElementPanel_update.tsx +0 -4
- package/templates/src/components/edit/panels/StyleLinkPanel_config.tsx +8 -5
- package/templates/src/components/edit/panels/StyleLinkPanel_update.tsx +1 -2
- package/templates/src/components/edit/panels/StyleParentPanel.tsx +1 -3
- package/templates/src/components/edit/panels/StyleParentPanel_update.tsx +2 -5
- package/templates/src/components/edit/panels/StyleWidgetPanel_config.tsx +2 -8
- package/templates/src/components/edit/panels/StyleWidgetPanel_update.tsx +0 -4
- package/templates/src/components/edit/state/SaveToLibraryModal.tsx +27 -16
- package/templates/src/components/edit/storyfragment/StoryFragmentConfigPanel.tsx +9 -26
- package/templates/src/components/edit/storyfragment/StoryFragmentPanel_og.tsx +7 -16
- package/templates/src/components/edit/storyfragment/StoryFragmentPanel_slug.tsx +5 -6
- package/templates/src/components/edit/widgets/InteractiveDisclosureWidget.tsx +0 -5
- package/templates/src/components/fields/BackgroundImageWrapper.tsx +1 -7
- package/templates/src/components/fields/ColorPickerCombo.tsx +8 -12
- package/templates/src/components/fields/ViewportComboBox.tsx +4 -6
- package/templates/src/stores/nodes.ts +14 -6
- package/templates/src/stores/storykeep.ts +3 -3
- package/templates/src/types/compositorTypes.ts +2 -0
- package/templates/src/utils/compositor/TemplatePanes.ts +0 -76
- package/templates/src/utils/compositor/aiPaneParser.ts +3 -1
- package/templates/src/utils/compositor/designLibraryHelper.ts +240 -17
- package/templates/src/utils/helpers.ts +5 -4
- package/utils/inject-files.ts +6 -32
- package/templates/src/components/compositor/preview/VisualBreakPreview.tsx +0 -154
- package/templates/src/components/edit/pane/PageGen_preview.tsx +0 -511
- package/templates/src/utils/compositor/processMarkdown.ts +0 -445
- package/templates/src/utils/compositor/templateMarkdownStyles.ts +0 -1273
|
@@ -1,445 +0,0 @@
|
|
|
1
|
-
import { ulid } from 'ulid';
|
|
2
|
-
import { NodesContext } from '@/stores/nodes';
|
|
3
|
-
import { fullContentMapStore } from '@/stores/storykeep';
|
|
4
|
-
import { tailwindToHex } from './tailwindColors';
|
|
5
|
-
import { SvgBreaks } from '@/constants/shapes';
|
|
6
|
-
import { findUniqueSlug } from '@/utils/helpers';
|
|
7
|
-
import type {
|
|
8
|
-
PageDesign,
|
|
9
|
-
PaneNode,
|
|
10
|
-
StoryFragmentNode,
|
|
11
|
-
} from '@/types/compositorTypes';
|
|
12
|
-
|
|
13
|
-
interface ProcessedPage {
|
|
14
|
-
sections: PageSection[];
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
type PageSectionType = 'intro' | 'content' | 'subcontent';
|
|
18
|
-
|
|
19
|
-
interface PageSection {
|
|
20
|
-
type: PageSectionType;
|
|
21
|
-
content: string;
|
|
22
|
-
children?: PageSection[]; // Optional because only content sections can have children
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
interface TitleSlugResponse {
|
|
26
|
-
title: string;
|
|
27
|
-
slug: string;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Generate title and slug using askLemur API
|
|
32
|
-
*/
|
|
33
|
-
async function getTitleSlug(
|
|
34
|
-
markdownContent: string,
|
|
35
|
-
existingSlugs: string[]
|
|
36
|
-
): Promise<TitleSlugResponse | null> {
|
|
37
|
-
if (
|
|
38
|
-
!markdownContent ||
|
|
39
|
-
markdownContent.trim() === '...' ||
|
|
40
|
-
markdownContent.trim().length === 0
|
|
41
|
-
) {
|
|
42
|
-
return null;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
try {
|
|
46
|
-
const backendUrl =
|
|
47
|
-
import.meta.env.PUBLIC_GO_BACKEND || 'http://localhost:8080';
|
|
48
|
-
const tenantId = import.meta.env.PUBLIC_TENANTID || 'default';
|
|
49
|
-
|
|
50
|
-
const response = await fetch(`${backendUrl}/api/v1/aai/askLemur`, {
|
|
51
|
-
method: 'POST',
|
|
52
|
-
headers: {
|
|
53
|
-
'Content-Type': 'application/json',
|
|
54
|
-
'X-Tenant-ID': tenantId,
|
|
55
|
-
},
|
|
56
|
-
credentials: 'include',
|
|
57
|
-
body: JSON.stringify({
|
|
58
|
-
prompt: `Generate a concise title (maximum 40-50 characters) and a URL-friendly slug (lowercase, only letters, numbers, and dashes, no spaces) that captures the essence of this markdown content. Return only a JSON object with "title" and "slug" keys.
|
|
59
|
-
|
|
60
|
-
Example response format:
|
|
61
|
-
{
|
|
62
|
-
"title": "Short Descriptive Title",
|
|
63
|
-
"slug": "short-descriptive-title"
|
|
64
|
-
}`,
|
|
65
|
-
input_text: markdownContent,
|
|
66
|
-
final_model: '',
|
|
67
|
-
temperature: 0.3,
|
|
68
|
-
max_tokens: 200,
|
|
69
|
-
}),
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
if (response.ok) {
|
|
73
|
-
const data = await response.json();
|
|
74
|
-
if (data.success && data.data?.response) {
|
|
75
|
-
let titleData;
|
|
76
|
-
try {
|
|
77
|
-
if (typeof data.data.response === 'string') {
|
|
78
|
-
titleData = JSON.parse(data.data.response);
|
|
79
|
-
} else {
|
|
80
|
-
titleData = data.data.response;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (titleData.title && titleData.slug) {
|
|
84
|
-
return {
|
|
85
|
-
title: findUniqueSlug(titleData.title, existingSlugs),
|
|
86
|
-
slug: findUniqueSlug(titleData.slug, existingSlugs),
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
} catch (parseError) {
|
|
90
|
-
console.error('Error parsing title data:', parseError);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
} catch (error) {
|
|
95
|
-
console.error('Error generating title:', error);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return null;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export function parsePageMarkdown(markdown: string): ProcessedPage {
|
|
102
|
-
const lines = markdown.split('\n');
|
|
103
|
-
const sections: PageSection[] = [];
|
|
104
|
-
let currentSection: PageSection | null = null;
|
|
105
|
-
let currentParagraph = '';
|
|
106
|
-
|
|
107
|
-
for (let i = 0; i < lines.length; i++) {
|
|
108
|
-
const line = lines[i].trim();
|
|
109
|
-
|
|
110
|
-
const h2Match = line.match(/^## (.*)/);
|
|
111
|
-
const h3Match = line.match(/^### (.*)/);
|
|
112
|
-
const h4Match = line.match(/^#### (.*)/);
|
|
113
|
-
|
|
114
|
-
if (h2Match && !currentSection) {
|
|
115
|
-
currentSection = {
|
|
116
|
-
type: 'intro',
|
|
117
|
-
content: `## ${h2Match[1]}\n\n`,
|
|
118
|
-
children: [],
|
|
119
|
-
};
|
|
120
|
-
} else if (h3Match) {
|
|
121
|
-
if (currentSection) {
|
|
122
|
-
if (currentParagraph) {
|
|
123
|
-
currentSection.content += currentParagraph.trim() + '\n\n';
|
|
124
|
-
currentParagraph = '';
|
|
125
|
-
}
|
|
126
|
-
sections.push(currentSection);
|
|
127
|
-
}
|
|
128
|
-
currentSection = {
|
|
129
|
-
type: 'content',
|
|
130
|
-
content: `### ${h3Match[1]}\n\n`,
|
|
131
|
-
children: [],
|
|
132
|
-
};
|
|
133
|
-
} else if (h4Match && currentSection) {
|
|
134
|
-
if (currentParagraph) {
|
|
135
|
-
currentSection.content += currentParagraph.trim() + '\n\n';
|
|
136
|
-
currentParagraph = '';
|
|
137
|
-
}
|
|
138
|
-
const subSection: PageSection = {
|
|
139
|
-
type: 'subcontent',
|
|
140
|
-
content: `#### ${h4Match[1]}\n\n`,
|
|
141
|
-
children: [],
|
|
142
|
-
};
|
|
143
|
-
if (!currentSection.children) currentSection.children = [];
|
|
144
|
-
currentSection.children.push(subSection);
|
|
145
|
-
// Reset currentParagraph to start collecting content for the subSection
|
|
146
|
-
currentParagraph = '';
|
|
147
|
-
} else if (currentSection) {
|
|
148
|
-
// This part handles content for both H3 and H4 sections
|
|
149
|
-
if (currentParagraph === '') {
|
|
150
|
-
currentParagraph = line;
|
|
151
|
-
} else {
|
|
152
|
-
currentParagraph += '\n' + line;
|
|
153
|
-
}
|
|
154
|
-
// If we're in a subSection, we need to update its content directly
|
|
155
|
-
if (
|
|
156
|
-
currentSection.children &&
|
|
157
|
-
currentSection.children.length > 0 &&
|
|
158
|
-
currentSection.type === 'content'
|
|
159
|
-
) {
|
|
160
|
-
const lastChild =
|
|
161
|
-
currentSection.children[currentSection.children.length - 1];
|
|
162
|
-
if (lastChild.type === 'subcontent') {
|
|
163
|
-
lastChild.content += line + '\n';
|
|
164
|
-
currentParagraph = ''; // Reset since we've added it directly to subSection
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Handle the last paragraph or section
|
|
171
|
-
if (currentSection) {
|
|
172
|
-
if (currentSection.type === 'intro' && currentParagraph) {
|
|
173
|
-
currentSection.content += currentParagraph.trim() + '\n\n';
|
|
174
|
-
} else if (currentParagraph) {
|
|
175
|
-
if (
|
|
176
|
-
currentSection.children &&
|
|
177
|
-
currentSection.children.length > 0 &&
|
|
178
|
-
currentSection.type === 'content'
|
|
179
|
-
) {
|
|
180
|
-
const lastChild =
|
|
181
|
-
currentSection.children[currentSection.children.length - 1];
|
|
182
|
-
if (lastChild.type === 'subcontent') {
|
|
183
|
-
lastChild.content += currentParagraph.trim() + '\n\n';
|
|
184
|
-
} else {
|
|
185
|
-
currentSection.content += currentParagraph.trim() + '\n\n';
|
|
186
|
-
}
|
|
187
|
-
} else {
|
|
188
|
-
currentSection.content += currentParagraph.trim() + '\n\n';
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
sections.push(currentSection);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
return { sections };
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Creates panes for a processed page using the selected design
|
|
199
|
-
*/
|
|
200
|
-
export async function createPagePanes(
|
|
201
|
-
processedPage: ProcessedPage,
|
|
202
|
-
design: PageDesign,
|
|
203
|
-
ctx: NodesContext,
|
|
204
|
-
generateTitle: boolean,
|
|
205
|
-
nodeId?: string,
|
|
206
|
-
onProgress?: (current: number, total: number) => void
|
|
207
|
-
): Promise<string[]> {
|
|
208
|
-
const ownerId = nodeId || ctx.rootNodeId.get();
|
|
209
|
-
const paneIds: string[] = [];
|
|
210
|
-
let panesCreated = 0;
|
|
211
|
-
|
|
212
|
-
// --- 1. Calculate the total number of panes for the progress bar ---
|
|
213
|
-
const introSection = processedPage.sections.find((s) => s.type === 'intro');
|
|
214
|
-
const contentSections = processedPage.sections.filter(
|
|
215
|
-
(s) => s.type === 'content'
|
|
216
|
-
);
|
|
217
|
-
const subContentSections = contentSections.flatMap((s) => s.children || []);
|
|
218
|
-
let totalPanes = 0;
|
|
219
|
-
if (introSection) totalPanes++;
|
|
220
|
-
totalPanes += contentSections.length;
|
|
221
|
-
totalPanes += subContentSections.length;
|
|
222
|
-
if (design.visualBreaks && contentSections.length > 0) {
|
|
223
|
-
// A break is added between each content section
|
|
224
|
-
totalPanes += contentSections.length - 1;
|
|
225
|
-
}
|
|
226
|
-
onProgress?.(0, totalPanes); // Report initial progress
|
|
227
|
-
|
|
228
|
-
const existingSlugs = generateTitle
|
|
229
|
-
? fullContentMapStore
|
|
230
|
-
.get()
|
|
231
|
-
.filter((item: { type: string }) =>
|
|
232
|
-
['Pane', 'StoryFragment'].includes(item.type)
|
|
233
|
-
)
|
|
234
|
-
.map((item: { slug: string }) => item.slug)
|
|
235
|
-
: [];
|
|
236
|
-
|
|
237
|
-
// --- 2. Create Panes and Report Progress After Each Step ---
|
|
238
|
-
if (introSection) {
|
|
239
|
-
const introPane = design.introDesign();
|
|
240
|
-
introPane.id = ulid();
|
|
241
|
-
introPane.slug = '';
|
|
242
|
-
introPane.title = '';
|
|
243
|
-
introPane.markdown.markdownBody = introSection.content || '';
|
|
244
|
-
|
|
245
|
-
if (generateTitle && introPane.markdown.markdownBody.trim().length > 0) {
|
|
246
|
-
const titleSlugResult = await getTitleSlug(
|
|
247
|
-
introPane.markdown.markdownBody,
|
|
248
|
-
existingSlugs
|
|
249
|
-
);
|
|
250
|
-
if (titleSlugResult) {
|
|
251
|
-
introPane.title = titleSlugResult.title;
|
|
252
|
-
introPane.slug = titleSlugResult.slug;
|
|
253
|
-
existingSlugs.push(titleSlugResult.slug);
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
const paneId = ctx.addTemplatePane(ownerId, introPane);
|
|
258
|
-
if (paneId) {
|
|
259
|
-
paneIds.push(paneId);
|
|
260
|
-
panesCreated++;
|
|
261
|
-
onProgress?.(panesCreated, totalPanes); // Report progress
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
for (let index = 0; index < contentSections.length; index++) {
|
|
266
|
-
const section = contentSections[index];
|
|
267
|
-
const useOdd = index % 2 === 0;
|
|
268
|
-
|
|
269
|
-
if (design.visualBreaks && index > 0) {
|
|
270
|
-
const isEven = (index - 1) % 2 === 0;
|
|
271
|
-
const breakTemplate = isEven
|
|
272
|
-
? design.visualBreaks.even()
|
|
273
|
-
: design.visualBreaks.odd();
|
|
274
|
-
const lastPaneId = paneIds[paneIds.length - 1];
|
|
275
|
-
|
|
276
|
-
if (lastPaneId) {
|
|
277
|
-
const abovePane = ctx.allNodes.get().get(lastPaneId) as PaneNode;
|
|
278
|
-
const aboveColor = abovePane?.bgColour || 'white';
|
|
279
|
-
const nextContentPane = design.contentDesign(!useOdd);
|
|
280
|
-
const belowColor = nextContentPane.bgColour;
|
|
281
|
-
const breakData = breakTemplate.bgPane?.breakDesktop;
|
|
282
|
-
const shapeName = breakData
|
|
283
|
-
? `${breakData.collection}${breakData.image}`
|
|
284
|
-
: '';
|
|
285
|
-
const isFlipped = (shapeName && SvgBreaks[shapeName]?.flipped) || false;
|
|
286
|
-
|
|
287
|
-
breakTemplate.bgColour = tailwindToHex(
|
|
288
|
-
isFlipped ? belowColor : aboveColor,
|
|
289
|
-
null
|
|
290
|
-
);
|
|
291
|
-
const svgFill = tailwindToHex(
|
|
292
|
-
isFlipped ? aboveColor : belowColor,
|
|
293
|
-
null
|
|
294
|
-
);
|
|
295
|
-
if (breakTemplate.bgPane) {
|
|
296
|
-
if (breakTemplate.bgPane.breakDesktop) {
|
|
297
|
-
breakTemplate.bgPane.breakDesktop.svgFill = svgFill;
|
|
298
|
-
}
|
|
299
|
-
if (breakTemplate.bgPane.breakTablet) {
|
|
300
|
-
breakTemplate.bgPane.breakTablet.svgFill = svgFill;
|
|
301
|
-
}
|
|
302
|
-
if (breakTemplate.bgPane.breakMobile) {
|
|
303
|
-
breakTemplate.bgPane.breakMobile.svgFill = svgFill;
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
const breakPaneId = ctx.addTemplatePane(
|
|
307
|
-
ownerId,
|
|
308
|
-
breakTemplate,
|
|
309
|
-
lastPaneId,
|
|
310
|
-
'after'
|
|
311
|
-
);
|
|
312
|
-
if (breakPaneId) {
|
|
313
|
-
paneIds.push(breakPaneId);
|
|
314
|
-
panesCreated++;
|
|
315
|
-
onProgress?.(panesCreated, totalPanes); // Report progress
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
const contentPane = design.contentDesign(useOdd);
|
|
321
|
-
contentPane.id = ulid();
|
|
322
|
-
contentPane.slug = '';
|
|
323
|
-
contentPane.title = '';
|
|
324
|
-
contentPane.markdown.markdownBody = section.content || '';
|
|
325
|
-
|
|
326
|
-
if (generateTitle && contentPane.markdown.markdownBody.trim().length > 0) {
|
|
327
|
-
const titleSlugResult = await getTitleSlug(
|
|
328
|
-
contentPane.markdown.markdownBody,
|
|
329
|
-
existingSlugs
|
|
330
|
-
);
|
|
331
|
-
if (titleSlugResult) {
|
|
332
|
-
contentPane.title = titleSlugResult.title;
|
|
333
|
-
contentPane.slug = titleSlugResult.slug;
|
|
334
|
-
existingSlugs.push(titleSlugResult.slug);
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
const contentPaneId = ctx.addTemplatePane(ownerId, contentPane);
|
|
339
|
-
if (contentPaneId) {
|
|
340
|
-
paneIds.push(contentPaneId);
|
|
341
|
-
panesCreated++;
|
|
342
|
-
onProgress?.(panesCreated, totalPanes); // Report progress
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
if (section.children && section.children.length > 0) {
|
|
346
|
-
for (const subSection of section.children) {
|
|
347
|
-
const subContentPane = design.contentDesign(!useOdd);
|
|
348
|
-
subContentPane.id = ulid();
|
|
349
|
-
subContentPane.slug = '';
|
|
350
|
-
subContentPane.title = '';
|
|
351
|
-
subContentPane.markdown.markdownBody = subSection.content || '';
|
|
352
|
-
|
|
353
|
-
if (
|
|
354
|
-
generateTitle &&
|
|
355
|
-
subContentPane.markdown.markdownBody.trim().length > 0
|
|
356
|
-
) {
|
|
357
|
-
const titleSlugResult = await getTitleSlug(
|
|
358
|
-
subContentPane.markdown.markdownBody,
|
|
359
|
-
existingSlugs
|
|
360
|
-
);
|
|
361
|
-
if (titleSlugResult) {
|
|
362
|
-
subContentPane.title = titleSlugResult.title;
|
|
363
|
-
subContentPane.slug = titleSlugResult.slug;
|
|
364
|
-
existingSlugs.push(titleSlugResult.slug);
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
const subContentPaneId = ctx.addTemplatePane(ownerId, subContentPane);
|
|
369
|
-
if (subContentPaneId) {
|
|
370
|
-
paneIds.push(subContentPaneId);
|
|
371
|
-
panesCreated++;
|
|
372
|
-
onProgress?.(panesCreated, totalPanes); // Report progress
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
return paneIds;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
/**
|
|
382
|
-
* Builds a complete page preview in the provided context
|
|
383
|
-
*/
|
|
384
|
-
export async function buildPagePreview(
|
|
385
|
-
markdown: string,
|
|
386
|
-
design: PageDesign,
|
|
387
|
-
ctx: NodesContext
|
|
388
|
-
): Promise<void> {
|
|
389
|
-
// Parse markdown into sections
|
|
390
|
-
const processedPage = parsePageMarkdown(markdown);
|
|
391
|
-
|
|
392
|
-
// Create story fragment node - add proper type assertion
|
|
393
|
-
const pageNode = ctx.allNodes.get().get('tmp') as StoryFragmentNode;
|
|
394
|
-
if (!pageNode) return;
|
|
395
|
-
|
|
396
|
-
// Create all panes using design
|
|
397
|
-
const paneIds = await createPagePanes(processedPage, design, ctx, false);
|
|
398
|
-
|
|
399
|
-
// Update story fragment with panes
|
|
400
|
-
pageNode.title = '';
|
|
401
|
-
pageNode.slug = 'preview';
|
|
402
|
-
pageNode.paneIds = paneIds;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
/**
|
|
406
|
-
* Validates markdown structure matches expected page format
|
|
407
|
-
*/
|
|
408
|
-
export function validatePageMarkdown(markdown: string): boolean {
|
|
409
|
-
const { sections } = parsePageMarkdown(markdown);
|
|
410
|
-
|
|
411
|
-
// Must have at least one section
|
|
412
|
-
if (sections.length === 0) return false;
|
|
413
|
-
|
|
414
|
-
// First section should be intro (H2)
|
|
415
|
-
if (sections[0].type !== 'intro') return false;
|
|
416
|
-
|
|
417
|
-
// Should have at least one content section
|
|
418
|
-
if (!sections.some((s) => s.type === 'content')) return false;
|
|
419
|
-
|
|
420
|
-
// All sections should have content
|
|
421
|
-
if (!sections.every((s) => s.content.trim())) return false;
|
|
422
|
-
|
|
423
|
-
return true;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
/**
|
|
427
|
-
* Creates preview text of how markdown will be split into panes
|
|
428
|
-
*/
|
|
429
|
-
export function getPagePreview(markdown: string): string {
|
|
430
|
-
const { sections } = parsePageMarkdown(markdown);
|
|
431
|
-
|
|
432
|
-
let preview = ``;
|
|
433
|
-
|
|
434
|
-
sections.forEach((section, i) => {
|
|
435
|
-
preview += `=== Pane ${i + 1} ===\n`;
|
|
436
|
-
preview += `Type: ${section.type}\n`;
|
|
437
|
-
preview += `Content Preview: ${section.content.substring(0, 100)}...\n`;
|
|
438
|
-
if (section.children?.length) {
|
|
439
|
-
preview += `Subsections: ${section.children.length}\n`;
|
|
440
|
-
}
|
|
441
|
-
preview += '\n';
|
|
442
|
-
});
|
|
443
|
-
|
|
444
|
-
return preview;
|
|
445
|
-
}
|