astro-tractstack 2.2.2 → 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 (67) 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 -134
  9. package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag.tsx +2 -4
  10. package/templates/src/components/edit/Header.tsx +1 -2
  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/ConfigPanePanel.tsx +2 -2
  17. package/templates/src/components/edit/pane/PanePanel_impression.tsx +0 -4
  18. package/templates/src/components/edit/pane/PanePanel_path.tsx +0 -1
  19. package/templates/src/components/edit/pane/PanePanel_title.tsx +1 -2
  20. package/templates/src/components/edit/pane/RestylePaneModal.tsx +1 -4
  21. package/templates/src/components/edit/pane/steps/AiCreativeDesignStep.tsx +0 -3
  22. package/templates/src/components/edit/pane/steps/AiRefineDesignStep.tsx +2 -2
  23. package/templates/src/components/edit/pane/steps/AiStandardDesignStep.tsx +173 -80
  24. package/templates/src/components/edit/pane/steps/CreativeInjectStep.tsx +0 -5
  25. package/templates/src/components/edit/pane/steps/DesignLibraryStep.tsx +2 -1
  26. package/templates/src/components/edit/panels/StyleBreakPanel.tsx +1 -4
  27. package/templates/src/components/edit/panels/StyleCodeHookPanel.tsx +0 -1
  28. package/templates/src/components/edit/panels/StyleElementPanel.tsx +1 -1
  29. package/templates/src/components/edit/panels/StyleElementPanel_remove.tsx +1 -4
  30. package/templates/src/components/edit/panels/StyleElementPanel_update.tsx +3 -3
  31. package/templates/src/components/edit/panels/StyleImagePanel.tsx +3 -3
  32. package/templates/src/components/edit/panels/StyleImagePanel_remove.tsx +1 -4
  33. package/templates/src/components/edit/panels/StyleImagePanel_update.tsx +3 -4
  34. package/templates/src/components/edit/panels/StyleLiElementPanel_remove.tsx +1 -4
  35. package/templates/src/components/edit/panels/StyleLiElementPanel_update.tsx +3 -3
  36. package/templates/src/components/edit/panels/StyleLinkPanel.tsx +1 -1
  37. package/templates/src/components/edit/panels/StyleLinkPanel_config.tsx +1 -1
  38. package/templates/src/components/edit/panels/StyleLinkPanel_remove.tsx +1 -1
  39. package/templates/src/components/edit/panels/StyleLinkPanel_update.tsx +1 -1
  40. package/templates/src/components/edit/panels/StyleParentPanel.tsx +0 -7
  41. package/templates/src/components/edit/panels/StyleParentPanel_deleteLayer.tsx +0 -2
  42. package/templates/src/components/edit/panels/StyleParentPanel_remove.tsx +0 -2
  43. package/templates/src/components/edit/panels/StyleParentPanel_update.tsx +0 -2
  44. package/templates/src/components/edit/panels/StyleWidgetPanel_config.tsx +0 -3
  45. package/templates/src/components/edit/panels/StyleWidgetPanel_remove.tsx +1 -4
  46. package/templates/src/components/edit/panels/StyleWidgetPanel_update.tsx +3 -4
  47. package/templates/src/components/edit/panels/StyleWordCarouselPanel.tsx +0 -2
  48. package/templates/src/components/edit/state/StylesMemory.tsx +3 -9
  49. package/templates/src/components/edit/storyfragment/StoryFragmentConfigPanel.tsx +0 -1
  50. package/templates/src/components/edit/storyfragment/StoryFragmentPanel_menu.tsx +0 -2
  51. package/templates/src/components/edit/storyfragment/StoryFragmentPanel_og.tsx +0 -2
  52. package/templates/src/components/edit/storyfragment/StoryFragmentPanel_slug.tsx +0 -1
  53. package/templates/src/components/edit/storyfragment/StoryFragmentPanel_title.tsx +0 -1
  54. package/templates/src/components/fields/ArtpackImage.tsx +0 -7
  55. package/templates/src/components/fields/BackgroundImage.tsx +0 -14
  56. package/templates/src/components/fields/BackgroundImageWrapper.tsx +0 -5
  57. package/templates/src/components/fields/ImageUpload.tsx +0 -3
  58. package/templates/src/pages/[...slug]/edit.astro +0 -1
  59. package/templates/src/pages/sandbox.astro +0 -1
  60. package/templates/src/stores/nodes.ts +278 -312
  61. package/templates/src/stores/nodesHistory.ts +59 -24
  62. package/templates/src/utils/api/setupHelpers.ts +1 -1
  63. package/templates/src/utils/compositor/aiPaneParser.ts +57 -0
  64. package/templates/src/utils/compositor/designLibraryHelper.ts +1 -3
  65. package/templates/src/utils/compositor/htmlAst.ts +109 -2
  66. package/templates/src/utils/compositor/nodesHelper.ts +1 -9
  67. package/templates/src/utils/compositor/savePipeline.ts +1 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro-tractstack",
