astro-tractstack 2.0.16 → 2.0.18

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 (65) hide show
  1. package/dist/index.js +24 -0
  2. package/package.json +1 -1
  3. package/templates/custom/with-examples/SandboxLauncher.tsx +11 -9
  4. package/templates/src/components/codehooks/FeaturedArticleSetup.tsx +1 -1
  5. package/templates/src/components/codehooks/ListContentSetup.tsx +1 -1
  6. package/templates/src/components/compositor/Compositor.tsx +1 -0
  7. package/templates/src/components/compositor/Node.tsx +41 -17
  8. package/templates/src/components/compositor/nodes/GhostInsertBlock.tsx +9 -6
  9. package/templates/src/components/compositor/nodes/GridLayout.tsx +124 -0
  10. package/templates/src/components/compositor/nodes/GridLayout_eraser.tsx +33 -0
  11. package/templates/src/components/compositor/nodes/Markdown.tsx +67 -37
  12. package/templates/src/components/compositor/nodes/Markdown_eraser.tsx +56 -0
  13. package/templates/src/components/compositor/nodes/Pane_DesignLibrary.tsx +1 -1
  14. package/templates/src/components/compositor/preview/FeaturedArticlePreview.tsx +8 -2
  15. package/templates/src/components/edit/PanelSwitch.tsx +232 -75
  16. package/templates/src/components/edit/SettingsPanel.tsx +0 -1
  17. package/templates/src/components/edit/pane/AddPanePanel_codehook.tsx +3 -3
  18. package/templates/src/components/edit/pane/AddPanePanel_new.tsx +184 -151
  19. package/templates/src/components/edit/pane/AddPanePanel_reuse.tsx +2 -2
  20. package/templates/src/components/edit/pane/ConfigPanePanel.tsx +1 -7
  21. package/templates/src/components/edit/pane/PanePanel_impression.tsx +1 -1
  22. package/templates/src/components/edit/pane/RestylePaneModal.tsx +8 -5
  23. package/templates/src/components/edit/pane/steps/AiDesignStep.tsx +6 -6
  24. package/templates/src/components/edit/pane/steps/CopyInputStep.tsx +3 -3
  25. package/templates/src/components/edit/pane/steps/DesignLibraryStep.tsx +4 -4
  26. package/templates/src/components/edit/pane/steps/DirectInjectStep.tsx +96 -0
  27. package/templates/src/components/edit/panels/StyleElementPanel.tsx +11 -4
  28. package/templates/src/components/edit/panels/StyleElementPanel_add.tsx +8 -8
  29. package/templates/src/components/edit/panels/StyleElementPanel_remove.tsx +14 -4
  30. package/templates/src/components/edit/panels/StyleElementPanel_update.tsx +16 -4
  31. package/templates/src/components/edit/panels/StyleImagePanel.tsx +8 -3
  32. package/templates/src/components/edit/panels/StyleImagePanel_add.tsx +9 -2
  33. package/templates/src/components/edit/panels/StyleImagePanel_remove.tsx +5 -2
  34. package/templates/src/components/edit/panels/StyleImagePanel_update.tsx +5 -2
  35. package/templates/src/components/edit/panels/StyleLiElementPanel.tsx +7 -3
  36. package/templates/src/components/edit/panels/StyleLiElementPanel_add.tsx +9 -2
  37. package/templates/src/components/edit/panels/StyleLiElementPanel_remove.tsx +5 -2
  38. package/templates/src/components/edit/panels/StyleLiElementPanel_update.tsx +5 -2
  39. package/templates/src/components/edit/panels/StyleParentPanel.tsx +530 -171
  40. package/templates/src/components/edit/panels/StyleParentPanel_add.tsx +77 -42
  41. package/templates/src/components/edit/panels/StyleParentPanel_deleteLayer.tsx +38 -22
  42. package/templates/src/components/edit/panels/StyleParentPanel_remove.tsx +171 -66
  43. package/templates/src/components/edit/panels/StyleParentPanel_update.tsx +166 -98
  44. package/templates/src/components/edit/panels/StyleWidgetPanel.tsx +7 -3
  45. package/templates/src/components/edit/panels/StyleWidgetPanel_add.tsx +9 -2
  46. package/templates/src/components/edit/panels/StyleWidgetPanel_remove.tsx +5 -2
  47. package/templates/src/components/edit/panels/StyleWidgetPanel_update.tsx +6 -2
  48. package/templates/src/components/edit/state/SaveModal.tsx +10 -2
  49. package/templates/src/components/edit/state/SaveToLibraryModal.tsx +6 -6
  50. package/templates/src/components/fields/PaneBreakShapeSelector.tsx +1 -1
  51. package/templates/src/components/widgets/ImpressionWrapper.tsx +4 -1
  52. package/templates/src/constants/prompts.json +1 -1
  53. package/templates/src/constants.ts +1 -0
  54. package/templates/src/stores/nodes.ts +110 -33
  55. package/templates/src/stores/storykeep.ts +3 -1
  56. package/templates/src/types/compositorTypes.ts +37 -2
  57. package/templates/src/utils/compositor/TemplateNodes.ts +8 -0
  58. package/templates/src/utils/compositor/aiPaneParser.ts +8 -2
  59. package/templates/src/utils/compositor/nodesHelper.ts +229 -0
  60. package/templates/src/utils/compositor/reduceNodesClassNames.ts +40 -1
  61. package/templates/src/utils/compositor/typeGuards.ts +7 -0
  62. package/templates/src/utils/etl/extractor.ts +1 -5
  63. package/templates/src/utils/etl/index.ts +1 -0
  64. package/templates/src/utils/etl/transformer.ts +70 -25
  65. package/utils/inject-files.ts +24 -0
