astro-tractstack 2.2.1 → 2.2.3

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 (68) hide show
  1. package/package.json +1 -1
  2. package/templates/src/components/codehooks/BunnyVideoSetup.tsx +0 -1
  3. package/templates/src/components/codehooks/FeaturedArticleSetup.tsx +0 -1
  4. package/templates/src/components/codehooks/ListContentSetup.tsx +0 -1
  5. package/templates/src/components/codehooks/ProductCardSetup.tsx +0 -1
  6. package/templates/src/components/codehooks/ProductGridSetup.tsx +0 -1
  7. package/templates/src/components/compositor/Compositor.tsx +0 -1
  8. package/templates/src/components/compositor/Node.tsx +157 -133
  9. package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag.tsx +2 -4
  10. package/templates/src/components/edit/Header.tsx +2 -6
  11. package/templates/src/components/edit/context/ContextPaneConfig_slug.tsx +1 -1
  12. package/templates/src/components/edit/context/ContextPaneConfig_title.tsx +0 -1
  13. package/templates/src/components/edit/pane/AddPanePanel_break.tsx +1 -0
  14. package/templates/src/components/edit/pane/AddPanePanel_new.tsx +8 -12
  15. package/templates/src/components/edit/pane/AddPanePanel_reuse.tsx +9 -6
  16. package/templates/src/components/edit/pane/AiRestylePaneModal.tsx +7 -69
  17. package/templates/src/components/edit/pane/ConfigPanePanel.tsx +2 -2
  18. package/templates/src/components/edit/pane/PanePanel_impression.tsx +0 -4
  19. package/templates/src/components/edit/pane/PanePanel_path.tsx +0 -1
  20. package/templates/src/components/edit/pane/PanePanel_title.tsx +1 -2
  21. package/templates/src/components/edit/pane/RestylePaneModal.tsx +1 -4
  22. package/templates/src/components/edit/pane/steps/AiCreativeDesignStep.tsx +0 -3
  23. package/templates/src/components/edit/pane/steps/AiRefineDesignStep.tsx +2 -2
  24. package/templates/src/components/edit/pane/steps/AiStandardDesignStep.tsx +173 -80
  25. package/templates/src/components/edit/pane/steps/CreativeInjectStep.tsx +0 -5
  26. package/templates/src/components/edit/pane/steps/DesignLibraryStep.tsx +2 -1
  27. package/templates/src/components/edit/panels/StyleBreakPanel.tsx +1 -4
  28. package/templates/src/components/edit/panels/StyleCodeHookPanel.tsx +0 -1
  29. package/templates/src/components/edit/panels/StyleElementPanel.tsx +1 -1
  30. package/templates/src/components/edit/panels/StyleElementPanel_remove.tsx +1 -4
  31. package/templates/src/components/edit/panels/StyleElementPanel_update.tsx +3 -3
  32. package/templates/src/components/edit/panels/StyleImagePanel.tsx +3 -3
  33. package/templates/src/components/edit/panels/StyleImagePanel_remove.tsx +1 -4
  34. package/templates/src/components/edit/panels/StyleImagePanel_update.tsx +3 -4
  35. package/templates/src/components/edit/panels/StyleLiElementPanel_remove.tsx +1 -4
  36. package/templates/src/components/edit/panels/StyleLiElementPanel_update.tsx +3 -3
  37. package/templates/src/components/edit/panels/StyleLinkPanel.tsx +1 -1
  38. package/templates/src/components/edit/panels/StyleLinkPanel_config.tsx +1 -1
  39. package/templates/src/components/edit/panels/StyleLinkPanel_remove.tsx +1 -1
  40. package/templates/src/components/edit/panels/StyleLinkPanel_update.tsx +1 -1
  41. package/templates/src/components/edit/panels/StyleParentPanel.tsx +0 -7
  42. package/templates/src/components/edit/panels/StyleParentPanel_deleteLayer.tsx +0 -2
  43. package/templates/src/components/edit/panels/StyleParentPanel_remove.tsx +0 -2
  44. package/templates/src/components/edit/panels/StyleParentPanel_update.tsx +0 -2
  45. package/templates/src/components/edit/panels/StyleWidgetPanel_config.tsx +0 -3
  46. package/templates/src/components/edit/panels/StyleWidgetPanel_remove.tsx +1 -4
  47. package/templates/src/components/edit/panels/StyleWidgetPanel_update.tsx +3 -4
  48. package/templates/src/components/edit/panels/StyleWordCarouselPanel.tsx +0 -2
  49. package/templates/src/components/edit/state/StylesMemory.tsx +3 -9
  50. package/templates/src/components/edit/storyfragment/StoryFragmentConfigPanel.tsx +0 -1
  51. package/templates/src/components/edit/storyfragment/StoryFragmentPanel_menu.tsx +0 -2
  52. package/templates/src/components/edit/storyfragment/StoryFragmentPanel_og.tsx +0 -2
  53. package/templates/src/components/edit/storyfragment/StoryFragmentPanel_slug.tsx +0 -1
  54. package/templates/src/components/edit/storyfragment/StoryFragmentPanel_title.tsx +0 -1
  55. package/templates/src/components/fields/ArtpackImage.tsx +0 -7
  56. package/templates/src/components/fields/BackgroundImage.tsx +0 -14
  57. package/templates/src/components/fields/BackgroundImageWrapper.tsx +0 -5
  58. package/templates/src/components/fields/ImageUpload.tsx +0 -3
  59. package/templates/src/pages/[...slug]/edit.astro +0 -1
  60. package/templates/src/pages/sandbox.astro +0 -1
  61. package/templates/src/stores/nodes.ts +278 -312
  62. package/templates/src/stores/nodesHistory.ts +59 -24
  63. package/templates/src/utils/api/setupHelpers.ts +1 -1
  64. package/templates/src/utils/compositor/aiPaneParser.ts +57 -0
  65. package/templates/src/utils/compositor/designLibraryHelper.ts +1 -3
  66. package/templates/src/utils/compositor/htmlAst.ts +109 -2
  67. package/templates/src/utils/compositor/nodesHelper.ts +1 -9
  68. package/templates/src/utils/compositor/savePipeline.ts +1 -4
