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.
Files changed (55) 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/compositor/preview/PanesPreviewGenerator.tsx +1 -0
  17. package/templates/src/components/edit/PanelSwitch.tsx +3 -24
  18. package/templates/src/components/edit/SettingsPanel.tsx +0 -1
  19. package/templates/src/components/edit/ToolMode.tsx +6 -14
  20. package/templates/src/components/edit/pane/AddPanePanel.tsx +45 -25
  21. package/templates/src/components/edit/pane/AddPanePanel_new.tsx +2 -8
  22. package/templates/src/components/edit/pane/AddPanePanel_paste.tsx +111 -0
  23. package/templates/src/components/edit/pane/RestylePaneModal.tsx +6 -13
  24. package/templates/src/components/edit/pane/steps/AiDesignStep.tsx +0 -5
  25. package/templates/src/components/edit/pane/steps/DesignLibraryStep.tsx +4 -11
  26. package/templates/src/components/edit/panels/StyleBreakPanel.tsx +1 -3
  27. package/templates/src/components/edit/panels/StyleElementPanel_update.tsx +0 -6
  28. package/templates/src/components/edit/panels/StyleImagePanel_update.tsx +0 -3
  29. package/templates/src/components/edit/panels/StyleLiElementPanel_update.tsx +0 -4
  30. package/templates/src/components/edit/panels/StyleLinkPanel_config.tsx +8 -5
  31. package/templates/src/components/edit/panels/StyleLinkPanel_update.tsx +1 -2
  32. package/templates/src/components/edit/panels/StyleParentPanel.tsx +1 -3
  33. package/templates/src/components/edit/panels/StyleParentPanel_update.tsx +2 -5
  34. package/templates/src/components/edit/panels/StyleWidgetPanel_config.tsx +2 -8
  35. package/templates/src/components/edit/panels/StyleWidgetPanel_update.tsx +0 -4
  36. package/templates/src/components/edit/state/SaveToLibraryModal.tsx +27 -16
  37. package/templates/src/components/edit/storyfragment/StoryFragmentConfigPanel.tsx +9 -26
  38. package/templates/src/components/edit/storyfragment/StoryFragmentPanel_og.tsx +7 -16
  39. package/templates/src/components/edit/storyfragment/StoryFragmentPanel_slug.tsx +5 -6
  40. package/templates/src/components/edit/widgets/InteractiveDisclosureWidget.tsx +0 -5
  41. package/templates/src/components/fields/BackgroundImageWrapper.tsx +1 -7
  42. package/templates/src/components/fields/ColorPickerCombo.tsx +8 -12
  43. package/templates/src/components/fields/ViewportComboBox.tsx +4 -6
  44. package/templates/src/stores/nodes.ts +14 -6
  45. package/templates/src/stores/storykeep.ts +3 -3
  46. package/templates/src/types/compositorTypes.ts +2 -0
  47. package/templates/src/utils/compositor/TemplatePanes.ts +0 -76
  48. package/templates/src/utils/compositor/aiPaneParser.ts +3 -1
  49. package/templates/src/utils/compositor/designLibraryHelper.ts +240 -17
  50. package/templates/src/utils/helpers.ts +5 -4
  51. package/utils/inject-files.ts +6 -32
  52. package/templates/src/components/compositor/preview/VisualBreakPreview.tsx +0 -154
  53. package/templates/src/components/edit/pane/PageGen_preview.tsx +0 -511
  54. package/templates/src/utils/compositor/processMarkdown.ts +0 -445
  55. package/templates/src/utils/compositor/templateMarkdownStyles.ts +0 -1273
@@ -1,6 +1,6 @@
1
1
  import { useEffect, useState } from 'react';
2
2
  import * as htmlToImage from 'html-to-image';
3
- import type { BrandConfig } from '@/types/tractstack';
3
+ import { brandConfigStore } from '@/stores/storykeep';
4
4
 