@@ -10,6 +10,7 @@ import type {
10
10
  ArtpackImageNode,
11
11
  BgImageNode,
12
12
  Tag,
13
+ GridLayoutNode,
13
14
  } from '@/types/compositorTypes';
14
15
 
15
16
  interface BreakData {
@@ -192,3 +193,9 @@ export function hasPlayerJS(
192
193
  ): window is Window & { playerjs: PlayerJS } {
193
194
  return 'playerjs' in window;
194
195
  }
196
+
197
+ export function isGridLayoutNode(
198
+ node: BaseNode | undefined | null
199
+ ): node is GridLayoutNode {
200
+ return node?.nodeType === 'GridLayoutNode';
201
+ }
@@ -52,11 +52,8 @@ export function extractPaneSubtree(
52
52
  bgColour: paneNode.bgColour,
53
53
  });
54
54
 
55
- // --- START: REPLACEMENT FOR getNodesRecursively ---
56
- // Use a safe, non-recursive breadth-first traversal to gather all descendant
57
- // nodes, which preserves the correct sibling order.
58
55
  const allDescendantNodes: BaseNode[] = [];
59
- const queue: string[] = [...ctx.getChildNodeIDs(paneNode.id)]; // Start queue with direct children
56
+ const queue: string[] = [...ctx.getChildNodeIDs(paneNode.id)];
60
57
 
61
58
  while (queue.length > 0) {
62
59
  const currentId = queue.shift();
@@ -70,7 +67,6 @@ export function extractPaneSubtree(
70
67
  queue.push(...childrenIds);
71
68
  }
72
69
  }
73
- // --- END: REPLACEMENT FOR getNodesRecursively ---
74
70
 
75
71
  if (VERBOSE)
76
72
  console.log(
@@ -75,6 +75,7 @@ export interface OptionsPayload {
75
75
  | undefined;
76
76
  parentClasses?: any | undefined;
77
77
  parentCss?: string[] | undefined;
78
+ gridCss?: string | undefined;
78
79
  hiddenViewportMobile?: boolean | undefined;
79
80
  hiddenViewportTablet?: boolean | undefined;
80
81
  hiddenViewportDesktop?: boolean | undefined;
@@ -11,6 +11,7 @@ import type {
11
11
  ArtpackImageNode,
12
12
  BgImageNode,
13
13
  StoryFragmentNode,
14
+ GridLayoutNode,
14
15
  } from '@/types/compositorTypes';
15
16
  import type {
16
17
  OptionsPayload,
@@ -21,7 +22,9 @@ import {
21
22
  isBreakNode,
22
23
  isArtpackImageNode,
23
24
  isBgImageNode,
25
+ isGridLayoutNode,
24
26
  } from '@/utils/compositor/typeGuards';
27
+ import { processClassesForViewports } from '@/utils/compositor/reduceNodesClassNames';
25
28
 
26
29
  const VERBOSE = false;
27
30
 
@@ -42,7 +45,6 @@ export function transformToOptionsPayload(
42
45
  })),
43
46
  });
44
47
 
45
- // 1. Generate flattened nodes array with computed CSS
46
48
  const flattenedNodes = subtree.allChildNodes