@@ -100,7 +100,6 @@ const PaneImpressionPanel = ({ nodeId, setMode }: PaneImpressionPanelProps) => {
100
100
  ...cloneDeep(impressionNode),
101
101
  tagName: 'impression',
102
102
  ...data,
103
- isChanged: true,
104
103
  };
105
104
  ctx.modifyNodes([node]);
106
105
  } else {
@@ -111,14 +110,12 @@ const PaneImpressionPanel = ({ nodeId, setMode }: PaneImpressionPanelProps) => {
111
110
  nodeType: 'Impression',
112
111
  parentId: nodeId,
113
112
  ...data,
114
- isChanged: true,
115
113
  } as ImpressionNode;
116
114
  ctx.addTemplateImpressionNode(nodeId, node);
117
115
  }
118
116
  ctx.modifyNodes([
119
117
  {
120
118
  ...paneNode,
121
- isChanged: true,
122
119
  },
123
120
  ]);
124
121
  },
@@ -156,7 +153,6 @@ const PaneImpressionPanel = ({ nodeId, setMode }: PaneImpressionPanelProps) => {
156
153
  ctx.modifyNodes([
157
154
  {
158
155
  ...paneNode,
159
- isChanged: true,
160
156
  },
161
157
  ]);
162
158
  setMode(PaneConfigMode.DEFAULT);
@@ -107,7 +107,6 @@ const PaneMagicPathPanel = ({ nodeId, setMode }: PaneMagicPathPanelProps) => {
107
107
 
108
108
  updatedNode.heldBeliefs = updatedHeldPaths;
109
109
  updatedNode.withheldBeliefs = updatedWithheldPaths;
110
- updatedNode.isChanged = true;
111
110
 
112
111
  const currentPanelState = ctx.activePaneMode.get();
113
112
 
@@ -145,7 +145,6 @@ const PaneTitlePanel = ({ nodeId, setMode }: PaneTitlePanelProps) => {
145
145
  ...cloneDeep(paneNode),
146
146
  title,
147
147
  slug: updatedSlug,
148
- isChanged: true,
149
148
  };
150
149
  ctx.modifyNodes([updatedNode]);
151
150
  }
@@ -154,7 +153,7 @@ const PaneTitlePanel = ({ nodeId, setMode }: PaneTitlePanelProps) => {
154
153
  const handleSlugBlur = () => {
155
154
  if (canSaveSlug) {
156
155
  const ctx = getCtx();
157
- const updatedNode = { ...cloneDeep(paneNode), slug, isChanged: true };
156
+ const updatedNode = { ...cloneDeep(paneNode), slug };
158
157
  ctx.modifyNodes([updatedNode]);
159
158
  }
160
159
  };
@@ -60,6 +60,7 @@ const TemplatePreviewItem = ({
60
60
 
61
61
  const fragmentRequest = useMemo((): PanePreviewRequest[] => {
62
62
  const ctx = new NodesContext();
63
+ ctx.isTemplate.set(true);
63
64
  ctx.addNode(createEmptyStorykeep('tmp'));
64
65
  ctx.addTemplatePane('tmp', template);
65
66
  return [{ id: template.id, ctx }];
@@ -319,11 +320,7 @@ export const RestylePaneModal = () => {
319
320
  paneToUpdate.heightRatioDesktop = template.heightRatioDesktop;
320
321
  paneToUpdate.heightRatioMobile = template.heightRatioMobile;
321
322
  paneToUpdate.heightRatioTablet = template.heightRatioTablet;
322
- paneToUpdate.isChanged = true;
323
-
324
323
  ctx.modifyNodes([paneToUpdate]);
325
- ctx.notifyNode('root');
326
-
327
324
  handleClose();
328
325
  };
329
326
 
@@ -4,7 +4,6 @@ import CheckIcon from '@heroicons/react/24/outline/CheckIcon';
4
4
  import XMarkIcon from '@heroicons/react/24/outline/XMarkIcon';
5
5
  import ArrowPathRoundedSquareIcon from '@heroicons/react/24/outline/ArrowPathRoundedSquareIcon';
6
6
  import prompts from '@/constants/prompts.json';
7
- import { getCtx } from '@/stores/nodes';
8
7
  import { htmlToHtmlAst } from '@/utils/compositor/htmlAst';
9
8
  import type { TemplatePane } from '@/types/compositorTypes';
10
9
  import { callAskLemurAPI } from '@/utils/compositor/aiGeneration';
@@ -163,8 +162,6 @@ export const AiCreativeDesignStep = ({
163
162
  const handleAccept = () => {
164
163
  if (pendingTemplate) {
165
164
  onCreatePane(pendingTemplate);
166
- const ctx = getCtx();
167
- ctx.showSaveBypass.set(true);
168
165
  onSuccess();
169
166
  }
170
167
  };
@@ -4,7 +4,7 @@ import CheckIcon from '@heroicons/react/24/outline/CheckIcon';
4
4
  import XMarkIcon from '@heroicons/react/24/outline/XMarkIcon';
5
5
  import ArrowPathRoundedSquareIcon from '@heroicons/react/24/outline/ArrowPathRoundedSquareIcon';
6
6
  import prompts from '@/constants/prompts.json';
7
- import { htmlToHtmlAst } from '@/utils/compositor/htmlAst';
7
+ import { htmlToHtmlAst, cleanHtml } from '@/utils/compositor/htmlAst';
8
8
  import { callAskLemurAPI } from '@/utils/compositor/aiGeneration';
9
9
  import type { TemplatePane } from '@/types/compositorTypes';
10
10
 
@@ -51,7 +51,7 @@ export const AiRefineDesignStep = ({
51
51
 
52
52
  userPrompt = userPrompt.replace('{{DESIGN_NOTES}}', prompt);
53
53
  userPrompt = userPrompt.replace('{{CSS_INPUT}}', initialCss);
54
- userPrompt = userPrompt.replace('{{HTML_INPUT}}', initialHtml);
54
+ userPrompt = userPrompt.replace('{{HTML_INPUT}}', cleanHtml(initialHtml));
55
55
 
56
56
  // 1. Get RAW output from AI
57
57
  const resultHtml = await callAskLemurAPI({
@@ -1,7 +1,11 @@
1
1
  import { useState, useCallback, useMemo, useEffect } from 'react';
2
2
  import prompts from '@/constants/prompts.json';
3
- import { parseAiPane } from '@/utils/compositor/aiPaneParser';
3
+ import {
4
+ parseAiPane,
5
+ createDefaultShell,
6
+ } from '@/utils/compositor/aiPaneParser';
4
7
  import { callAskLemurAPI } from '@/utils/compositor/aiGeneration';
8
+ import { markdownToHtml } from '@/utils/compositor/htmlAst';
5
9
  import { CopyInputStep, type CopyMode } from './CopyInputStep';
6
10
  import { AiDesignStep, type AiDesignConfig } from './AiDesignStep';
7
11
  import BooleanToggle from '@/components/form/BooleanToggle';
@@ -43,10 +47,13 @@ export const AiStandardDesignStep = ({
43
47
  const [col1Copy, setCol1Copy] = useState('');
44
48
  const [col2Copy, setCol2Copy] = useState('');
45
49
 
50
+ // Prompts State
46
51
  const [masterShellSystem, setMasterShellSystem] = useState('');
47
52
  const [masterShellUser, setMasterShellUser] = useState('');
48
53
  const [masterCopySystem, setMasterCopySystem] = useState('');
49
54
  const [masterCopyUser, setMasterCopyUser] = useState('');
55
+ const [masterStyleSystem, setMasterStyleSystem] = useState('');
56
+ const [masterStyleUser, setMasterStyleUser] = useState('');
50
57
 
51
58
  const [aiDesignConfig, setAiDesignConfig] = useState<AiDesignConfig>({
52
59
  harmony: 'Analogous',
@@ -75,6 +82,7 @@ export const AiStandardDesignStep = ({
75
82
  }
76
83
  }, [promptOptions, selectedPromptId]);
77
84
 
85
+ // Load Prompt Templates
78
86
  useEffect(() => {
79
87
  if (!selectedPromptId) return;
80
88
 
@@ -85,8 +93,11 @@ export const AiStandardDesignStep = ({
85
93
 
86
94
  const promptKey = activeConfig.prompts.copy;
87
95
  const shellKey = activeConfig.prompts.shell;
96
+ const styleKey = activeConfig.prompts.style; // New: Load style prompt key
97
+
88
98
  const copyPromptGroup = (prompts as any)[promptKey];
89
99
  const shellPromptGroup = (prompts as any)[shellKey];
100
+ const stylePromptGroup = (prompts as any)[styleKey];
90
101
 
91
102
  if (shellPromptGroup) {
92
103
  setMasterShellSystem(shellPromptGroup.system || '');
@@ -96,6 +107,10 @@ export const AiStandardDesignStep = ({
96
107
  setMasterCopySystem(copyPromptGroup.system || '');
97
108
  setMasterCopyUser(copyPromptGroup.user_template || '');
98
109
  }
110
+ if (stylePromptGroup) {
111
+ setMasterStyleSystem(stylePromptGroup.system || '');
112
+ setMasterStyleUser(stylePromptGroup.user_template || '');
113
+ }
99
114
 
100
115
  if (!copyPromptGroup) return;
101
116
 
@@ -126,65 +141,128 @@ export const AiStandardDesignStep = ({
126
141
  );
127
142
  if (!activeConfig) throw new Error('Selected prompt type not found.');
128
143
 
129
- let designInput = `Generate a design using a **${aiDesignConfig.harmony.toLowerCase()}** color scheme with a **${aiDesignConfig.theme.toLowerCase()}** theme.`;
130
- if (aiDesignConfig.baseColor)
131
- designInput += ` Base the colors around **${aiDesignConfig.baseColor}**.`;
132
- if (aiDesignConfig.accentColor)
133
- designInput += ` Use **${aiDesignConfig.accentColor}** as an accent color.`;
134
- if (additionalNotes)
135
- designInput += ` Refine with these notes: "${additionalNotes}"`;
136
-
137
144
  const layoutType = 'Text Only';
138
145
  const promptMap = prompts as any;
139
146
 
140
- const injectTopic = (text: string) => {
141
- return text.replace('{{TOPIC}}', topic.trim());
142
- };
143
-
144
- let formattedShellPrompt = masterShellUser
145
- .replace('{{DESIGN_INPUT}}', designInput)
146
- .replace('{{LAYOUT_TYPE}}', layoutType);
147
+ // --- 1. Shell Generation ---
148
+ let shellResult = '';
147
149
 
148
- if (layoutChoice === 'grid') {
149
- const finalOverallPrompt = injectTopic(overallPrompt);
150
- formattedShellPrompt = formattedShellPrompt.replace(
151
- '{{COPY_INPUT}}',
152
- finalOverallPrompt
153
- );
154
- }
155
-
156
- const shellResult = await callAskLemurAPI({
157
- prompt: formattedShellPrompt,
158
- context: masterShellSystem,
159
- expectJson: true,
160
- isSandboxMode,
161
- });
150
+ if (isAiStyling) {
151
+ // AI Path: Generate Shell via AskLemur
152
+ let designInput = `Generate a design using a **${aiDesignConfig.harmony.toLowerCase()}** color scheme with a **${aiDesignConfig.theme.toLowerCase()}** theme.`;
153
+ if (aiDesignConfig.baseColor)
154
+ designInput += ` Base the colors around **${aiDesignConfig.baseColor}**.`;
155
+ if (aiDesignConfig.accentColor)
156
+ designInput += ` Use **${aiDesignConfig.accentColor}** as an accent color.`;
157
+ if (additionalNotes)
158
+ designInput += ` Refine with these notes: "${additionalNotes}"`;
162
159
 
163
- if (layoutChoice === 'standard') {
164
- const copyInputContent =
165
- copyMode === 'prompt' ? injectTopic(promptValue) : copyValue;
160
+ const injectTopic = (text: string) => {
161
+ return text.replace('{{TOPIC}}', topic.trim());
162
+ };
166
163
 
167
- const formattedCopyPrompt = masterCopyUser
168
- .replace('{{COPY_INPUT}}', copyInputContent)
164
+ let formattedShellPrompt = masterShellUser
169
165
  .replace('{{DESIGN_INPUT}}', designInput)
170
- .replace('{{LAYOUT_TYPE}}', layoutType)
171
- .replace('{{SHELL_JSON}}', shellResult);
166
+ .replace('{{LAYOUT_TYPE}}', layoutType);
167
+
168
+ if (layoutChoice === 'grid') {
169
+ const finalOverallPrompt = injectTopic(overallPrompt);
170
+ formattedShellPrompt = formattedShellPrompt.replace(
171
+ '{{COPY_INPUT}}',
172
+ finalOverallPrompt
173
+ );
174
+ }
172
175
 
173
- const copyResult = await callAskLemurAPI({
174
- prompt: formattedCopyPrompt,
175
- context: masterCopySystem,
176
- expectJson: false,
176
+ shellResult = await callAskLemurAPI({
177
+ prompt: formattedShellPrompt,
178
+ context: masterShellSystem,
179
+ expectJson: true,
177
180
  isSandboxMode,
178
181
  });
182
+ } else {
183
+ // No AI Path: Use Default Shell
184
+ shellResult = JSON.stringify(createDefaultShell(layoutChoice));
185
+ }
186
+
187
+ const designInputForCopy = isAiStyling
188
+ ? `Using a **${aiDesignConfig.harmony.toLowerCase()}** color scheme.`
189
+ : 'Use the provided default classes.';
190
+
191
+ // --- 2. Content Generation ---
192
+ if (layoutChoice === 'standard') {
193
+ let finalHtml = '';
194
+
195
+ if (copyMode === 'prompt') {
196
+ // A. Prompt Mode: AI writes copy
197
+ const injectTopic = (text: string) =>
198
+ text.replace('{{TOPIC}}', topic.trim());
199
+ const copyInputContent = injectTopic(promptValue);
200
+
201
+ const formattedCopyPrompt = masterCopyUser
202
+ .replace('{{COPY_INPUT}}', copyInputContent)
203
+ .replace('{{DESIGN_INPUT}}', designInputForCopy)
204
+ .replace('{{LAYOUT_TYPE}}', layoutType)
205
+ .replace('{{SHELL_JSON}}', shellResult);
206
+
207
+ finalHtml = await callAskLemurAPI({
208
+ prompt: formattedCopyPrompt,
209
+ context: masterCopySystem,
210
+ expectJson: false,
211
+ isSandboxMode,
212
+ });
213
+ } else {
214
+ // B. Raw Mode: User provided markdown
215
+ if (isAiStyling) {
216
+ // Style with AI: Use the 'style' prompt to format markdown
217
+ const formattedStylePrompt = masterStyleUser
218
+ .replace('{{SHELL_JSON}}', shellResult)
219
+ .replace('{{COPY_INPUT}}', copyValue);
220
+
221
+ finalHtml = await callAskLemurAPI({
222
+ prompt: formattedStylePrompt,
223
+ context: masterStyleSystem,
224
+ expectJson: false,
225
+ isSandboxMode,
226
+ });
227
+ } else {
228
+ // No AI: Local conversion
229
+ finalHtml = markdownToHtml(copyValue);
230
+ }
231
+ }
179
232
 
180
- const finalPane = parseAiPane(shellResult, copyResult, layoutType);
233
+ const finalPane = parseAiPane(shellResult, finalHtml, layoutType);
181
234
  onCreatePane(finalPane);
182
235
  } else if (layoutChoice === 'grid') {
236
+ // --- Grid Layout Path ---
183
237
  if (copyMode === 'raw') {
238
+ // B. Raw Mode (Grid)
184
239
  const rawContents = [col1Copy, col2Copy];
185
- const finalPane = parseAiPane(shellResult, rawContents, layoutType);
240
+ let finalContents: string[] = [];
241
+
242
+ if (isAiStyling) {
243
+ // Style with AI: Loop through columns and style each
244
+ for (const content of rawContents) {
245
+ const formattedStylePrompt = masterStyleUser
246
+ .replace('{{SHELL_JSON}}', shellResult)
247
+ .replace('{{COPY_INPUT}}', content);
248
+
249
+ const styledHtml = await callAskLemurAPI({
250
+ prompt: formattedStylePrompt,
251
+ context: masterStyleSystem,
252
+ expectJson: false,
253
+ isSandboxMode,
254
+ });
255
+ finalContents.push(styledHtml);
256
+ }
257
+ } else {
258
+ // No AI: Local conversion
259
+ finalContents = rawContents.map(markdownToHtml);
260
+ }
261
+
262
+ const finalPane = parseAiPane(shellResult, finalContents, layoutType);
186
263
  onCreatePane(finalPane);
187
264
  } else {
265
+ // A. Prompt Mode (Grid): AI writes copy
188
266
  const copyPromptKey = activeConfig.prompts.copy;
189
267
  const copyPromptDetails = promptMap[copyPromptKey];
190
268
  const preset =
@@ -192,6 +270,9 @@ export const AiStandardDesignStep = ({
192
270
  copyPromptDetails.presets?.heroDefault;
193
271
  const copyResults: string[] = [];
194
272
 
273
+ const injectTopic = (text: string) =>
274
+ text.replace('{{TOPIC}}', topic.trim());
275
+
195
276
  const promptsToRun = [
196
277
  { prompt: promptValueCol1, presetKey: 'left' as ColumnPresetKey },
197
278
  { prompt: promptValueCol2, presetKey: 'right' as ColumnPresetKey },
@@ -204,7 +285,7 @@ export const AiStandardDesignStep = ({
204
285
  .replace('{{SHELL_JSON}}', shellResult)
205
286
  .replace('{{COPY_INPUT}}', injectTopic(overallPrompt))
206
287
  .replace('{{COLUMN_PROMPT}}', item.prompt)
207
- .replace('{{DESIGN_INPUT}}', designInput)
288
+ .replace('{{DESIGN_INPUT}}', designInputForCopy)
208
289
  .replace('{{LAYOUT_TYPE}}', layoutType)
209
290
  .replace('{{COLUMN_EXAMPLE}}', columnPreset.example);
210
291
 
@@ -245,16 +326,25 @@ export const AiStandardDesignStep = ({
245
326
  masterShellSystem,
246
327
  masterCopyUser,
247
328
  masterCopySystem,
329
+ masterStyleUser,
330
+ masterStyleSystem,
248
331
  topic,
332
+ isAiStyling, // Added dependency
249
333
  ]);
250
334
 
251
335
  if (isGenerating) {
252
336
  return (
253
337
  <div className="flex min-h-96 flex-col items-center justify-center space-y-4 p-6">
254
338
  <div className="h-8 w-8 animate-spin rounded-full border-b-2 border-cyan-600"></div>
255
- <p className="text-sm text-gray-600">Generating Design & Content...</p>
339
+ <p className="text-sm text-gray-600">
340
+ {isAiStyling
341
+ ? 'Generating Design & Content...'
342
+ : 'Processing Content...'}
343
+ </p>
256
344
  <p className="text-xs text-gray-500">
257
- Creating a unique layout from scratch.
345
+ {isAiStyling
346
+ ? 'Creating a unique layout from scratch.'
347
+ : 'Applying standard formatting.'}
258
348
  </p>
259
349
  </div>
260
350
  );
@@ -301,42 +391,45 @@ export const AiStandardDesignStep = ({
301
391
  masterCopyUser={masterCopyUser}
302
392
  setMasterCopyUser={setMasterCopyUser}
303
393
  >
304
- <div className="border-t pt-4">
305
- <div>
306
- <label
307
- htmlFor="dashboard-notes"
308
- className="block text-sm font-bold text-gray-700"
309
- >
310
- Design Notes
311
- </label>
312
- <textarea
313
- id="dashboard-notes"
314
- rows={2}
315
- className="mt-1 block w-full rounded-md border-gray-300 px-3 py-2 text-sm shadow-sm focus:border-cyan-500 focus:ring-cyan-500"
316
- placeholder="e.g. Clean, minimal, high contrast..."
317
- value={additionalNotes}
318
- onChange={(e) => setAdditionalNotes(e.target.value)}
319
- />
320
- </div>
321
-
322
- <div className="my-4 flex items-center">
323
- <BooleanToggle
324
- label="Customize Colors"
325
- value={showColors}
326
- onChange={setShowColors}
327
- size="sm"
328
- />
329
- </div>
394
+ {/* Style config is only relevant if AI Styling is ON */}
395
+ {isAiStyling && (
396
+ <div className="border-t pt-4">
397
+ <div>
398
+ <label
399
+ htmlFor="dashboard-notes"
400
+ className="block text-sm font-bold text-gray-700"
401
+ >
402
+ Design Notes
403
+ </label>
404
+ <textarea
405
+ id="dashboard-notes"
406
+ rows={2}
407
+ className="mt-1 block w-full rounded-md border-gray-300 px-3 py-2 text-sm shadow-sm focus:border-cyan-500 focus:ring-cyan-500"
408
+ placeholder="e.g. Clean, minimal, high contrast..."
409
+ value={additionalNotes}
410
+ onChange={(e) => setAdditionalNotes(e.target.value)}
411
+ />
412
+ </div>
330
413
 
331
- {showColors && (
332
- <div className="rounded-lg border border-gray-100 bg-gray-50 p-4">
333
- <AiDesignStep
334
- designConfig={aiDesignConfig}
335
- onDesignConfigChange={setAiDesignConfig}
414
+ <div className="my-4 flex items-center">
415
+ <BooleanToggle
416
+ label="Customize Colors"
417
+ value={showColors}
418
+ onChange={setShowColors}
419
+ size="sm"
336
420
  />
337
421
  </div>
338
- )}
339
- </div>
422
+
423
+ {showColors && (
424
+ <div className="rounded-lg border border-gray-100 bg-gray-50 p-4">
425
+ <AiDesignStep
426
+ designConfig={aiDesignConfig}
427
+ onDesignConfigChange={setAiDesignConfig}
428
+ />
429
+ </div>
430
+ )}
431
+ </div>
432
+ )}
340
433
  </CopyInputStep>
341
434
 
342
435
  {error && (
@@ -363,7 +456,7 @@ export const AiStandardDesignStep = ({
363
456
  }
364
457
  className="rounded-md bg-cyan-600 px-6 py-2 text-sm font-bold text-white shadow-sm hover:bg-cyan-700 disabled:cursor-not-allowed disabled:bg-gray-400"
365
458
  >
366
- ✨ Generate Pane
459
+ {isAiStyling ? '✨ Generate Pane' : 'Create Pane'}
367
460
  </button>
368
461
  </div>
369
462
  </div>
@@ -1,5 +1,4 @@
1
1
  import { useState } from 'react';
2
- import { getCtx } from '@/stores/nodes';
3
2
  import { htmlToHtmlAst } from '@/utils/compositor/htmlAst';
4
3
  import type { TemplatePane } from '@/types/compositorTypes';
5
4
 
@@ -28,7 +27,6 @@ export const CreativeInjectStep = ({
28
27
 
29
28
  try {
30
29
  const htmlAst = await htmlToHtmlAst(html, css);
31
-
32
30
  const template: TemplatePane = {
33
31
  id: '',
34
32
  nodeType: 'Pane',
@@ -48,10 +46,7 @@ export const CreativeInjectStep = ({
48
46
  nodes: [],
49
47
  },
50
48
  };
51
-
52
49
  onCreatePane(template);
53
- const ctx = getCtx();
54
- ctx.showSaveBypass.set(true);
55
50
  } catch (err: any) {
56
51
  console.error('Compiler Error:', err);
57
52
  setError(`Compilation failed: ${err.message}`);
@@ -56,8 +56,9 @@ const TemplatePreviewItem = ({
56
56
  );
57
57
 
58
58
  const fragmentRequest = useMemo((): PanePreviewRequest[] => {
59
- // This preview logic is correct: it creates a *temporary* context.
59
+ // This preview logic creates a *temporary* context.
60
60
  const ctx = new NodesContext();
61
+ ctx.isTemplate.set(true);
61
62
  ctx.addNode(createEmptyStorykeep('tmp'));
62
63
  ctx.addTemplatePane('tmp', liveTemplate);
63
64
  return [{ id: liveTemplate.id, ctx }];
@@ -104,10 +104,7 @@ const StyleBreakPanel = ({ node, parentNode }: BasePanelProps) => {
104
104
  }
105
105
  });
106
106
 
107
- const updatedNodes = [
108
- { ...breakNode, isChanged: true },
109
- { ...paneNode, isChanged: true },
110
- ];
107
+ const updatedNodes = [{ ...breakNode }, { ...paneNode }];
111
108
  ctx.modifyNodes(updatedNodes);
112
109
  }, [settings, node, parentNode, ctx, allNodes]);
113
110
 
@@ -64,7 +64,6 @@ const StyleCodeHookPanel = ({
64
64
  codeHookPayload: {
65
65
  options: JSON.stringify(options),
66
66
  },
67
- isChanged: true,
68
67
  };
69
68
  ctx.modifyNodes([updatedNode]);
70
69
  },
@@ -215,7 +215,7 @@ const StyleElementPanel = ({
215
215
  ...preset,
216
216
  };
217
217
 
218
- ctx.modifyNodes([{ ...targetNode, isChanged: true }]);
218
+ ctx.modifyNodes([{ ...targetNode }]);
219
219
 
220
220
  setShowPresets(false);
221
221
  };
@@ -100,10 +100,7 @@ const StyleElementRemovePanel = ({
100
100
  }
101
101
  }
102
102
 
103
- ctx.modifyNodes([
104
- { ...elementNode, isChanged: true },
105
- { ...markdownNode, isChanged: true },
106
- ]);
103
+ ctx.modifyNodes([{ ...elementNode }, { ...markdownNode }]);
107
104
  resetStore();
108
105
  };
109
106
 
@@ -131,7 +131,7 @@ const StyleElementUpdatePanel = ({
131
131
  break;
132
132
  }
133
133
 
134
- ctx.modifyNodes([{ ...elementNode, isChanged: true }]);
134
+ ctx.modifyNodes([{ ...elementNode }]);
135
135
  } else {
136
136
  // Update default classes
137
137
  const markdownNode = cloneDeep(
@@ -170,7 +170,7 @@ const StyleElementUpdatePanel = ({
170
170
  break;
171
171
  }
172
172
 
173
- ctx.modifyNodes([{ ...markdownNode, isChanged: true }]);
173
+ ctx.modifyNodes([{ ...markdownNode }]);
174
174
  }
175
175
 
176
176
  setPendingUpdate(null);
@@ -251,7 +251,7 @@ const StyleElementUpdatePanel = ({
251
251
  }
252
252
  }
253
253
 
254
- ctx.modifyNodes([{ ...elementNode, isChanged: true }]);
254
+ ctx.modifyNodes([{ ...elementNode }]);
255
255
  setIsOverridden(checked);
256
256
  },
257
257
  [node, className, parentNode]
@@ -269,7 +269,7 @@ const StyleImagePanel = ({
269
269
  const imgNode = cloneDeep(allNodes.get(node.id)) as FlatNode;
270
270
  if (!imgNode) return;
271
271
  imgNode.alt = newAlt;
272
- ctx.modifyNodes([{ ...imgNode, isChanged: true }]);
272
+ ctx.modifyNodes([{ ...imgNode }]);
273
273
  };
274
274
 
275
275
  const handleImageUpdate = (params: ImageParams) => {
@@ -285,7 +285,7 @@ const StyleImagePanel = ({
285
285
  params.altDescription || '!!This is requires a description!!'
286
286
  );
287
287
  if (params.srcSet) imgNode.srcSet = params.srcSet;
288
- ctx.modifyNodes([{ ...imgNode, isChanged: true }]);
288
+ ctx.modifyNodes([{ ...imgNode }]);
289
289
  };
290
290
 
291
291
  const handleImageRemove = () => {
@@ -297,7 +297,7 @@ const StyleImagePanel = ({
297
297
  imgNode.src = `/static.jpg`;
298
298
  if (typeof imgNode.srcSet === `string`) delete imgNode.srcSet;
299
299
  imgNode.alt = `This is a placeholder for an image that hasn't yet been uploaded`;
300
- ctx.modifyNodes([{ ...imgNode, isChanged: true }]);
300
+ ctx.modifyNodes([{ ...imgNode }]);
301
301
  };
302
302
 
303
303
  if (
@@ -109,10 +109,7 @@ const StyleImageRemovePanel = ({
109
109
  }
110
110
  }
111
111
 
112
- ctx.modifyNodes([
113
- { ...targetNode, isChanged: true },
114
- { ...parentNodeClone, isChanged: true },
115
- ]);
112
+ ctx.modifyNodes([{ ...targetNode }, { ...parentNodeClone }]);
116
113
  resetStore();
117
114
  };
118
115
 
@@ -18,7 +18,6 @@ const StyleImageUpdatePanel = ({
18
18
  node,
19
19
  parentNode,
20
20
  className,
21
- config,
22
21
  childId,
23
22
  }: BasePanelProps) => {
24
23
  if (
@@ -119,7 +118,7 @@ const StyleImageUpdatePanel = ({
119
118
  break;
120
119
  }
121
120
 
122
- ctx.modifyNodes([{ ...targetNode, isChanged: true }]);
121
+ ctx.modifyNodes([{ ...targetNode }]);
123
122
  } else {
124
123
  const markdownNode = cloneDeep(
125
124
  allNodes.get(parentNode.id)
@@ -158,7 +157,7 @@ const StyleImageUpdatePanel = ({
158
157
  break;
159
158
  }
160
159
 
161
- ctx.modifyNodes([{ ...markdownNode, isChanged: true }]);
160
+ ctx.modifyNodes([{ ...markdownNode }]);
162
161
  }
163
162
 
164
163
  setPendingUpdate(null);
@@ -240,7 +239,7 @@ const StyleImageUpdatePanel = ({
240
239
  }
241
240
  }
242
241
 
243
- ctx.modifyNodes([{ ...targetNode, isChanged: true }]);
242
+ ctx.modifyNodes([{ ...targetNode }]);
244
243
  setIsOverridden(checked);
245
244
  },
246
245
  [node, className, parentNode, childId, isImage]