astro-tractstack 2.0.19 → 2.0.21

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.
Files changed (54) hide show
  1. package/dist/index.js +6 -32
  2. package/package.json +1 -1
  3. package/templates/src/components/codehooks/BunnyVideoSetup.tsx +1 -4
  4. package/templates/src/components/codehooks/FeaturedArticleSetup.tsx +0 -4
  5. package/templates/src/components/codehooks/ListContentSetup.tsx +1 -8
  6. package/templates/src/components/codehooks/ProductCardSetup.tsx +0 -2
  7. package/templates/src/components/codehooks/ProductGridSetup.tsx +0 -2
  8. package/templates/src/components/compositor/Compositor.tsx +3 -6
  9. package/templates/src/components/compositor/Node.tsx +13 -32
  10. package/templates/src/components/compositor/NodeWithGuid.tsx +49 -5
  11. package/templates/src/components/compositor/nodes/Pane.tsx +4 -21
  12. package/templates/src/components/compositor/nodes/Pane_DesignLibrary.tsx +27 -7
  13. package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag.tsx +3 -1
  14. package/templates/src/components/compositor/preview/OgImagePreview.tsx +0 -5
  15. package/templates/src/components/compositor/preview/PaneSnapshotGenerator.tsx +5 -6
  16. package/templates/src/components/edit/PanelSwitch.tsx +3 -24
  17. package/templates/src/components/edit/SettingsPanel.tsx +0 -1
  18. package/templates/src/components/edit/ToolMode.tsx +6 -14
  19. package/templates/src/components/edit/pane/AddPanePanel.tsx +58 -25
  20. package/templates/src/components/edit/pane/AddPanePanel_new.tsx +140 -133
  21. package/templates/src/components/edit/pane/AddPanePanel_paste.tsx +111 -0
  22. package/templates/src/components/edit/pane/RestylePaneModal.tsx +231 -282
  23. package/templates/src/components/edit/pane/steps/AiDesignStep.tsx +0 -5
  24. package/templates/src/components/edit/pane/steps/DesignLibraryStep.tsx +4 -13
  25. package/templates/src/components/edit/panels/StyleBreakPanel.tsx +1 -3
  26. package/templates/src/components/edit/panels/StyleElementPanel_update.tsx +0 -6
  27. package/templates/src/components/edit/panels/StyleImagePanel_update.tsx +0 -3
  28. package/templates/src/components/edit/panels/StyleLiElementPanel_update.tsx +0 -4
  29. package/templates/src/components/edit/panels/StyleLinkPanel_config.tsx +8 -5
  30. package/templates/src/components/edit/panels/StyleLinkPanel_update.tsx +1 -2
  31. package/templates/src/components/edit/panels/StyleParentPanel.tsx +1 -3
  32. package/templates/src/components/edit/panels/StyleParentPanel_update.tsx +2 -5
  33. package/templates/src/components/edit/panels/StyleWidgetPanel_config.tsx +2 -8
  34. package/templates/src/components/edit/panels/StyleWidgetPanel_update.tsx +0 -4
  35. package/templates/src/components/edit/state/SaveToLibraryModal.tsx +29 -16
  36. package/templates/src/components/edit/storyfragment/StoryFragmentConfigPanel.tsx +9 -26
  37. package/templates/src/components/edit/storyfragment/StoryFragmentPanel_og.tsx +7 -16
  38. package/templates/src/components/edit/storyfragment/StoryFragmentPanel_slug.tsx +5 -6
  39. package/templates/src/components/edit/widgets/InteractiveDisclosureWidget.tsx +0 -5
  40. package/templates/src/components/fields/BackgroundImageWrapper.tsx +1 -7
  41. package/templates/src/components/fields/ColorPickerCombo.tsx +8 -12
  42. package/templates/src/components/fields/ViewportComboBox.tsx +4 -6
  43. package/templates/src/stores/nodes.ts +14 -6
  44. package/templates/src/stores/storykeep.ts +3 -3
  45. package/templates/src/types/compositorTypes.ts +2 -0
  46. package/templates/src/utils/compositor/TemplatePanes.ts +0 -76
  47. package/templates/src/utils/compositor/aiPaneParser.ts +3 -1
  48. package/templates/src/utils/compositor/designLibraryHelper.ts +523 -203
  49. package/templates/src/utils/helpers.ts +5 -4
  50. package/utils/inject-files.ts +6 -32
  51. package/templates/src/components/compositor/preview/VisualBreakPreview.tsx +0 -154
  52. package/templates/src/components/edit/pane/PageGen_preview.tsx +0 -511
  53. package/templates/src/utils/compositor/processMarkdown.ts +0 -445
  54. 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
- }