3
- "version": "2.2.2",
3
+ "version": "2.2.3",
4
4
  "description": "Astro integration for TractStack - the free web press by At Risk Media",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -149,7 +149,6 @@ const BunnyVideoSetup = ({ nodeId, params }: BunnyVideoSetupProps) => {
149
149
  bgColor,
150
150
  }),
151
151
  },
152
- isChanged: true,
153
152
  };
154
153
 
155
154
  if (bgColor) {
@@ -90,7 +90,6 @@ const FeaturedArticleSetup = ({
90
90
  }),
91
91
  },
92
92
  bgColour: bgColor || undefined,
93
- isChanged: true,
94
93
  };
95
94
 
96
95
  // If bgColor is empty, remove the property
@@ -109,7 +109,6 @@ const ListContentSetup = ({ params, nodeId }: ListContentSetupProps) => {
109
109
  }),
110
110
  },
111
111
  bgColour: bgColor || undefined,
112
- isChanged: true,
113
112
  };
114
113
 
115
114
  // If bgColor is empty, remove the property
@@ -56,7 +56,6 @@ export const ProductCardSetup = (props: ProductCardSetupProps) => {
56
56
  target: paneNode.codeHookPayload?.target,
57
57
  options: JSON.stringify(newPayload),
58
58
  },
59
- isChanged: true,
60
59
  };
61
60
  ctx.modifyNodes([updatedPaneNode]);
62
61
  };
@@ -117,7 +117,6 @@ export const ProductGridSetup = (props: ProductGridSetupProps) => {
117
117
  target: paneNode.codeHookPayload?.target,
118
118
  options: JSON.stringify(constructPayload()),
119
119
  },
120
- isChanged: true,
121
120
  };
122
121
  ctx.modifyNodes([updatedPaneNode]);
123
122
  }, 500);
@@ -463,7 +463,6 @@ export const Compositor = (props: CompositorProps) => {
463
463
  {
464
464
  ...node,
465
465
  wordCarouselPayload: { words, speed: 2 },
466
- isChanged: true,
467
466
  } as FlatNode,
468
467
  ]);
