astro-tractstack 2.0.18 → 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 (58) 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 +277 -70
  22. package/templates/src/components/edit/pane/AddPanePanel_paste.tsx +111 -0
  23. package/templates/src/components/edit/pane/RestylePaneModal.tsx +7 -14
  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.tsx +0 -1
  29. package/templates/src/components/edit/panels/StyleImagePanel_update.tsx +0 -3
  30. package/templates/src/components/edit/panels/StyleLiElementPanel_update.tsx +0 -4
  31. package/templates/src/components/edit/panels/StyleLinkPanel_config.tsx +8 -5
  32. package/templates/src/components/edit/panels/StyleLinkPanel_update.tsx +1 -2
  33. package/templates/src/components/edit/panels/StyleParentPanel.tsx +1 -3
  34. package/templates/src/components/edit/panels/StyleParentPanel_update.tsx +2 -5
  35. package/templates/src/components/edit/panels/StyleWidgetPanel_config.tsx +2 -8
  36. package/templates/src/components/edit/panels/StyleWidgetPanel_update.tsx +0 -4
  37. package/templates/src/components/edit/state/SaveToLibraryModal.tsx +27 -16
  38. package/templates/src/components/edit/storyfragment/StoryFragmentConfigPanel.tsx +9 -26
  39. package/templates/src/components/edit/storyfragment/StoryFragmentPanel_og.tsx +7 -16
  40. package/templates/src/components/edit/storyfragment/StoryFragmentPanel_slug.tsx +5 -6
  41. package/templates/src/components/edit/widgets/InteractiveDisclosureWidget.tsx +0 -5
  42. package/templates/src/components/fields/BackgroundImageWrapper.tsx +1 -7
  43. package/templates/src/components/fields/ColorPickerCombo.tsx +8 -12
  44. package/templates/src/components/fields/ViewportComboBox.tsx +4 -6
  45. package/templates/src/constants/prompts.json +22 -1
  46. package/templates/src/stores/nodes.ts +297 -222
  47. package/templates/src/stores/storykeep.ts +3 -3
  48. package/templates/src/types/compositorTypes.ts +21 -1
  49. package/templates/src/types/tractstack.ts +1 -0
  50. package/templates/src/utils/compositor/TemplatePanes.ts +0 -76
  51. package/templates/src/utils/compositor/aiPaneParser.ts +265 -83
  52. package/templates/src/utils/compositor/designLibraryHelper.ts +252 -26
  53. package/templates/src/utils/helpers.ts +5 -4
  54. package/utils/inject-files.ts +6 -32
  55. package/templates/src/components/compositor/preview/VisualBreakPreview.tsx +0 -154
  56. package/templates/src/components/edit/pane/PageGen_preview.tsx +0 -511
  57. package/templates/src/utils/compositor/processMarkdown.ts +0 -445
  58. package/templates/src/utils/compositor/templateMarkdownStyles.ts +0 -1273
package/dist/index.js CHANGED
@@ -452,6 +452,12 @@ async function w(t, e, c) {
452
452
  src: t("../templates/src/components/edit/pane/AddPanePanel.tsx"),
453
453
  dest: "src/components/edit/pane/AddPanePanel.tsx"
454
454
  },