5
5
  export type SnapshotData = {
6
6
  imageData: string;
@@ -12,7 +12,6 @@ export interface PaneSnapshotGeneratorProps {
12
12
  htmlString: string;
13
13
  onComplete: (id: string, data: SnapshotData) => void;
14
14
  onError?: (id: string, error: string) => void;
15
- config?: BrandConfig;
16
15
  outputWidth?: number;
17
16
  }
18
17
 
@@ -33,7 +32,6 @@ export const PaneSnapshotGenerator = ({
33
32
  htmlString,
34
33
  onComplete,
35
34
  onError,
36
- config,
37
35
  outputWidth = 800,
38
36
  }: PaneSnapshotGeneratorProps) => {
39
37
  const [isGenerating, setIsGenerating] = useState(false);
@@ -80,7 +78,8 @@ export const PaneSnapshotGenerator = ({
80
78
  const customCssUrl = `${cssBasePath}/custom.css`;
81
79
  const storykeepCssUrl = `${cssBasePath}/storykeep.css`;
82
80
 
83
- const brandColors = config?.BRAND_COLOURS?.split(',') || [];
81
+ const brandColors =
82
+ brandConfigStore.get()?.BRAND_COLOURS?.split(',') || [];
84
83
 
85
84
  // Get all existing CSS links from current document
86
85
  const existingCssLinks = Array.from(
@@ -100,7 +99,7 @@ export const PaneSnapshotGenerator = ({
100
99
  <link rel="stylesheet" href="${storykeepCssUrl}">
101
100
  ${existingCssLinks.map((href) => `<link rel="stylesheet" href="${href}">`).join('\n')}
102
101
  ${
103
- config
102
+ brandColors
104
103
  ? `
105
104
  <style>
106
105
  :root {
@@ -203,7 +202,7 @@ export const PaneSnapshotGenerator = ({
203
202
  };
204
203
 
205
204
  generateSnapshot();
206
- }, [id, htmlString, isGenerating, onComplete, onError, config, outputWidth]);
205
+ }, [id, htmlString, isGenerating, onComplete, onError, outputWidth]);
207
206
 
208
207
  // Show spinner while generating
209
208
  if (isGenerating) {
@@ -62,6 +62,7 @@ export const PanesPreviewGenerator = ({
62
62
  request.ctx,
63
63
  actualPaneId
64
64
  );
65
+ console.log(previewPayload);
65
66
 
66
67
  previewPayloads.push(previewPayload);
67
68
  requestMap.set(previewPayload.id, request.id);
@@ -36,7 +36,6 @@ import StyleLiElementUpdatePanel from './panels/StyleLiElementPanel_update';
36
36
  import StyleLiElementRemovePanel from './panels/StyleLiElementPanel_remove';
37
37
  import StyleCodeHookPanel from './panels/StyleCodeHookPanel';
38
38
  import { getSettingsPanelTitle } from '@/utils/helpers';
39
- import type { BrandConfig } from '@/types/tractstack';
40
39
  import type {
41
40
  FlatNode,
42
41
  GridLayoutNode,
@@ -44,13 +43,11 @@ import type {
44
43
  } from '@/types/compositorTypes';
45
44
 
46
45
  interface SettingsPanelProps {
47
- config: BrandConfig;
48
46
  availableCodeHooks: string[];
49
47
  onTitleChange?: (title: string) => void;
50
48
  }
51
49
 
52
50
  const PanelSwitch = ({
53
- config,
54
51
  availableCodeHooks,
55
52
  onTitleChange,
56
53
  }: SettingsPanelProps) => {
@@ -101,13 +98,7 @@ const PanelSwitch = ({
101
98
  switch (signal.action) {
102
99
  case 'style-break':
103
100
  if (clickedNode && paneNode)
104
- return (
105
- <StyleBreakPanel
106
- config={config}
107
- node={clickedNode}
108
- parentNode={paneNode}
109
- />
110
- );
101
+ return <StyleBreakPanel node={clickedNode} parentNode={paneNode} />;
111
102
  break;
112
103
 
113
104
  case 'style-parent':
@@ -117,7 +108,6 @@ const PanelSwitch = ({
117
108
  node={markdownNode}
118
109
  parentNode={paneNode}
119
110
  layer={signal.layer || 0}
120
- config={config}
121
111
  />
122
112
  );
123
113
  else if (gridLayoutNode && paneNode)
@@ -126,7 +116,6 @@ const PanelSwitch = ({
126
116
  node={gridLayoutNode}
127
117
  parentNode={paneNode}
128
118
  layer={signal.layer || 0}
129
- config={config}
130
119
  />
131
120
  );
132
121
  break;
@@ -181,7 +170,6 @@ const PanelSwitch = ({
181
170
  node={clickedNode}
182
171
  layer={signal.layer || 0}
183
172
  className={signal.className}
184
- config={config}
185
173
  targetProperty={(signal as any).targetProperty}
186
174
  />
187
175
  );
@@ -215,7 +203,6 @@ const PanelSwitch = ({
215
203
  <StyleLinkUpdatePanel
216
204
  node={clickedNode}
217
205
  className={signal.className}
218
- config={config}
219
206
  />
220
207
  );
221
208
  break;
@@ -232,8 +219,7 @@ const PanelSwitch = ({
232
219
  break;
233
220
 
234
221
  case 'style-link-config':
235
- if (clickedNode)
236
- return <StyleLinkConfigPanel node={clickedNode} config={config} />;
222
+ if (clickedNode) return <StyleLinkConfigPanel node={clickedNode} />;
237
223
  break;
238
224
 
239
225
  case 'style-element':
@@ -280,7 +266,6 @@ const PanelSwitch = ({
280
266
  parentNode={styleContextNode}
281
267
  className={signal.className}
282
268
  onTitleChange={onTitleChange}
283
- config={config}
284
269
  />
285
270
  );
286
271
  break;
@@ -353,7 +338,6 @@ const PanelSwitch = ({
353
338
  parentNode={styleContextNode}
354
339
  className={signal.className}
355
340
  childId={signal.childId}
356
- config={config}
357
341
  />
358
342
  );
359
343
  break;
@@ -429,8 +413,7 @@ const PanelSwitch = ({
429
413
  }
430
414
 
431
415
  case 'style-code-config':
432
- if (clickedNode)
433
- return <StyleWidgetConfigPanel node={clickedNode} config={config} />;
416
+ if (clickedNode) return <StyleWidgetConfigPanel node={clickedNode} />;
434
417
  break;
435
418
 
436
419
  case 'style-code-add':
@@ -453,7 +436,6 @@ const PanelSwitch = ({
453
436
  node={clickedNode}
454
437
  parentNode={styleContextNode}
455
438
  className={signal.className}
456
- config={config}
457
439
  />
458
440
  );
459
441
  break;
@@ -467,7 +449,6 @@ const PanelSwitch = ({
467
449
  parentNode={styleContextNode}
468
450
  className={signal.className}
469
451
  childId={signal.childId}
470
- config={config}
471
452
  />
472
453
  );
473
454
  break;
@@ -541,7 +522,6 @@ const PanelSwitch = ({
541
522
  node={clickedNode}
542
523
  parentNode={styleContextNode}
543
524
  className={signal.className}
544
- config={config}
545
525
  />
546
526
  );
547
527
  break;
@@ -554,7 +534,6 @@ const PanelSwitch = ({
554
534
  parentNode={styleContextNode}
555
535
  className={signal.className}
556
536
  childId={signal.childId}
557
- config={config}
558
537
  />
559
538
  );
560
539
  break;
@@ -66,7 +66,6 @@ const SettingsPanel = ({ config, availableCodeHooks }: SettingsPanelProps) => {
66
66
  <div className="min-h-0 flex-1 space-y-4 overflow-y-auto p-1.5 pt-0 md:p-2.5 md:pt-0">
67
67
  <div className="rounded bg-gray-50 p-1.5 md:p-2.5">
68
68
  <PanelSwitch
69
- config={config}
70
69
  availableCodeHooks={availableCodeHooks}
71
70
  onTitleChange={setPanelTitle}
72
71
  />
@@ -52,6 +52,12 @@ const storykeepToolModes = [
52
52
  title: 'Design Library',
53
53
  description: 'Save pane to design library',
54
54
  },
55
+ {
56
+ key: 'debug' as const,
57
+ Icon: BugAntIcon,
58
+ title: 'Debug Mode',
59
+ description: 'Toggle node tree id reveal',
60
+ },
55
61
  ] as const;
56
62
 
57
63
  interface StoryKeepToolModeProps {
@@ -62,7 +68,6 @@ const StoryKeepToolMode = ({ isContext }: StoryKeepToolModeProps) => {
62
68
  const ctx = getCtx();
63
69
  const { value: toolModeVal } = useStore(ctx.toolModeValStore);
64
70
  const $selection = useStore(selectionStore);
65
- const showGuids = useStore(ctx.showGuids);
66
71
  const navRef = useRef<HTMLElement>(null);
67
72
 
68
73
  const hasTitle = useStore(ctx.hasTitle);
@@ -73,8 +78,6 @@ const StoryKeepToolMode = ({ isContext }: StoryKeepToolModeProps) => {
73
78
  const className =
74
79
  'w-8 h-8 py-1 rounded-xl bg-white text-myblue hover:bg-mygreen/20 hover:text-black hover:rotate-3 cursor-pointer transition-all';
75
80
  const classNameActive = 'w-8 h-8 py-1.5 rounded-md bg-myblue text-white';
76
- const classNameDebugActive =
77
- 'w-8 h-8 py-1.5 rounded-md bg-orange-500 text-white';
78
81
 
79
82
  const currentToolMode =
80
83
  storykeepToolModes.find((mode) => mode.key === toolModeVal) ??
@@ -86,11 +89,6 @@ const StoryKeepToolMode = ({ isContext }: StoryKeepToolModeProps) => {
86
89
  ctx.notifyNode('root');
87
90
  };
88
91
 
89
- const handleDebugToggle = () => {
90
- ctx.showGuids.set(!showGuids);
91
- ctx.notifyNode('root');
92
- };
93
-
94
92
  const handleStyleClick = () => {
95
93
  selectionStore.setKey('pendingAction', 'style');
96
94
  };
@@ -156,12 +154,6 @@ const StoryKeepToolMode = ({ isContext }: StoryKeepToolModeProps) => {
156
154
  )}
157
155
  </div>
158
156
  ))}
159
- <div title="Toggle debug node ids" key="debug">
160
- <BugAntIcon
161
- className={showGuids ? classNameDebugActive : className}
162
- onClick={handleDebugToggle}
163
- />
164
- </div>
165
157
  </div>
166
158
  )}
167
159
 
@@ -5,9 +5,9 @@ import AddPaneNewPanel from './AddPanePanel_new';
5
5
  import AddPaneBreakPanel from './AddPanePanel_break';
6
6
  import AddPaneReUsePanel from './AddPanePanel_reuse';
7
7
  import AddPaneCodeHookPanel from './AddPanePanel_codehook';
8
+ import AddPanePanel_paste from './AddPanePanel_paste';
8
9
  import { NodesContext, ROOT_NODE_NAME, getCtx } from '@/stores/nodes';
9
10
  import { PaneAddMode } from '@/types/compositorTypes';
10
- import type { BrandConfig } from '@/types/tractstack';
11
11
 
12
12
  interface AddPanePanelProps {
13
13
  nodeId: string;
@@ -15,7 +15,6 @@ interface AddPanePanelProps {
15
15
  ctx?: NodesContext;
16
16
  isStoryFragment?: boolean;
17
17
  isContextPane?: boolean;
18
- config?: BrandConfig;
19
18
  isSandboxMode?: boolean;
20
19
  }
21
20
 
@@ -25,16 +24,14 @@ const AddPanePanel = ({
25
24
  ctx,
26
25
  isStoryFragment = false,
27
26
  isContextPane = false,
28
- config,
29
27
  isSandboxMode = false,
30
28
  }: AddPanePanelProps) => {
31
29
  const [reset, setReset] = useState(false);
32
- const lookup = first ? `${nodeId}-0` : nodeId;
30
+ const [isExpanded, setIsExpanded] = useState(false);
33
31
 
34
- // Always get a valid context, either from props or the global getter
32
+ const lookup = first ? `${nodeId}-0` : nodeId;
35
33
  const nodesCtx = ctx || getCtx();
36
34
 
37
- // Use the guaranteed context to subscribe to stores
38
35
  const activePaneMode = useStore(nodesCtx.activePaneMode);
39
36
  const hasPanes = useStore(nodesCtx.hasPanes);
40
37
  const isTemplate = useStore(nodesCtx.isTemplate);
@@ -51,12 +48,14 @@ const AddPanePanel = ({
51
48
 
52
49
  const setMode = (newMode: PaneAddMode, reset?: boolean) => {
53
50
  setReset(true);
54
- nodesCtx.setPanelMode(lookup, 'add', newMode); // No longer needs optional chaining
51
+ nodesCtx.setPanelMode(lookup, 'add', newMode);
52
+ if (newMode === PaneAddMode.DEFAULT) {
53
+ setIsExpanded(false);
54
+ }
55
55
  if (reset) nodesCtx.notifyNode(ROOT_NODE_NAME);
56
56
  settingsPanelStore.set(null);
57
57
  };
58
58
 
59
- // Always render a stable container div for the intersection observer
60
59
  return (
61
60
  <div className="add-pane-panel-wrapper border-mydarkgrey border-b-2 border-t-2 border-dotted">
62
61
  {mode === PaneAddMode.NEW || (!hasPanes && first && !reset) ? (
@@ -67,7 +66,6 @@ const AddPanePanel = ({
67
66
  ctx={nodesCtx}
68
67
  isStoryFragment={isStoryFragment}
69
68
  isContextPane={isContextPane}
70
- config={config!}
71
69
  isSandboxMode={isSandboxMode}
72
70
  />
73
71
  ) : mode === PaneAddMode.BREAK && !isContextPane ? (
@@ -79,47 +77,60 @@ const AddPanePanel = ({
79
77
  isStoryFragment={isStoryFragment}
80
78
  />
81
79
  ) : mode === PaneAddMode.REUSE && !isContextPane ? (
82
- <AddPaneReUsePanel
80
+ <AddPaneReUsePanel nodeId={nodeId} first={first} setMode={setMode} />
81
+ ) : mode === PaneAddMode.CODEHOOK ? (
82
+ <AddPaneCodeHookPanel
83
83
  nodeId={nodeId}
84
84
  first={first}
85
85
  setMode={setMode}
86
- //ctx={nodesCtx}
86
+ isStoryFragment={isStoryFragment}
87
+ isContextPane={isContextPane}
87
88
  />
88
- ) : mode === PaneAddMode.CODEHOOK ? (
89
- <AddPaneCodeHookPanel
89
+ ) : mode === PaneAddMode.PASTE ? (
90
+ <AddPanePanel_paste
90
91
  nodeId={nodeId}
91
92
  first={first}
92
93
  setMode={setMode}
94
+ ctx={nodesCtx}
93
95
  isStoryFragment={isStoryFragment}
94
96
  isContextPane={isContextPane}
95
97
  />
96
- ) : (
98
+ ) : isExpanded ? (
97
99
  <div className="border-mylightgrey border-t border-dotted">
98
- <div className="group flex w-full gap-1 px-1.5 pb-0.5 pt-1.5">
99
- <div className="rounded-md bg-gray-200 px-2 py-1 text-sm text-gray-800">
100
- Insert Pane Here
101
- </div>
102
- <div className={`flex gap-1 transition-opacity`}>
100
+ <div className="group flex w-full flex-wrap items-center gap-2 px-1.5 pb-0.5 pt-1.5">
101
+ <button
102
+ onClick={() => setIsExpanded(false)}
103
+ className="rounded-md bg-gray-200 px-2 py-1 text-sm font-bold text-gray-800"
104
+ >
105
+ &lt; Cancel
106
+ </button>
107
+ <div className={`flex flex-wrap gap-1 transition-opacity`}>
103
108
  <button
104
109
  onClick={() => setMode(PaneAddMode.NEW)}
105
- className="rounded bg-white px-2 py-1 text-sm text-cyan-700 shadow-sm transition-colors hover:bg-cyan-700 hover:text-white focus:bg-cyan-700 focus:text-white"
110
+ className="rounded bg-white px-2 py-1 text-sm text-cyan-700 shadow-sm transition-colors hover:bg-cyan-700 hover:text-white"
106
111
  >
107
112
  + Design New
108
113
  </button>
114
+ <button
115
+ onClick={() => setMode(PaneAddMode.PASTE)}
116
+ className="rounded bg-white px-2 py-1 text-sm text-cyan-700 shadow-sm transition-colors hover:bg-cyan-700 hover:text-white"
117
+ >
118
+ + Paste Pane
119
+ </button>
109
120
  {!isContextPane && (
110
121
  <>
111
122
  <button
112
123
  onClick={() => setMode(PaneAddMode.BREAK)}
113
- className="rounded bg-white px-2 py-1 text-sm text-cyan-700 shadow-sm transition-colors hover:bg-cyan-700 hover:text-white focus:bg-cyan-700 focus:text-white"
124
+ className="rounded bg-white px-2 py-1 text-sm text-cyan-700 shadow-sm transition-colors hover:bg-cyan-700 hover:text-white"
114
125
  >
115
126
  + Visual Break
116
127
  </button>
117
128
  {!isTemplate && (
118
129
  <button
119
130
  onClick={() => setMode(PaneAddMode.REUSE)}
120
- className="rounded bg-white px-2 py-1 text-sm text-cyan-700 shadow-sm transition-colors hover:bg-cyan-700 hover:text-white focus:bg-cyan-700 focus:text-white"
131
+ className="rounded bg-white px-2 py-1 text-sm text-cyan-700 shadow-sm transition-colors hover:bg-cyan-700 hover:text-white"
121
132
  >
122
- + Re-use existing pane
133
+ + Re-use Pane
123
134
  </button>
124
135
  )}
125
136
  </>
@@ -127,14 +138,23 @@ const AddPanePanel = ({
127
138
  {!isTemplate && (
128
139
  <button
129
140
  onClick={() => setMode(PaneAddMode.CODEHOOK)}
130
- className="rounded bg-white px-2 py-1 text-sm text-cyan-700 shadow-sm transition-colors hover:bg-cyan-700 hover:text-white focus:bg-cyan-700 focus:text-white"
141
+ className="rounded bg-white px-2 py-1 text-sm text-cyan-700 shadow-sm transition-colors hover:bg-cyan-700 hover:text-white"
131
142
  >
132
- + Custom Code Hook
143
+ + Code Hook
133
144
  </button>
134
145
  )}
135
146
  </div>
136
147
  </div>
137
148
  </div>
149
+ ) : (
150
+ <div className="border-mylightgrey border-t border-dotted p-2">
151
+ <button
152
+ onClick={() => setIsExpanded(true)}
153
+ className="text-sm text-gray-500 underline decoration-dotted underline-offset-4 transition-colors hover:text-cyan-700"
154
+ >
155
+ Insert Pane Here
156
+ </button>
157
+ </div>
138
158
  )}
139
159
  </div>
140
160
  );
@@ -9,7 +9,7 @@ import { NodesContext, getCtx } from '@/stores/nodes';
9
9
  import { cloneDeep } from '@/utils/helpers';
10
10
  import { hasAssemblyAIStore } from '@/stores/storykeep';
11
11
  import prompts from '@/constants/prompts.json';
12
- import type { BrandConfig, DesignLibraryEntry } from '@/types/tractstack';
12
+ import type { DesignLibraryEntry } from '@/types/tractstack';
13
13
  import { PaneAddMode, type TemplatePane } from '@/types/compositorTypes';
14
14
  import { useStore } from '@nanostores/react';
15
15
  import { CopyInputStep } from './steps/CopyInputStep';
@@ -120,7 +120,6 @@ interface AddPaneNewPanelProps {
120
120
  ctx?: NodesContext;
121
121
  isStoryFragment?: boolean;
122
122
  isContextPane?: boolean;
123
- config?: BrandConfig;
124
123
  isSandboxMode?: boolean;
125
124
  }
126
125
 
@@ -131,7 +130,6 @@ const AddPaneNewPanel = ({
131
130
  ctx: providedCtx,
132
131
  isStoryFragment = false,
133
132
  isContextPane = false,
134
- config,
135
133
  isSandboxMode = false,
136
134
  }: AddPaneNewPanelProps) => {
137
135
  const ctx = providedCtx || getCtx();
@@ -672,17 +670,13 @@ const AddPaneNewPanel = ({
672
670
  ← Back to Choice
673
671
  </button>
674
672
  </div>
675
- <DesignLibraryStep
676
- config={config!}
677
- onSelect={handleDesignLibrarySelect}
678
- />
673
+ <DesignLibraryStep onSelect={handleDesignLibrarySelect} />
679
674
  </div>
680
675
  );
681
676
 
682
677
  const renderAiDesignStep = () => (
683
678
  <div className="space-y-4 p-4">
684
679
  <AiDesignStep
685
- config={config!}
686
680
  designConfig={aiDesignConfig}
687
681
  onDesignConfigChange={setAiDesignConfig}
688
682
  />
@@ -0,0 +1,111 @@
1
+ import { useState } from 'react';
2
+ import { getCtx, type NodesContext } from '@/stores/nodes';
3
+ import { PaneAddMode, type StoragePane } from '@/types/compositorTypes';
4
+ import {
5
+ remapPaneIds,
6
+ convertStorageToLiveTemplate,
7
+ } from '@/utils/compositor/designLibraryHelper';
8
+
9
+ interface AddPanePanelPasteProps {
10
+ nodeId: string;
11
+ first: boolean;
12
+ setMode: (mode: PaneAddMode, reset?: boolean) => void;
13
+ ctx?: NodesContext;
14
+ isStoryFragment?: boolean;
15
+ isContextPane?: boolean;
16
+ }
17
+
18
+ const AddPanePanel_paste = ({
19
+ nodeId,
20
+ first,
21
+ setMode,
22
+ ctx: providedCtx,
23
+ isStoryFragment,
24
+ isContextPane,
25
+ }: AddPanePanelPasteProps) => {
26
+ const [jsonInput, setJsonInput] = useState('');
27
+ const [error, setError] = useState<string | null>(null);
28
+ const ctx = providedCtx || getCtx();
29
+
30
+ const handleCreate = () => {
31
+ setError(null);
32
+ if (!jsonInput.trim()) {
33
+ setError('Paste content cannot be empty.');
34
+ return;
35
+ }
36
+
37
+ try {
38
+ const parsedPane = JSON.parse(jsonInput) as StoragePane;
39
+ if (parsedPane.nodeType !== 'Pane') {
40
+ throw new Error('Pasted content is not a valid Pane object.');
41
+ }
42
+
43
+ const remappedPane = remapPaneIds(parsedPane);
44
+ const liveTemplate = convertStorageToLiveTemplate(remappedPane);
45
+
46
+ const ownerId =
47
+ isStoryFragment || isContextPane
48
+ ? nodeId
49
+ : ctx.getClosestNodeTypeFromId(nodeId, 'StoryFragment');
50
+
51
+ ctx.addTemplatePane(
52
+ ownerId,
53
+ liveTemplate,
54
+ nodeId,
55
+ first ? 'before' : 'after'
56
+ );
57
+ ctx.notifyNode('root');
58
+ setMode(PaneAddMode.DEFAULT, true);
59
+ } catch (err) {
60
+ const message =
61
+ err instanceof Error
62
+ ? err.message
63
+ : 'An unknown error occurred during parsing.';
64
+ setError(`Invalid Pane JSON: ${message}`);
65
+ }
66
+ };
67
+
68
+ return (
69
+ <div className="p-2 shadow-inner">
70
+ <div className="rounded-md border bg-gray-50 p-4">
71
+ <div className="flex items-center justify-between pb-4">
72
+ <div className="flex items-center gap-2">
73
+ <button
74
+ onClick={() => setMode(PaneAddMode.DEFAULT)}
75
+ className="w-fit rounded bg-gray-200 px-3 py-1 text-sm text-gray-800 transition-colors hover:bg-gray-300"
76
+ >
77
+ ← Go Back
78
+ </button>
79
+ <h3 className="font-action text-sm font-bold text-cyan-700">
80
+ Paste Pane
81
+ </h3>
82
+ </div>
83
+ </div>
84
+ <div className="space-y-4">
85
+ <p className="text-sm text-gray-600">
86
+ Paste the JSON content of a copied pane into the text area below.
87
+ </p>
88
+ <textarea
89
+ value={jsonInput}
90
+ onChange={(e) => setJsonInput(e.target.value)}
91
+ placeholder="Paste pane JSON here..."
92
+ className="h-48 w-full rounded-md border border-gray-300 bg-white p-2 font-mono text-xs focus:border-cyan-500 focus:ring-cyan-500"
93
+ spellCheck={false}
94
+ />
95
+ {error && <p className="text-sm text-red-600">{error}</p>}
96
+ <div className="flex justify-end">
97
+ <button
98
+ onClick={handleCreate}
99
+ disabled={!jsonInput.trim()}
100
+ className="rounded-md bg-cyan-600 px-4 py-2 text-sm font-bold text-white shadow-sm hover:bg-cyan-700 disabled:cursor-not-allowed disabled:bg-gray-400"
101
+ >
102
+ Create Pane from Paste
103
+ </button>
104
+ </div>
105
+ </div>
106
+ </div>
107
+ </div>
108
+ );
109
+ };
110
+
111
+ export default AddPanePanel_paste;
@@ -15,6 +15,7 @@ import XMarkIcon from '@heroicons/react/24/outline/XMarkIcon';
15
15
  import { selectionStore } from '@/stores/selection';
16
16
  import { getCtx, NodesContext } from '@/stores/nodes';
17
17
  import { createEmptyStorykeep } from '@/utils/compositor/nodesHelper';
18
+ import { brandConfigStore } from '@/stores/storykeep';
18
19
  import {
19
20
  extractPaneCopy,
20
21
  mergeCopyIntoTemplate,
@@ -24,10 +25,10 @@ import type {
24
25
  PaneNode,
25
26
  StoragePane,
26
27
  TemplatePane,
27
- TemplateMarkdown, // Added import
28
- BaseNode, // Added import
28
+ TemplateMarkdown,
29
+ BaseNode,
29
30
  } from '@/types/compositorTypes';
30
- import type { BrandConfig, DesignLibraryEntry } from '@/types/tractstack';
31
+ import type { DesignLibraryEntry } from '@/types/tractstack';
31
32
  import {
32
33
  PaneSnapshotGenerator,
33
34
  type SnapshotData,
@@ -44,13 +45,11 @@ const VERBOSE = false;
44
45
 
45
46
  interface TemplatePreviewItemProps {
46
47
  template: TemplatePane;
47
- config: BrandConfig;
48
48
  onClick: () => void;
49
49
  }
50
50
 
51
51
  const TemplatePreviewItem = ({
52
52
  template,
53
- config,
54
53
  onClick,
55
54
  }: TemplatePreviewItemProps) => {
56
55
  const [previewState, setPreviewState] = useState<{
@@ -111,7 +110,6 @@ const TemplatePreviewItem = ({
111
110
  id={template.id}
112
111
  htmlString={previewState.htmlFragment}
113
112
  outputWidth={800}
114
- config={config}
115
113
  onComplete={(_id, data) => handleSnapshotComplete(data)}
116
114
  onError={(_id, err) =>
117
115
  setPreviewState((prev) =>
@@ -141,16 +139,12 @@ const TemplatePreviewItem = ({
141
139
  );
142
140
  };
143
141
 
144
- interface RestylePaneModalProps {
145
- config: BrandConfig;
146
- }
147
-
148
- export const RestylePaneModal = ({ config }: RestylePaneModalProps) => {
142
+ export const RestylePaneModal = () => {
149
143
  const ctx = getCtx();
150
144
  const { isRestyleModalOpen, paneToRestyleId } = useStore(selectionStore, {
151
145
  keys: ['isRestyleModalOpen', 'paneToRestyleId'],
152
146
  });
153
- const designLibrary = config?.DESIGN_LIBRARY || [];
147
+ const designLibrary = brandConfigStore.get()?.DESIGN_LIBRARY || [];
154
148
 
155
149
  const [selectedCategory, setSelectedCategory] = useState<string>('all');
156
150
  const [searchTerm, setSearchTerm] = useState('');
@@ -502,7 +496,6 @@ export const RestylePaneModal = ({ config }: RestylePaneModalProps) => {
502
496
  <TemplatePreviewItem
503
497
  key={template.id}
504
498
  template={template}
505
- config={config}
506
499
  onClick={() => handleSelectTemplate(template)}
507
500
  />
508
501
  ))}
@@ -1,5 +1,4 @@
1
1
  import ColorPickerCombo from '@/components/fields/ColorPickerCombo';
2
- import type { BrandConfig } from '@/types/tractstack';
3
2
 
4
3
  export interface AiDesignConfig {
5
4
  harmony: string;
@@ -10,7 +9,6 @@ export interface AiDesignConfig {
10
9
  }
11
10
 
12
11
  interface AiDesignStepProps {
13
- config: BrandConfig;
14
12
  designConfig: AiDesignConfig;
15
13
  onDesignConfigChange: (newConfig: AiDesignConfig) => void;
16
14
  }
@@ -24,7 +22,6 @@ const harmonyOptions = [
24
22
  const themeOptions = ['Light', 'Dark', 'Bright', 'Muted', 'Pastel', 'Earthy'];
25
23
 
26
24
  export const AiDesignStep = ({
27
- config,
28
25
  designConfig,
29
26
  onDesignConfigChange,
30
27
  }: AiDesignStepProps) => {
@@ -71,7 +68,6 @@ export const AiDesignStep = ({
71
68
  <div>
72
69
  <ColorPickerCombo
73
70
  title="Base Color (Optional)"
74
- config={config}
75
71
  defaultColor={designConfig.baseColor}
76
72
  onColorChange={(color) => updateField('baseColor', color)}
77
73
  allowNull={true}
@@ -80,7 +76,6 @@ export const AiDesignStep = ({
80
76
  <div>
81
77
  <ColorPickerCombo
82
78
  title="Accent Color (Optional)"
83
- config={config}
84
79
  defaultColor={designConfig.accentColor}
85
80
  onColorChange={(color) => updateField('accentColor', color)}
86
81
  allowNull={true}