469
468
  settingsPanelStore.set({
@@ -8,7 +8,6 @@ import {
8
8
  isTopLevelBlockNode,
9
9
  parseCodeHook,
10
10
  } from '@/utils/compositor/nodesHelper';
11
- import { isPaneNode, isGridLayoutNode } from '@/utils/compositor/typeGuards';
12
11
  import { PaneAddMode } from '@/types/compositorTypes';
13
12
  import { NodeOverlay } from './tools/NodeOverlay';
14
13
  import PanelVisibilityWrapper from '@/components/compositor/PanelVisibilityWrapper';
@@ -59,70 +58,32 @@ export const Node = memo((props: NodeProps) => {
59
58
  const ctx = getCtx(props);
60
59
  const node = ctx.allNodes.get().get(props.nodeId);
61
60
  const viewport = useStore(viewportKeyStore).value;
62
- const isPreview = ctx.rootNodeId.get() === `tmp`;
61
+ const isPreview = ctx.rootNodeId.get() === `tmp` || ctx.isTemplate.get();
63
62
  const hasPanes = useStore(ctx.hasPanes);
64
63
 
65
64
  if (!node) return null;
66
65
 
67
66
  let element: ReactNode = null;
68
67
 
69
- // 1. Root Types (StoryFragment & Pane) - handled with specific early returns or complex wrapping
70
- if (node.nodeType === 'StoryFragment') {
71
- const sf = node as StoryFragmentNode;
72
- if (!isPreview) ctx.hasTitle.set(!(!sf.slug || !sf.title));
68
+ switch (node.nodeType) {
69
+ case 'StoryFragment': {
70
+ const sf = node as StoryFragmentNode;
71
+ if (!isPreview) ctx.hasTitle.set(!(!sf.slug || !sf.title));
73
72
 
74
- if (!(sf.slug && sf.title)) {
75
- return (
76
- <div
77
- className="fixed inset-0 overflow-y-auto bg-black bg-opacity-75"
78
- style={{ zIndex: 9005 }}
79
- >
80
- <div className="flex min-h-screen items-center justify-center p-1.5">
81
- <div
82
- className="fixed inset-0 bg-black opacity-65"
83
- onClick={() => (window.location.href = '/storykeep')}
84
- />
85
- <div className="relative w-full max-w-4xl rounded-lg bg-white shadow-xl">
86
- <div className="p-6">
87
- <StoryFragmentTitlePanel nodeId={props.nodeId} />
88
- </div>
89
- </div>
90
- </div>
91
- </div>
92
- );
93
- }
94
-
95
- if (!hasPanes && !isPreview) {
96
- return <EmptyPageHandler {...props} />;
97
- }
98
-
99
- return (
100
- <>
101
- <StoryFragmentConfigPanel
102
- nodeId={props.nodeId}
103
- isSandboxMode={props.isSandboxMode || false}
104
- />
105
- <StoryFragment {...props} />
106
- </>
107
- );
108
- } else if (isPaneNode(node)) {
109
- const paneNode = node as PaneNode;
110
- const isHtmlAstPane = !!paneNode.htmlAst;
111
- const paneNodes = ctx.getChildNodeIDs(node.id);
112
-
113
- if (paneNode.isContextPane) {
114
- if (!isPreview) ctx.hasTitle.set(!(!paneNode.slug || !paneNode.title));
115
-
116
- if (!isPreview && !(paneNode.slug && paneNode.title)) {
73
+ if (!(sf.slug && sf.title)) {
117
74
  return (
118
75
  <div
119
76
  className="fixed inset-0 overflow-y-auto bg-black bg-opacity-75"
120
77
  style={{ zIndex: 9005 }}
121
78
  >
122
79
  <div className="flex min-h-screen items-center justify-center p-1.5">
80
+ <div
81
+ className="fixed inset-0 bg-black opacity-65"
82
+ onClick={() => (window.location.href = '/storykeep')}
83
+ />
123
84
  <div className="relative w-full max-w-4xl rounded-lg bg-white shadow-xl">
124
85
  <div className="p-6">
125
- <ContextPaneTitlePanel nodeId={props.nodeId} />
86
+ <StoryFragmentTitlePanel nodeId={props.nodeId} />
126
87
  </div>
127
88
  </div>
128
89
  </div>
@@ -130,97 +91,159 @@ export const Node = memo((props: NodeProps) => {
130
91
  );
131
92
  }
132
93
 
133
- if (!isPreview && !paneNodes.length) {
134
- // Context Pane Empty State
135
- return (
136
- <>
137
- <ContextPanePanel nodeId={node.id} />
138
- <PanelVisibilityWrapper nodeId={node.id} panelType="add" ctx={ctx}>
139
- <Pane {...props} />
140
- </PanelVisibilityWrapper>
141
- <AddPanePanel
142
- nodeId={node.id}
143
- first={true}
144
- ctx={ctx}
145
- isContextPane={true}
146
- />
147
- </>
148
- );
94
+ if (!hasPanes && !isPreview) {
95
+ return <EmptyPageHandler {...props} />;
149
96
  }
150
- }
151
97
 
152
- // Resolve Content
153
- const content = isHtmlAstPane ? (
154
- <CreativePane nodeId={props.nodeId} htmlAst={paneNode.htmlAst!} />
155
- ) : (
156
- <Pane {...props} />
157
- );
158
-
159
- element = (
160
- <>
161
- <div className="py-0.5">
162
- <ConfigPanePanel
98
+ return (
99
+ <>
100
+ <StoryFragmentConfigPanel
163
101
  nodeId={props.nodeId}
164
- isHtmlAstPane={isHtmlAstPane}
165
102
  isSandboxMode={props.isSandboxMode || false}
166
103
  />
167
- <PanelVisibilityWrapper
168
- nodeId={props.nodeId}
169
- panelType="settings"
170
- ctx={ctx}
171
- >
172
- {content}
173
- </PanelVisibilityWrapper>
174
- </div>
175
- <AddPanePanel nodeId={props.nodeId} first={false} ctx={ctx} />
176
- </>
177
- );
178
- }
179
- // 2. Content Types
180
- else if (node.nodeType === 'BgPane') {
181
- element = <BgPaneWrapper {...props} />;
182
- } else if (node.nodeType === 'Markdown') {
183
- element = <Markdown {...props} />;
184
- } else if (isGridLayoutNode(node)) {
185
- element = <GridLayout {...props} />;
186
- } else if (node.nodeType === 'TagElement') {
187
- const flatNode = node as FlatNode;
188
-
189
- switch (flatNode.tagName) {
190
- case 'text':
191
- element = <NodeText {...props} />;
192
- break;
193
- case 'img':
194
- element = <NodeImg {...props} />;
195
- break;
196
- case 'a':
197
- element = <NodeA {...props} />;
198
- break;
199
- case 'button':
200
- element = <NodeButton {...props} />;
201
- break;
202
- case 'code': {
203
- const hookData = parseCodeHook(node);
204
- element = hookData ? (
205
- <Widget
206
- {...props}
207
- hook={hookData.hook}
208
- value1={hookData.value1}
209
- value2={hookData.value2}
210
- value3={hookData.value3}
211
- />
212
- ) : null;
213
- break;
214
- }
215
- default:
104
+ <StoryFragment {...props} />
105
+ </>
106
+ );
107
+ }
108
+ case 'Pane':
109
+ {
110
+ const paneNode = node as PaneNode;
111
+ const storyfragmentNodeId = ctx.getClosestNodeTypeFromId(
112
+ node.id,
113
+ 'StoryFragment'
114
+ );
115
+ const storyfragmentNode = ctx.allNodes
116
+ .get()
117
+ .get(storyfragmentNodeId) as StoryFragmentNode;
118
+ const first = paneNode.id === storyfragmentNode.paneIds?.[0];
119
+ const isHtmlAstPane = !!paneNode.htmlAst;
120
+ const paneNodes = ctx.getChildNodeIDs(node.id);
121
+
122
+ if (paneNode.isContextPane) {
123
+ if (!isPreview)
124
+ ctx.hasTitle.set(!(!paneNode.slug || !paneNode.title));
125
+
126
+ if (!isPreview && !(paneNode.slug && paneNode.title)) {
127
+ return (
128
+ <div
129
+ className="fixed inset-0 overflow-y-auto bg-black bg-opacity-75"
130
+ style={{ zIndex: 9005 }}
131
+ >
132
+ <div className="flex min-h-screen items-center justify-center p-1.5">
133
+ <div className="relative w-full max-w-4xl rounded-lg bg-white shadow-xl">
134
+ <div className="p-6">
135
+ <ContextPaneTitlePanel nodeId={props.nodeId} />
136
+ </div>
137
+ </div>
138
+ </div>
139
+ </div>
140
+ );
141
+ }
142
+
143
+ if (!isPreview && !paneNodes.length) {
144
+ return (
145
+ <>
146
+ <ContextPanePanel nodeId={node.id} />
147
+ <PanelVisibilityWrapper
148
+ nodeId={node.id}
149
+ panelType="add"
150
+ ctx={ctx}
151
+ >
152
+ <Pane {...props} />
153
+ </PanelVisibilityWrapper>
154
+ <AddPanePanel
155
+ nodeId={node.id}
156
+ first={true}
157
+ ctx={ctx}
158
+ isContextPane={true}
159
+ />
160
+ </>
161
+ );
162
+ }
163
+ }
164
+
165
+ const content = isHtmlAstPane ? (
166
+ <CreativePane nodeId={props.nodeId} htmlAst={paneNode.htmlAst!} />
167
+ ) : (
168
+ <Pane {...props} />
169
+ );
170
+
216
171
  element = (
217
- <NodeBasicTag
218
- {...props}
219
- tagName={flatNode.tagName as keyof JSX.IntrinsicElements}
220
- isSelectableText={true}
221
- />
172
+ <>
173
+ {first && (
174
+ <AddPanePanel nodeId={props.nodeId} first={true} ctx={ctx} />
175
+ )}
176
+ <div className="py-0.5">
177
+ <ConfigPanePanel
178
+ nodeId={props.nodeId}
179
+ isHtmlAstPane={isHtmlAstPane}
180
+ isSandboxMode={props.isSandboxMode || false}
181
+ />
182
+ <PanelVisibilityWrapper
183
+ nodeId={props.nodeId}
184
+ panelType="settings"
185
+ ctx={ctx}
186
+ >
187
+ {content}
188
+ </PanelVisibilityWrapper>
189
+ </div>
190
+ <AddPanePanel nodeId={props.nodeId} first={false} ctx={ctx} />
191
+ </>
222
192
  );
223
- break;
193
+ }
194
+ break;
195
+
196
+ case 'BgPane':
197
+ element = <BgPaneWrapper {...props} />;
198
+ break;
199
+
200
+ case 'Markdown':
201
+ element = <Markdown {...props} />;
202
+ break;
203
+
204
+ case 'GridLayoutNode':
205
+ element = <GridLayout {...props} />;
206
+ break;
207
+
208
+ case 'TagElement': {
209
+ const flatNode = node as FlatNode;
210
+
211
+ switch (flatNode.tagName) {
212
+ case 'text':
213
+ element = <NodeText {...props} />;
214
+ break;
215
+ case 'img':
216
+ element = <NodeImg {...props} />;
217
+ break;
218
+ case 'a':
219
+ element = <NodeA {...props} />;
220
+ break;
221
+ case 'button':
222
+ element = <NodeButton {...props} />;
223
+ break;
224
+ case 'code': {
225
+ const hookData = parseCodeHook(node);
226
+ element = hookData ? (
227
+ <Widget
228
+ {...props}
229
+ hook={hookData.hook}
230
+ value1={hookData.value1}
231
+ value2={hookData.value2}
232
+ value3={hookData.value3}
233
+ />
234
+ ) : null;
235
+ break;
236
+ }
237
+ default:
238
+ element = (
239
+ <NodeBasicTag
240
+ {...props}
241
+ tagName={flatNode.tagName as keyof JSX.IntrinsicElements}
242
+ isSelectableText={true}
243
+ />
244
+ );
245
+ break;
246
+ }
224
247
  }
225
248
  }
226
249
 
@@ -497,7 +497,6 @@ export const NodeBasicTag = (props: NodeTagProps) => {
497
497
  const updatedNode = {
498
498
  ...cloneDeep(node),
499
499
  isPlaceholder: false,
500
- isChanged: true,
501
500
  };
502
501
  ctx.modifyNodes([updatedNode]);
503
502
  }
@@ -507,7 +506,7 @@ export const NodeBasicTag = (props: NodeTagProps) => {
507
506
  const paneNode = cloneDeep(
508
507
  ctx.allNodes.get().get(paneNodeId)
509
508
  ) as PaneNode;
510
- ctx.modifyNodes([{ ...paneNode, isChanged: true }]);
509
+ ctx.modifyNodes([paneNode]);
511
510
  }
512
511
  }
513
512
  } catch (error) {
@@ -573,7 +572,6 @@ export const NodeBasicTag = (props: NodeTagProps) => {
573
572
  const updatedNode = {
574
573
  ...cloneDeep(node),
575
574
  isPlaceholder: false,
576
- isChanged: true,
577
575
  };
578
576
  ctx.modifyNodes([updatedNode]);
579
577
  }
@@ -591,7 +589,7 @@ export const NodeBasicTag = (props: NodeTagProps) => {
591
589
  const paneNode = cloneDeep(
592
590
  ctx.allNodes.get().get(paneNodeId)
593
591
  ) as PaneNode;
594
- ctx.modifyNodes([{ ...paneNode, isChanged: true }]);
592
+ ctx.modifyNodes([paneNode]);
595
593
  }
596
594
 
597
595
  ctx.clearEditLock();
@@ -34,7 +34,6 @@ const StoryKeepHeader = ({
34
34
  const viewport = useStore(viewportModeStore);
35
35
  const pendingHomePageSlug = useStore(pendingHomePageSlugStore);
36
36
  const ctx = getCtx();
37
- const showSaveBypass = useStore(ctx.showSaveBypass);
38
37
  const hasTitle = useStore(ctx.hasTitle);
39
38
  const hasPanes = useStore(ctx.hasPanes);
40
39
  const [canUndo, setCanUndo] = useState(false);
@@ -124,7 +123,7 @@ const StoryKeepHeader = ({
124
123
  ];
125
124
 
126
125
  // Show save button if there are undo changes OR pending home page change
127
- const shouldShowSave = canUndo || pendingHomePageSlug || showSaveBypass;
126
+ const shouldShowSave = canUndo || pendingHomePageSlug;
128
127
 
129
128
  if (!hasTitle && !hasPanes) return null;
130
129
 
@@ -57,7 +57,7 @@ const PaneSlugPanel = ({ nodeId, setMode }: PaneSlugPanelProps) => {
57
57
  if (slug.length >= 3) {
58
58
  // Only update if meets minimum length
59
59
  const ctx = getCtx();
60
- const updatedNode = { ...cloneDeep(paneNode), slug, isChanged: true };
60
+ const updatedNode = { ...cloneDeep(paneNode), slug };
61
61
  ctx.modifyNodes([updatedNode]);
62
62
  }
63
63
  };
@@ -57,7 +57,6 @@ const ContextPaneTitlePanel = ({
57
57
  ...paneNode,
58
58
  title,
59
59
  ...(newSlug ? { slug: newSlug } : {}),
60
- isChanged: true,
61
60
  });
62
61
  ctx.modifyNodes([updatedNode]);
63
62
  }
@@ -114,6 +114,7 @@ const AddPaneBreakPanel = ({
114
114
  useEffect(() => {
115
115
  const newPreviews = filteredVariants.map((variant, index: number) => {
116
116
  const ctx = new NodesContext();
117
+ ctx.isTemplate.set(true);
117
118
  ctx.addNode(createEmptyStorykeep('tmp'));
118
119
  const template = templateCategory.getTemplatesByVariant(variant)[0];
119
120
  ctx.addTemplatePane('tmp', template);
@@ -134,22 +134,18 @@ const AddPaneNewPanel = ({
134
134
 
135
135
  if (isContextPane) {
136
136
  insertTemplate.isContextPane = true;
137
- await ctx.applyAtomicUpdate(async (tmpCtx) => {
138
- tmpCtx.addContextTemplatePane(ownerId, insertTemplate);
139
- });
137
+ ctx.addContextTemplatePane(ownerId, insertTemplate);
140
138
  } else {
141
- await ctx.applyAtomicUpdate(async (tmpCtx) => {
142
- tmpCtx.addTemplatePane(
143
- ownerId,
144
- insertTemplate,
145
- nodeId,
146
- first ? 'before' : 'after'
147
- );
148
- });
139
+ ctx.addTemplatePane(
140
+ ownerId,
141
+ insertTemplate,
142
+ nodeId,
143
+ first ? 'before' : 'after'
144
+ );
149
145
  const storyFragment = cloneDeep(
150
146
  ctx.allNodes.get().get(ownerId)
151
147
  ) as StoryFragmentNode;
152
- ctx.modifyNodes([{ ...storyFragment, isChanged: true }]);
148
+ ctx.modifyNodes([{ ...storyFragment }]);
153
149
  }
154
150
  ctx.notifyNode(`root`);
155
151
  setParentMode(PaneAddMode.DEFAULT, false);
@@ -91,11 +91,15 @@ const AddPaneReUsePanel = ({
91
91
  pane.slug.toLowerCase().includes(query.toLowerCase())
92
92
  );
93
93
 
94
- const newPreviews = filteredPanes.map((pane, index) => ({
95
- ctx: new NodesContext(),
96
- pane,
97
- index,
98
- }));
94
+ const newPreviews = filteredPanes.map((pane, index) => {
95
+ const previewCtx = new NodesContext();
96
+ previewCtx.isTemplate.set(true);
97
+ return {
98
+ ctx: previewCtx,
99
+ pane,
100
+ index,
101
+ };
102
+ });
99
103
 
100
104
  setPreviews(newPreviews);
101
105
  setCurrentPage(0);
@@ -194,7 +198,6 @@ const AddPaneReUsePanel = ({
194
198
  );
195
199
  }
196
200
  }
197
- storyFragmentNode.isChanged = true;
198
201
 
199
202
  ctx.addNode(templateData.paneNode);
200
203
  ctx.linkChildToParent(
@@ -252,7 +252,7 @@ const ConfigPanePanel = ({
252
252
  </div>
253
253
 
254
254
  {/* Design Library Tools (Right Aligned) */}
255
- <div className="ml-auto flex items-center gap-2 border-l border-gray-300 pl-2">
255
+ <div className="ml-auto flex items-center gap-2 border-l border-gray-300 px-2">
256
256
  {!isHtmlAstPane && !isSandboxMode && (
257
257
  <button
258
258
  title="Save Pane to Design Library"
@@ -280,7 +280,7 @@ const ConfigPanePanel = ({
280
280
  <ArrowPathRoundedSquareIcon className="h-4 w-4 text-white" />
281
281
  </button>
282
282
  )}
283
- {import.meta.env.DEV && (
283
+ {import.meta.env.DEV && !isHtmlAstPane && (
284
284
  <button
285
285
  title="Copy Pane Design to Clipboard"
286
286
  onClick={handleCopyToClipboard}
@@ -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
  };