455
+ {
456
+ src: t(
457
+ "../templates/src/components/edit/pane/AddPanePanel_paste.tsx"
458
+ ),
459
+ dest: "src/components/edit/pane/AddPanePanel_paste.tsx"
460
+ },
455
461
  {
456
462
  src: t(
457
463
  "../templates/src/components/edit/pane/AddPanePanel_break.tsx"
@@ -548,22 +554,6 @@ async function w(t, e, c) {
548
554
  ),
549
555
  dest: "src/components/edit/context/ContextPaneConfig_slug.tsx"
550
556
  },
551
- {
552
- src: t("../templates/src/components/edit/pane/PageGenSelector.tsx"),
553
- dest: "src/components/edit/pane/PageGenSelector.tsx"
554
- },
555
- {
556
- src: t("../templates/src/components/edit/pane/PageGenSpecial.tsx"),
557
- dest: "src/components/edit/pane/PageGenSpecial.tsx"
558
- },
559
- {
560
- src: t("../templates/src/components/edit/pane/PageGen.tsx"),
561
- dest: "src/components/edit/pane/PageGen.tsx"
562
- },
563
- {
564
- src: t("../templates/src/components/edit/pane/PageGen_preview.tsx"),
565
- dest: "src/components/edit/pane/PageGen_preview.tsx"
566
- },
567
557
  // Compositor previews
568
558
  {
569
559
  src: t(
@@ -583,12 +573,6 @@ async function w(t, e, c) {
583
573
  ),
584
574
  dest: "src/components/compositor/preview/OgImagePreview.tsx"
585
575
  },
586
- {
587
- src: t(
588
- "../templates/src/components/compositor/preview/VisualBreakPreview.tsx"
589
- ),
590
- dest: "src/components/compositor/preview/VisualBreakPreview.tsx"
591
- },
592
576
  {
593
577
  src: t(
594
578
  "../templates/src/components/compositor/preview/ListContentPreview.tsx"
@@ -649,16 +633,6 @@ async function w(t, e, c) {
649
633
  src: t("../templates/src/utils/compositor/aiPaneParser.ts"),
650
634
  dest: "src/utils/compositor/aiPaneParser.ts"
651
635
  },
652
- {
653
- src: t("../templates/src/utils/compositor/processMarkdown.ts"),
654
- dest: "src/utils/compositor/processMarkdown.ts"
655
- },
656
- {
657
- src: t(
658
- "../templates/src/utils/compositor/templateMarkdownStyles.ts"
659
- ),
660
- dest: "src/utils/compositor/templateMarkdownStyles.ts"
661
- },
662
636
  {
663
637
  src: t(
664
638
  "../templates/src/utils/compositor/nodesMarkdownGenerator.ts"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro-tractstack",
3
- "version": "2.0.18",
3
+ "version": "2.0.20",
4
4
  "description": "Astro integration for TractStack - redeeming the web from boring experiences",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -11,7 +11,6 @@ import { canonicalURLStore } from '@/stores/storykeep';
11
11
  import { cloneDeep } from '@/utils/helpers';
12
12
  import ColorPickerCombo from '@/components/fields/ColorPickerCombo';
13
13
  import ActionBuilderSlugSelector from '@/components/form/ActionBuilderSlugSelector';
14
- import type { BrandConfig } from '@/types/tractstack';
15
14
  import type {
16
15
  PaneNode,
17
16
  VideoMoment,
@@ -21,7 +20,6 @@ import type {
21
20
  interface BunnyVideoSetupProps {
22
21
  nodeId: string;
23
22
  params: any;
24
- config: BrandConfig;
25
23
  }
26
24
 
27
25
  interface Chapter extends VideoMoment {
@@ -40,7 +38,7 @@ const generateId = (): string => {
40
38
  return Math.random().toString(36).substring(2, 9);
41
39
  };
42
40
 
43
- const BunnyVideoSetup = ({ nodeId, params, config }: BunnyVideoSetupProps) => {
41
+ const BunnyVideoSetup = ({ nodeId, params }: BunnyVideoSetupProps) => {
44
42
  const ctx = getCtx();
45
43
  const allNodes = ctx.allNodes.get();
46
44
  const canonicalURL = canonicalURLStore.get();
@@ -419,7 +417,6 @@ const BunnyVideoSetup = ({ nodeId, params, config }: BunnyVideoSetupProps) => {
419
417
  setBgColor(color);
420
418
  setTimeout(() => saveChanges(), 100);
421
419
  }}
422
- config={config!}
423
420
  allowNull={true}
424
421
  />
425
422
  <p className="mt-1 text-xs text-gray-500">
@@ -9,12 +9,10 @@ import { getCtx } from '@/stores/nodes';
9
9
  import { cloneDeep } from '@/utils/helpers';
10
10
  import ColorPickerCombo from '@/components/fields/ColorPickerCombo';
11
11
  import type { PaneNode } from '@/types/compositorTypes';
12
- import type { BrandConfig } from '@/types/tractstack';
13
12
 
14
13
  interface FeaturedArticleSetupProps {
15
14
  params: Record<string, string>;
16
15
  nodeId: string;
17
- config: BrandConfig;
18
16
  }
19
17
 
20
18
  const comboboxItemStyles = `
@@ -29,7 +27,6 @@ const comboboxItemStyles = `
29
27
  const FeaturedArticleSetup = ({
30
28
  params,
31
29
  nodeId,
32
- config,
33
30
  }: FeaturedArticleSetupProps) => {
34
31
  const $contentMap = useStore(fullContentMapStore);
35
32
  const $viewportKey = useStore(viewportKeyStore);
@@ -294,7 +291,6 @@ const FeaturedArticleSetup = ({
294
291
  title="Background Color"
295
292
  defaultColor={bgColor}
296
293
  onColorChange={(color: string) => setBgColor(color)}
297
- config={config!}
298
294
  allowNull={true}
299
295
  />
300
296
  <p className="mt-1 text-xs text-gray-500">
@@ -4,7 +4,6 @@ import { fullContentMapStore } from '@/stores/storykeep';
4
4
  import { classNames, cloneDeep } from '@/utils/helpers';
5
5
  import { getCtx } from '@/stores/nodes';
6
6
  import ColorPickerCombo from '@/components/fields/ColorPickerCombo';
7
- import type { BrandConfig } from '@/types/tractstack';
8
7
  import type { PaneNode } from '@/types/compositorTypes';
9
8
 
10
9
  const PER_PAGE = 20;
@@ -12,14 +11,9 @@ const PER_PAGE = 20;
12
11
  interface ListContentSetupProps {
13
12
  params?: Record<string, string>;
14
13
  nodeId: string;
15
- config: BrandConfig;
16
14
  }
17
15
 
18
- const ListContentSetup = ({
19
- params,
20
- nodeId,
21
- config,
22
- }: ListContentSetupProps) => {
16
+ const ListContentSetup = ({ params, nodeId }: ListContentSetupProps) => {
23
17
  const $contentMap = useStore(fullContentMapStore);
24
18
  const [isPanelOpen, setIsPanelOpen] = useState(false);
25
19
  const [excludedIds, setExcludedIds] = useState<string[]>(
@@ -327,7 +321,6 @@ const ListContentSetup = ({
327
321
  title="Background Color"
328
322
  defaultColor={bgColor}
329
323
  onColorChange={(color: string) => setBgColor(color)}
330
- config={config!}
331
324
  allowNull={true}
332
325
  />
333
326
  <p className="mt-1 text-xs text-gray-500">
@@ -6,12 +6,10 @@ import { useStore } from '@nanostores/react';
6
6
  import { fullContentMapStore } from '@/stores/storykeep';
7
7
  import { getCtx } from '@/stores/nodes';
8
8
  import type { PaneNode } from '@/types/compositorTypes';
9
- import type { BrandConfig } from '@/types/tractstack';
10
9
 
11
10
  interface ProductCardSetupProps {
12
11
  nodeId: string;
13
12
  params: Record<string, any> | null;
14
- config: BrandConfig;
15
13
  }
16
14
 
17
15
  export const ProductCardSetup = (props: ProductCardSetupProps) => {
@@ -11,12 +11,10 @@ import { fullContentMapStore } from '@/stores/storykeep';
11
11
  import { getCtx } from '@/stores/nodes';
12
12
  import CheckCircleIcon from '@heroicons/react/20/solid/CheckCircleIcon';
13
13
  import type { PaneNode } from '@/types/compositorTypes';
14
- import type { BrandConfig } from '@/types/tractstack';
15
14
 
16
15
  interface ProductGridSetupProps {
17
16
  nodeId: string;
18
17
  params: Record<string, any> | null;
19
- config: BrandConfig;
20
18
  }
21
19
 
22
20
  const modes = [
@@ -13,9 +13,9 @@ import {
13
13
  canonicalURLStore,
14
14
  preferredThemeStore,
15
15
  codehookMapStore,
16
- brandColourStore,
17
16
  hasArtpacksStore,
18
17
  settingsPanelStore,
18
+ brandConfigStore,
19
19
  } from '@/stores/storykeep';
20
20
  import { getCtx, ROOT_NODE_NAME, type NodesContext } from '@/stores/nodes';
21
21
  import { stopLoadingAnimation } from '@/utils/helpers';
@@ -290,17 +290,14 @@ export const Compositor = (props: CompositorProps) => {
290
290
  urlParamsStore.set(props.urlParams);
291
291
  canonicalURLStore.set(props.fullCanonicalURL);
292
292
  preferredThemeStore.set(props.config.THEME as Theme);
293
- if (props.config.BRAND_COLOURS)
294
- brandColourStore.set(props.config.BRAND_COLOURS);
295
293
  codehookMapStore.set(props.availableCodeHooks);
294
+ brandConfigStore.set(props.config);
296
295
  }, [
297
296
  props.fullContentMap,
298
- props.config.HAS_AAI,
299
- props.config.THEME,
300
- props.config.BRAND_COLOURS,
301
297
  props.urlParams,
302
298
  props.fullCanonicalURL,
303
299
  props.availableCodeHooks,
300
+ props.config,
304
301
  ]);
305
302
 
306
303
  // Initialize nodes tree and set up subscriptions
@@ -96,21 +96,18 @@ function parseCodeHook(node: BaseNode | FlatNode) {
96
96
  return null;
97
97
  }
98
98
 
99
- // Helper component to safely set the panel mode for an empty page
100
99
  const EmptyPageHandler = (props: NodeProps) => {
101
100
  const ctx = getCtx(props);
102
101
  useEffect(() => {
103
102
  ctx.setPaneAddMode(props.nodeId, PaneAddMode.NEW);
104
103
  }, []);
105
104
 
106
- // Now that the mode is set, render the panel which will read it.
107
105
  return (
108
106
  <AddPanePanel
109
107
  nodeId={props.nodeId}
110
108
  first={true}
111
109
  ctx={ctx}
112
110
  isStoryFragment={true}
113
- config={props.config!}
114
111
  isSandboxMode={props.isSandboxMode}
115
112
  />
116
113
  );
@@ -129,10 +126,10 @@ const getElement = (
129
126
  isSandboxMode: props.isSandboxMode,
130
127
  };
131
128
  const type = getType(node);
129
+ const toolModeVal = getCtx(props).toolModeValStore.get().value;
132
130
 
133
131
  switch (type) {
134
132
  case 'Markdown': {
135
- const toolModeVal = getCtx(props).toolModeValStore.get().value;
136
133
  if (toolModeVal === 'eraser') {
137
134
  const parentNode = node.parentId
138
135
  ? getCtx(props).allNodes.get().get(node.parentId)
@@ -200,10 +197,7 @@ const getElement = (
200
197
  panelType="storyfragment"
201
198
  ctx={getCtx(props)}
202
199
  >
203
- <StoryFragmentConfigPanel
204
- nodeId={props.nodeId}
205
- config={props.config!}
206
- />
200
+ <StoryFragmentConfigPanel nodeId={props.nodeId} />
207
201
  </PanelVisibilityWrapper>
208
202
  <StoryFragment {...sharedProps} />
209
203
  </>
@@ -213,12 +207,8 @@ const getElement = (
213
207
  }
214
208
 
215
209
  case 'Pane': {
216
- const toolModeVal = getCtx(props).toolModeValStore.get().value;
217
210
  const paneNodes = getCtx(props).getChildNodeIDs(node.id);
218
211
  const paneNode = node as PaneNode;
219
- if (toolModeVal === 'designLibrary') {
220
- return <Pane_DesignLibrary {...sharedProps} />;
221
- }
222
212
  if (paneNode.isContextPane) {
223
213
  if (!isPreview)
224
214
  getCtx(props).hasTitle.set(!(!paneNode.slug || !paneNode.title));
@@ -241,7 +231,11 @@ const getElement = (
241
231
  <ContextPanePanel nodeId={node.id} />
242
232
  ) : null}
243
233
  <div>
244
- <Pane {...sharedProps} />
234
+ {toolModeVal === 'designLibrary' ? (
235
+ <Pane_DesignLibrary {...sharedProps} />
236
+ ) : (
237
+ <Pane {...sharedProps} />
238
+ )}
245
239
  {!isPreview &&
246
240
  paneNode.slug &&
247
241
  paneNode.title &&
@@ -255,7 +249,6 @@ const getElement = (
255
249
  nodeId={node.id}
256
250
  first={true}
257
251
  ctx={getCtx(props)}
258
- config={props.config!}
259
252
  isContextPane={true}
260
253
  />
261
254
  </PanelVisibilityWrapper>
@@ -283,12 +276,7 @@ const getElement = (
283
276
  panelType="add"
284
277
  ctx={getCtx(props)}
285
278
  >
286
- <AddPanePanel
287
- nodeId={node.id}
288
- first={true}
289
- ctx={getCtx(props)}
290
- config={props.config!}
291
- />
279
+ <AddPanePanel nodeId={node.id} first={true} ctx={getCtx(props)} />
292
280
  </PanelVisibilityWrapper>
293
281
  )}
294
282
  <div className="py-0.5">
@@ -303,6 +291,8 @@ const getElement = (
303
291
  <PaneEraser {...sharedProps} />
304
292
  ) : toolModeVal === `layout` ? (
305
293
  <PaneLayout {...sharedProps} />
294
+ ) : toolModeVal === 'designLibrary' ? (
295
+ <Pane_DesignLibrary {...sharedProps} />
306
296
  ) : (
307
297
  <Pane {...sharedProps} />
308
298
  )}
@@ -312,12 +302,7 @@ const getElement = (
312
302
  panelType="add"
313
303
  ctx={getCtx(props)}
314
304
  >
315
- <AddPanePanel
316
- nodeId={node.id}
317
- first={false}
318
- ctx={getCtx(props)}
319
- config={props.config!}
320
- />
305
+ <AddPanePanel nodeId={node.id} first={false} ctx={getCtx(props)} />
321
306
  </PanelVisibilityWrapper>
322
307
  </>
323
308
  );
@@ -326,7 +311,6 @@ const getElement = (
326
311
  case 'BgPane':
327
312
  return <BgPaneWrapper {...sharedProps} />;
328
313
  case 'GridLayoutNode': {
329
- const toolModeVal = getCtx(props).toolModeValStore.get().value;
330
314
  if (toolModeVal === 'eraser') {
331
315
  return <GridLayoutEraser {...sharedProps} />;
332
316
  }
@@ -343,8 +327,6 @@ const getElement = (
343
327
  case 'li':
344
328
  case 'aside':
345
329
  case 'p': {
346
- const toolModeVal = getCtx(props).toolModeValStore.get().value;
347
-
348
330
  if (toolModeVal === 'styles') {
349
331
  const className = getCtx(props).getNodeClasses(
350
332
  node.id,
@@ -443,13 +425,11 @@ const getElement = (
443
425
  case 'text':
444
426
  return <NodeText {...sharedProps} />;
445
427
  case 'button': {
446
- const toolModeVal = getCtx(props).toolModeValStore.get().value;
447
428
  if (toolModeVal === `eraser`)
448
429
  return <NodeButtonEraser {...sharedProps} />;
449
430
  return <NodeButton {...sharedProps} isSelectableText={false} />;
450
431
  }
451
432
  case 'a': {
452
- const toolModeVal = getCtx(props).toolModeValStore.get().value;
453
433
  if (toolModeVal === `eraser`) return <NodeAEraser {...sharedProps} />;
454
434
  return <NodeA {...sharedProps} isSelectableText={false} />;
455
435
  }
@@ -471,6 +451,7 @@ const Node = memo((props: NodeProps) => {
471
451
  const node = getCtx(props).allNodes.get().get(props.nodeId) as FlatNode;
472
452
  const isPreview = getCtx(props).rootNodeId.get() === `tmp`;
473
453
  const settingsPanel = useStore(settingsPanelStore);
454
+ const toolModeVal = getCtx(props).toolModeValStore.get().value;
474
455
 
475
456
  const {
476
457
  markdownParentId,
@@ -560,7 +541,7 @@ const Node = memo((props: NodeProps) => {
560
541
  return <div style={highlightStyle}>{element}</div>;
561
542
  }
562
543
 
563
- if (!isPreview && getCtx(props).showGuids.get()) {
544
+ if (!isPreview && toolModeVal === `debug`) {
564
545
  return <NodeWithGuid {...props} element={element} />;
565
546
  }
566
547
 
@@ -1,13 +1,48 @@
1
1
  import { memo, type ReactElement } from 'react';
2
- import { getCtx } from '@/stores/nodes';
2
+ import { getCtx, type NodesContext } from '@/stores/nodes';
3
3
  import { getType } from '@/utils/compositor/typeGuards';
4
- import type { FlatNode } from '@/types/compositorTypes';
4
+ import type { BaseNode, FlatNode } from '@/types/compositorTypes';
5
5
  import type { NodeProps } from '@/types/nodeProps';
6
+ import ArrowDownTrayIcon from '@heroicons/react/24/outline/ArrowDownTrayIcon';
7
+
8
+ const getNodeTree = (nodeId: string, ctx: NodesContext): BaseNode | null => {
9
+ const allNodesMap = ctx.allNodes.get();
10
+ const node = allNodesMap.get(nodeId);
11
+
12
+ if (!node) {
13
+ return null;
14
+ }
15
+
16
+ // Use JSON stringify/parse to get a deep clone, breaking any proxies/reactivity
17
+ const clonedNode = JSON.parse(JSON.stringify(node));
18
+
19
+ const childIds = ctx.getChildNodeIDs(nodeId);
20
+
21
+ if (childIds.length > 0) {
22
+ clonedNode.children = childIds
23
+ .map((childId) => getNodeTree(childId, ctx))
24
+ .filter((child): child is BaseNode => child !== null);
25
+ }
26
+
27
+ return clonedNode;
28
+ };
6
29
 
7
30
  export type RenderableNodes = NodeProps & { element: ReactElement };
8
31
 
9
32
  export const NodeWithGuid = memo((props: RenderableNodes) => {
10
- const node = getCtx(props).allNodes.get().get(props.nodeId) as FlatNode;
33
+ const ctx = getCtx(props);
34
+ const node = ctx.allNodes.get().get(props.nodeId) as FlatNode;
35
+
36
+ const handleDumpTree = (e: React.MouseEvent) => {
37
+ e.stopPropagation();
38
+ const nodeTree = getNodeTree(props.nodeId, ctx);
39
+ console.log(
40
+ `%c--- Dumping Node Tree for ${getType(node)}: ${props.nodeId} ---`,
41
+ 'color: #0891b2; font-weight: bold;'
42
+ );
43
+ console.log(nodeTree);
44
+ };
45
+
11
46
  return (
12
47
  <div className="relative">
13
48
  <div
@@ -15,8 +50,17 @@ export const NodeWithGuid = memo((props: RenderableNodes) => {
15
50
  data-node-id={props.nodeId}
16
51
  data-node-type={getType(node)}
17
52
  >
18
- <div className="border-b border-dotted border-cyan-600 p-1 font-mono text-xs text-cyan-600">
19
- {getType(node)}: {props.nodeId}
53
+ <div className="flex items-center justify-between border-b border-dotted border-cyan-600 p-1 font-mono text-xs text-cyan-600">
54
+ <span className="truncate pr-2">
55
+ {getType(node)}: {props.nodeId}
56
+ </span>
57
+ <button
58
+ onClick={handleDumpTree}
59
+ title={`Log tree for ${props.nodeId}`}
60
+ className="flex-shrink-0 rounded p-0.5 hover:bg-cyan-100"
61
+ >
62
+ <ArrowDownTrayIcon className="h-4 w-4 text-cyan-700" />
63
+ </button>
20
64
  </div>
21
65
  <div className="p-0.5">{props.element}</div>
22
66
  </div>
@@ -166,35 +166,18 @@ const Pane = memo(
166
166
  className={useFlexLayout ? '' : wrapperClasses}
167
167
  >
168
168
  {codeHookPayload && codeHookTarget === 'product-card' ? (
169
- <ProductCardSetup
170
- nodeId={props.nodeId}
171
- params={codeHookParams}
172
- config={props.config!}
173
- />
169
+ <ProductCardSetup nodeId={props.nodeId} params={codeHookParams} />
174
170
  ) : codeHookPayload && codeHookTarget === 'product-grid' ? (
175
- <ProductGridSetup
176
- nodeId={props.nodeId}
177
- params={codeHookParams}
178
- config={props.config!}
179
- />
171
+ <ProductGridSetup nodeId={props.nodeId} params={codeHookParams} />
180
172
  ) : codeHookPayload && codeHookTarget === 'featured-article' ? (
181
173
  <FeaturedArticleSetup
182
174
  nodeId={props.nodeId}
183
175
  params={codeHookParams}
184
- config={props.config!}
185
176
  />
186
177
  ) : codeHookPayload && codeHookTarget === 'list-content' ? (
187
- <ListContentSetup
188
- nodeId={props.nodeId}
189
- params={codeHookParams}
190
- config={props.config!}
191
- />
178
+ <ListContentSetup nodeId={props.nodeId} params={codeHookParams} />
192
179
  ) : codeHookPayload && codeHookTarget === 'bunny-video' ? (
193
- <BunnyVideoSetup
194
- nodeId={props.nodeId}
195
- params={codeHookParams}
196
- config={props.config!}
197
- />
180
+ <BunnyVideoSetup nodeId={props.nodeId} params={codeHookParams} />
198
181
  ) : codeHookPayload && codeHookTarget ? (
199
182
  <CodeHookContainer
200
183
  payload={{ target: codeHookTarget, params: codeHookParams }}
@@ -2,6 +2,8 @@ import { type CSSProperties, useEffect, useState } from 'react';
2
2
  import { useStore } from '@nanostores/react';
3
3
  import ArchiveBoxArrowDownIcon from '@heroicons/react/24/outline/ArchiveBoxArrowDownIcon';
4
4
  import ArrowPathRoundedSquareIcon from '@heroicons/react/24/outline/ArrowPathRoundedSquareIcon';
5
+ import ArrowDownTrayIcon from '@heroicons/react/24/outline/ArrowDownTrayIcon';
6
+ import CheckIcon from '@heroicons/react/24/outline/CheckIcon';
5
7
  import { viewportKeyStore } from '@/stores/storykeep';
6
8
  import { getCtx } from '@/stores/nodes';
7
9
  import { RenderChildren } from './RenderChildren';
@@ -10,6 +12,7 @@ import type { NodeProps } from '@/types/nodeProps';
10
12
  import { SaveToLibraryModal } from '@/components/edit/state/SaveToLibraryModal';
11
13
  import { RestylePaneModal } from '@/components/edit/pane/RestylePaneModal';
12
14
  import { selectionStore } from '@/stores/selection';
15
+ import { copyPaneToClipboard } from '@/utils/compositor/designLibraryHelper';
13
16
 
14
17
  export const Pane_DesignLibrary = (props: NodeProps) => {
15
18
  const ctx = getCtx(props);
@@ -32,6 +35,7 @@ export const Pane_DesignLibrary = (props: NodeProps) => {
32
35
  ...ctx.getChildNodeIDs(props.nodeId),
33
36
  ]);
34
37
  const [isSaveModalOpen, setIsSaveModalOpen] = useState(false);
38
+ const [wasCopied, setWasCopied] = useState(false);
35
39
  const getPaneId = (): string => `pane-${props.nodeId}`;
36
40
 
37
41
  useEffect(() => {
@@ -52,9 +56,14 @@ export const Pane_DesignLibrary = (props: NodeProps) => {
52
56
  setIsSaveModalOpen(true);
53
57
  };
54
58
 
55
- if (!props.config || !props.config.TENANT_ID) {
56
- return <></>;
57
- }
59
+ const handleCopyToClipboard = async (e: React.MouseEvent) => {
60
+ e.stopPropagation();
61
+ const success = await copyPaneToClipboard(props.nodeId);
62
+ if (success) {
63
+ setWasCopied(true);
64
+ setTimeout(() => setWasCopied(false), 2000);
65
+ }
66
+ };
58
67
 
59
68
  return (
60
69
  <div id={getPaneId()} className="pane min-h-16">
@@ -67,7 +76,7 @@ export const Pane_DesignLibrary = (props: NodeProps) => {
67
76
  e.stopPropagation();
68
77
  }}
69
78
  >
70
- <div className="absolute left-2 top-2 z-10 flex flex-col gap-y-2">
79
+ <div className="absolute left-2 top-2 z-10 flex flex-row gap-x-2">
71
80
  {!props.isSandboxMode && (
72
81
  <button
73
82
  title="Save Pane to Design Library"
@@ -84,6 +93,19 @@ export const Pane_DesignLibrary = (props: NodeProps) => {
84
93
  >
85
94
  <ArrowPathRoundedSquareIcon className="h-7 w-7 text-white" />
86
95
  </button>
96
+ <button
97
+ title="Copy Pane Design to Clipboard"
98
+ onClick={handleCopyToClipboard}
99
+ className={`flex h-10 w-10 items-center justify-center rounded-full p-1.5 shadow-lg transition-colors ${
100
+ wasCopied ? 'bg-green-500' : 'bg-gray-600 hover:bg-gray-700'
101
+ }`}
102
+ >
103
+ {wasCopied ? (
104
+ <CheckIcon className="h-7 w-7 text-white" />
105
+ ) : (
106
+ <ArrowDownTrayIcon className="h-7 w-7 text-white" />
107
+ )}
108
+ </button>
87
109
  </div>
88
110
  {codeHookPayload ? (
89
111
  <CodeHookContainer payload={codeHookPayload} />
@@ -95,13 +117,11 @@ export const Pane_DesignLibrary = (props: NodeProps) => {
95
117
  {isSaveModalOpen && (
96
118
  <SaveToLibraryModal
97
119
  paneId={props.nodeId}
98
- config={props.config}
99
- tenantId={props.config.TENANT_ID}
100
120
  onClose={() => setIsSaveModalOpen(false)}
101
121
  />
102
122
  )}
103
123
 
104
- {isRestyleModalOpen && <RestylePaneModal config={props.config} />}
124
+ {isRestyleModalOpen && <RestylePaneModal />}
105
125
  </div>
106
126
  );
107
127
  };
@@ -37,7 +37,9 @@ const VERBOSE = false;
37
37
  export const NodeBasicTag = (props: NodeTagProps) => {
38
38
  const nodeId = props.nodeId;
39
39
  const ctx = getCtx(props);
40
- const Tag = ctx.showGuids.get() ? `div` : props.tagName;
40
+ //const Tag = ctx.showGuids.get() ? `div` : props.tagName;
41
+ const Tag =
42
+ ctx.toolModeValStore.get().value === `debug` ? `div` : props.tagName;
41
43
 
42
44
  if (props.tagName === 'span') {
43
45
  const node = ctx.allNodes.get().get(props.nodeId);
@@ -1,7 +1,6 @@
1
1
  import { useState, useEffect, useRef } from 'react';
2
2
  import ColorPickerCombo from '@/components/fields/ColorPickerCombo';
3
3
  import { setPendingImageOperation } from '@/stores/storykeep';
4
- import type { BrandConfig } from '@/types/tractstack';
5
4
 
6
5
  const OG_IMAGE_WIDTH = 1200;
7
6
  const OG_IMAGE_HEIGHT = 630;
@@ -12,7 +11,6 @@ interface OgImagePreviewProps {
12
11
  nodeId: string;
13
12
  title: string;
14
13
  socialImagePath: string | null;
15
- config: BrandConfig;
16
14
  onColorChange?: (textColor: string, bgColor: string) => void;
17
15
  }
18
16
 
@@ -20,7 +18,6 @@ const OgImagePreview = ({
20
18
  nodeId,
21
19
  title,
22
20
  socialImagePath,
23
- config,
24
21
  onColorChange,
25
22
  }: OgImagePreviewProps) => {
26
23
  const [fontSize, setFontSize] = useState<number>(48);
@@ -190,13 +187,11 @@ const OgImagePreview = ({
190
187
  title="Text Color"
191
188
  defaultColor={textColor}
192
189
  onColorChange={handleTextColorChange}
193
- config={config}
194
190
  />
195
191
  <ColorPickerCombo
196
192
  title="Background Color"
197
193
  defaultColor={bgColor}
198
194
  onColorChange={handleBgColorChange}
199
- config={config}
200
195
  />
201
196
  </div>
202
197
  )}