47
49
  .map((node) => {
48
50
  if (VERBOSE)
@@ -58,11 +60,8 @@ export function transformToOptionsPayload(
58
60
  parentId: node.parentId,
59
61
  };
60
62
 
61
- // Add type-specific fields based on node type
62
63
  if (node.nodeType === 'TagElement') {
63
64
  const flatNode = node as FlatNode;
64
-
65
- // Compute CSS using existing NodesContext methods
66
65
  let computedCSS: string | undefined;
67
66
  try {
68
67
  computedCSS = ctx.getNodeClasses(node.id, 'auto', 0);
@@ -91,10 +90,55 @@ export function transformToOptionsPayload(
91
90
  return transformedNode;
92
91
  }
93
92
 
93
+ if (isGridLayoutNode(node)) {
94
+ const gridLayoutNode = node as GridLayoutNode;
95
+ let gridCss = '';
96
+ if (gridLayoutNode.gridColumns) {
97
+ const { mobile, tablet, desktop } = gridLayoutNode.gridColumns;
98
+ gridCss = `grid grid-cols-${mobile}`;
99
+ if (tablet !== mobile) {
100
+ gridCss += ` md:grid-cols-${tablet}`;
101
+ }
102
+ if (desktop !== tablet) {
103
+ gridCss += ` xl:grid-cols-${desktop}`;
104
+ }
105
+ }
106
+
107
+ let parentCss: string[] | undefined;
108
+ if (gridLayoutNode.parentClasses) {
109
+ try {
110
+ parentCss = gridLayoutNode.parentClasses.map((_, index) =>
111
+ ctx.getNodeClasses(node.id, 'auto', index)
112
+ );
113
+ } catch (error) {
114
+ console.warn(
115
+ `Failed to compute parent CSS for grid node ${node.id}:`,
116
+ error
117
+ );
118
+ }
119
+ }
120
+
121
+ const transformedNode = {
122
+ ...baseNode,
123
+ type: gridLayoutNode.type,
124
+ gridCss: gridCss,
125
+ gridColumns: gridLayoutNode.gridColumns,
126
+ parentClasses: gridLayoutNode.parentClasses,
127
+ parentCss: parentCss,
128
+ defaultClasses: gridLayoutNode.defaultClasses,
129
+ hiddenViewportMobile: gridLayoutNode.hiddenViewportMobile,
130
+ hiddenViewportTablet: gridLayoutNode.hiddenViewportTablet,
131
+ hiddenViewportDesktop: gridLayoutNode.hiddenViewportDesktop,
132
+ };
133
+
134
+ if (VERBOSE)
135
+ console.log('✅ TRANSFORMER - GridLayout result:', transformedNode);
136
+ return transformedNode;
137
+ }
138
+
94
139
  if (node.nodeType === 'Markdown') {
95
140
  const markdownNode = node as MarkdownPaneFragmentNode;
96
141
 
97
- // Compute parentCss if parentClasses exist
98
142
  let parentCss: string[] | undefined;
99
143
  if (markdownNode.parentClasses) {
100
144
  try {
@@ -109,6 +153,25 @@ export function transformToOptionsPayload(
109
153
  }
110
154
  }
111
155
 
156
+ let gridCss: string | undefined;
157
+ if (markdownNode.gridClasses) {
158
+ try {
159
+ const [allClasses] = processClassesForViewports(
160
+ markdownNode.gridClasses,
161
+ {},
162
+ 1
163
+ );
164
+ if (allClasses && allClasses.length > 0) {
165
+ gridCss = allClasses[0];
166
+ }
167
+ } catch (error) {
168
+ console.warn(
169
+ `Failed to compute grid CSS for markdown node ${node.id}:`,
170
+ error
171
+ );
172
+ }
173
+ }
174
+
112
175
  const transformedNode = {
113
176
  ...baseNode,
114
177
  type: markdownNode.type,
@@ -119,6 +182,8 @@ export function transformToOptionsPayload(
119
182
  hiddenViewportMobile: markdownNode.hiddenViewportMobile,
120
183
  hiddenViewportTablet: markdownNode.hiddenViewportTablet,
121
184
  hiddenViewportDesktop: markdownNode.hiddenViewportDesktop,
185
+ gridClasses: markdownNode.gridClasses,
186
+ ...(gridCss && { gridCss: gridCss }),
122
187
  };
123
188
 
124
189
  if (VERBOSE)
@@ -127,7 +192,6 @@ export function transformToOptionsPayload(
127
192
  }
128
193
 
129
194
  if (node.nodeType === 'BgPane') {
130
- // Handle different BgPane types
131
195
  if (isBreakNode(node as FlatNode)) {
132
196
  const breakNode = node as VisualBreakNode;
133
197
  const transformedNode = {
@@ -188,20 +252,15 @@ export function transformToOptionsPayload(
188
252
  );
189
253
  return transformedNode;
190
254
  }
191
-
192
- // Fallback for unknown BgPane types
193
255
  if (VERBOSE)
194
256
  console.warn('⚠️ TRANSFORMER - Unknown BgPane type:', node);
195
257
  return baseNode;
196
258
  }
197
-
198
- // Unknown node type - return base node
199
259
  if (VERBOSE) console.warn('⚠️ TRANSFORMER - Unknown node type:', node);
200
260
  return baseNode;
201
261
  })
202
262
  .filter((node) => node !== null);
203
263
 
204
- // 2. Build final OptionsPayload
205
264
  const optionsPayload: OptionsPayload = {
206
265
  bgColour: subtree.paneNode.bgColour,
207
266
  isDecorative: subtree.paneNode.isDecorative,
@@ -230,11 +289,9 @@ export async function transformStoryFragmentForSave(
230
289
  const node = ctx.allNodes.get().get(fragmentId) as StoryFragmentNode;
231
290
  const seoData = storyFragmentTopicsStore.get()[fragmentId];
232
291
 
233
- // Get brand config from store to find default tractstack
234
292
  const brandConfig = await getBrandConfig(tenantId);
235
293
  const defaultTractStackSlug =
236
294
  brandConfig?.TRACTSTACK_HOME_SLUG || 'tractstack';
237
- // Find the default tractstack ID from content map
238
295
  const contentMap = fullContentMapStore.get();
239
296
  const defaultTractStack = contentMap.find(
240
297
  (item) => item.type === 'TractStack' && item.slug === defaultTractStackSlug
@@ -244,12 +301,10 @@ export async function transformStoryFragmentForSave(
244
301
 
245
302
  const payload = {
246
303
  ...node,
247
- // Add deferred SEO data if available
248
304
  ...(seoData && {
249
305
  topics: seoData.topics?.map((t) => t.title) || [],
250
306
  description: seoData.description || '',
251
307
  }),
252
- // Ensure tractStackId is set for new StoryFragments
253
308
  tractStackId: finalTractStackId,
254
309
  };
255
310
 
@@ -261,13 +316,8 @@ export function transformLivePaneForSave(
261
316
  paneId: string,
262
317
  isContext?: boolean
263
318
  ): BackendSavePayload {
264
- // 1. Extract distributed state
265
319
  const subtree = extractPaneSubtree(ctx, paneId);
266
-
267
- // 2. Transform to flattened OptionsPayload using existing NodesContext methods
268
320
  const optionsPayload = transformToOptionsPayload(ctx, subtree);
269
-
270
- // 3. Format for save endpoint
271
321
  return formatForSave(subtree.paneNode, optionsPayload, isContext);
272
322
  }
273
323
 
@@ -275,12 +325,7 @@ export function transformLivePaneForPreview(
275
325
  ctx: NodesContext,
276
326
  paneId: string
277
327
  ): BackendPreviewPayload {
278
- // 1. Extract distributed state
279
328
  const subtree = extractPaneSubtree(ctx, paneId);
280
-
281
- // 2. Transform to flattened OptionsPayload using existing NodesContext methods
282
329
  const optionsPayload = transformToOptionsPayload(ctx, subtree);
283
-
284
- // 3. Format for preview endpoint
285
330
  return formatForPreview(subtree.paneNode, optionsPayload);
286
331
  }
@@ -105,6 +105,18 @@ export async function injectTemplateFiles(
105
105
  ),
106
106
  dest: 'src/components/compositor/nodes/RenderChildren.tsx',
107
107
  },
108
+ {
109
+ src: resolve(
110
+ '../templates/src/components/compositor/nodes/GridLayout.tsx'
111
+ ),
112
+ dest: 'src/components/compositor/nodes/GridLayout.tsx',
113
+ },
114
+ {
115
+ src: resolve(
116
+ '../templates/src/components/compositor/nodes/GridLayout_eraser.tsx'
117
+ ),
118
+ dest: 'src/components/compositor/nodes/GridLayout_eraser.tsx',
119
+ },
108
120
  {
109
121
  src: resolve(
110
122
  '../templates/src/components/compositor/nodes/GhostInsertBlock.tsx'
@@ -155,6 +167,12 @@ export async function injectTemplateFiles(
155
167
  src: resolve('../templates/src/components/compositor/nodes/Markdown.tsx'),
156
168
  dest: 'src/components/compositor/nodes/Markdown.tsx',
157
169
  },
170
+ {
171
+ src: resolve(
172
+ '../templates/src/components/compositor/nodes/Markdown_eraser.tsx'
173
+ ),
174
+ dest: 'src/components/compositor/nodes/Markdown_eraser.tsx',
175
+ },
158
176
  {
159
177
  src: resolve(
160
178
  '../templates/src/components/compositor/nodes/BgPaneWrapper.tsx'
@@ -471,6 +489,12 @@ export async function injectTemplateFiles(
471
489
  ),
472
490
  dest: 'src/components/edit/pane/steps/AiDesignStep.tsx',
473
491
  },
492
+ {
493
+ src: resolve(
494
+ '../templates/src/components/edit/pane/steps/DirectInjectStep.tsx'
495
+ ),
496
+ dest: 'src/components/edit/pane/steps/DirectInjectStep.tsx',
497
+ },
474
498
  {
475
499
  src: resolve(
476
500
  '../templates/src/components/edit/pane/AddPanePanel_newCustomCopy.tsx'