astro-tractstack 2.0.0-rc.8 → 2.0.0

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 (141) hide show
  1. package/LICENSE +8 -97
  2. package/README.md +7 -5
  3. package/bin/create-tractstack.js +35 -11
  4. package/dist/index.js +106 -29
  5. package/package.json +10 -5
  6. package/templates/css/frontend.css +1 -1
  7. package/templates/custom/minimal/CodeHook.astro +13 -12
  8. package/templates/custom/minimal/CustomRoutes.astro +25 -31
  9. package/templates/custom/with-examples/CodeHook.astro +22 -11
  10. package/templates/custom/with-examples/CustomRoutes.astro +4 -8
  11. package/templates/custom/with-examples/ProductCard.astro +29 -0
  12. package/templates/custom/with-examples/ProductCardWrapper.astro +43 -0
  13. package/templates/custom/with-examples/ProductGrid.astro +64 -0
  14. package/templates/custom/with-examples/pages/Collections.astro +58 -98
  15. package/templates/gitignore +42 -0
  16. package/templates/prettierignore +5 -0
  17. package/templates/prettierrc +19 -0
  18. package/templates/src/client/app.js +127 -0
  19. package/templates/src/client/htmx.min.js +3519 -0
  20. package/templates/src/client/view.js +429 -0
  21. package/templates/src/components/Footer.astro +4 -9
  22. package/templates/src/components/Header.astro +67 -60
  23. package/templates/src/components/Menu.tsx +188 -52
  24. package/templates/src/components/codehooks/BunnyVideoSetup.tsx +2 -2
  25. package/templates/src/components/codehooks/EpinetDurationSelector.tsx +9 -13
  26. package/templates/src/components/codehooks/EpinetTableView.tsx +11 -7
  27. package/templates/src/components/codehooks/EpinetWrapper.tsx +1 -0
  28. package/templates/src/components/codehooks/FeaturedArticle.astro +105 -0
  29. package/templates/src/components/codehooks/FeaturedArticleSetup.tsx +318 -0
  30. package/templates/src/components/codehooks/ListContent.astro +32 -162
  31. package/templates/src/components/codehooks/ListContentSetup.tsx +43 -138
  32. package/templates/src/components/codehooks/ProductCardSetup.tsx +152 -0
  33. package/templates/src/components/codehooks/ProductGridSetup.tsx +274 -0
  34. package/templates/src/components/codehooks/SearchWidget.tsx +453 -0
  35. package/templates/src/components/compositor/Node.tsx +3 -6
  36. package/templates/src/components/compositor/PanelVisibilityWrapper.tsx +21 -11
  37. package/templates/src/components/compositor/elements/BunnyVideo.tsx +21 -20
  38. package/templates/src/components/compositor/nodes/Pane.tsx +51 -21
  39. package/templates/src/components/compositor/nodes/RenderChildren.tsx +6 -1
  40. package/templates/src/components/compositor/nodes/Widget.tsx +16 -2
  41. package/templates/src/components/compositor/preview/FeaturedArticlePreview.tsx +155 -0
  42. package/templates/src/components/compositor/preview/PaneSnapshotGenerator.tsx +20 -1
  43. package/templates/src/components/edit/Header.tsx +10 -4
  44. package/templates/src/components/edit/PanelSwitch.tsx +11 -7
  45. package/templates/src/components/edit/SettingsPanel.tsx +29 -18
  46. package/templates/src/components/edit/ToolBar.tsx +1 -28
  47. package/templates/src/components/edit/ToolMode.tsx +45 -32
  48. package/templates/src/components/edit/pane/AddPanePanel_break.tsx +12 -2
  49. package/templates/src/components/edit/pane/AddPanePanel_codehook.tsx +8 -2
  50. package/templates/src/components/edit/pane/AddPanePanel_newAICopy_modal.tsx +1 -1
  51. package/templates/src/components/edit/pane/ConfigPanePanel.tsx +17 -27
  52. package/templates/src/components/edit/pane/PageGenSelector.tsx +16 -16
  53. package/templates/src/components/edit/pane/PageGenSpecial.tsx +26 -49
  54. package/templates/src/components/edit/pane/PageGen_preview.tsx +17 -2
  55. package/templates/src/components/edit/pane/PanePanel_path.tsx +2 -4
  56. package/templates/src/components/edit/pane/PanePanel_title.tsx +243 -76
  57. package/templates/src/components/edit/panels/StyleBreakPanel.tsx +17 -19
  58. package/templates/src/components/edit/panels/StyleCodeHookPanel.tsx +48 -37
  59. package/templates/src/components/edit/panels/StyleElementPanel_add.tsx +60 -55
  60. package/templates/src/components/edit/panels/StyleImagePanel_add.tsx +56 -50
  61. package/templates/src/components/edit/panels/StyleLiElementPanel_add.tsx +54 -47
  62. package/templates/src/components/edit/panels/StyleLinkPanel_add.tsx +54 -44
  63. package/templates/src/components/edit/panels/StyleLinkPanel_config.tsx +113 -138
  64. package/templates/src/components/edit/panels/StyleParentPanel_add.tsx +54 -40
  65. package/templates/src/components/edit/panels/StyleWidgetPanel.tsx +3 -3
  66. package/templates/src/components/edit/panels/StyleWidgetPanel_add.tsx +56 -49
  67. package/templates/src/components/edit/panels/StyleWidgetPanel_config.tsx +14 -5
  68. package/templates/src/components/edit/state/SaveModal.tsx +316 -169
  69. package/templates/src/components/edit/storyfragment/StoryFragmentPanel_og.tsx +1 -1
  70. package/templates/src/components/edit/storyfragment/StoryFragmentPanel_slug.tsx +56 -55
  71. package/templates/src/components/edit/widgets/BunnyWidget.tsx +538 -59
  72. package/templates/src/components/edit/widgets/InteractiveDisclosureWidget.tsx +656 -0
  73. package/templates/src/components/edit/widgets/ToggleWidget.tsx +9 -16
  74. package/templates/src/components/fields/ArtpackImage.tsx +4 -1
  75. package/templates/src/components/fields/BackgroundImage.tsx +1 -1
  76. package/templates/src/components/fields/BackgroundImageWrapper.tsx +127 -35
  77. package/templates/src/components/fields/ColorPickerCombo.tsx +66 -62
  78. package/templates/src/components/fields/ImageUpload.tsx +1 -1
  79. package/templates/src/components/fields/ViewportComboBox.tsx +59 -42
  80. package/templates/src/components/form/ActionBuilderBeliefSelector.tsx +117 -0
  81. package/templates/src/components/form/ActionBuilderField.tsx +306 -87
  82. package/templates/src/components/search/SearchModal.tsx +420 -0
  83. package/templates/src/components/search/SearchResults.tsx +367 -0
  84. package/templates/src/components/search/SearchWrapper.tsx +46 -0
  85. package/templates/src/components/storykeep/Dashboard_Advanced.tsx +1 -1
  86. package/templates/src/components/storykeep/Dashboard_Analytics.tsx +34 -8
  87. package/templates/src/components/storykeep/Dashboard_Content.tsx +6 -0
  88. package/templates/src/components/storykeep/StoryKeepBackdrop.astro +87 -0
  89. package/templates/src/components/storykeep/controls/content/BeliefForm.tsx +37 -33
  90. package/templates/src/components/storykeep/controls/content/MenuForm.tsx +55 -7
  91. package/templates/src/components/storykeep/controls/content/ResourceForm.tsx +17 -2
  92. package/templates/src/components/storykeep/controls/content/StoryFragmentTable.tsx +5 -8
  93. package/templates/src/components/storykeep/state/FetchAnalytics.tsx +274 -228
  94. package/templates/src/components/storykeep/widgets/Wizard.tsx +14 -7
  95. package/templates/src/components/tenant/RegistrationForm.tsx +1 -1
  96. package/templates/src/components/widgets/ImpressionWrapper.tsx +0 -1
  97. package/templates/src/constants/shapes.ts +9 -0
  98. package/templates/src/constants.ts +2121 -16
  99. package/templates/src/hooks/useSearch.ts +228 -0
  100. package/templates/src/layouts/Layout.astro +213 -104
  101. package/templates/src/lib/storyData.ts +4 -1
  102. package/templates/src/pages/[...slug]/edit.astro +14 -14
  103. package/templates/src/pages/[...slug].astro +82 -21
  104. package/templates/src/pages/api/orphan-analysis.ts +0 -1
  105. package/templates/src/pages/api/tailwind.ts +23 -21
  106. package/templates/src/pages/context/[...contextSlug]/edit.astro +14 -14
  107. package/templates/src/pages/context/[...contextSlug].astro +7 -2
  108. package/templates/src/pages/storykeep/advanced.astro +5 -4
  109. package/templates/src/pages/storykeep/branding.astro +5 -4
  110. package/templates/src/pages/storykeep/content.astro +5 -4
  111. package/templates/src/pages/storykeep/init.astro +40 -1
  112. package/templates/src/pages/storykeep/login.astro +1 -1
  113. package/templates/src/pages/storykeep.astro +5 -4
  114. package/templates/src/stores/nodes.ts +59 -88
  115. package/templates/src/stores/orphanAnalysis.ts +19 -21
  116. package/templates/src/stores/storykeep.ts +7 -0
  117. package/templates/src/types/compositorTypes.ts +6 -0
  118. package/templates/src/types/tractstack.ts +17 -0
  119. package/templates/src/utils/actions/lispLexer.ts +2 -2
  120. package/templates/src/utils/actions/preParse_Action.ts +3 -0
  121. package/templates/src/utils/api/beliefHelpers.ts +12 -36
  122. package/templates/src/utils/api/menuHelpers.ts +2 -2
  123. package/templates/src/utils/api.ts +26 -0
  124. package/templates/src/utils/compositor/TemplateNodes.ts +7 -0
  125. package/templates/src/utils/compositor/allowInsert.ts +5 -3
  126. package/templates/src/utils/compositor/nodesHelper.ts +4 -0
  127. package/templates/src/utils/compositor/processMarkdown.ts +16 -2
  128. package/templates/src/utils/compositor/reduceNodesClassNames.ts +4 -0
  129. package/templates/src/utils/compositor/templateMarkdownStyles.ts +13 -13
  130. package/templates/src/utils/compositor/typeGuards.ts +1 -0
  131. package/templates/src/utils/customHelpers.ts +38 -0
  132. package/templates/src/utils/helpers.ts +2 -2
  133. package/templates/src/utils/layout.ts +65 -144
  134. package/utils/inject-files.ts +95 -18
  135. package/templates/src/client/analytics-events.js +0 -207
  136. package/templates/src/client/belief-events.js +0 -191
  137. package/templates/src/client/sse.js +0 -613
  138. package/templates/src/components/codehooks/FeaturedContent.astro +0 -273
  139. package/templates/src/components/codehooks/FeaturedContentSetup.tsx +0 -738
  140. package/templates/src/components/compositor/preview/FeaturedContentPreview.tsx +0 -128
  141. package/templates/src/components/edit/pane/PanePanel_slug.tsx +0 -219
@@ -1,4 +1,4 @@
1
- import { useEffect } from 'react';
1
+ import { useRef, useEffect } from 'react';
2
2
  import { useStore } from '@nanostores/react';
3
3
  import PencilSquareIcon from '@heroicons/react/24/outline/PencilSquareIcon';
4
4
  import PaintBrushIcon from '@heroicons/react/24/outline/PaintBrushIcon';
@@ -41,12 +41,6 @@ const storykeepToolModes = [
41
41
  title: 'Move',
42
42
  description: 'Keyboard accessible re-order',
43
43
  },
44
- {
45
- key: 'debug' as const,
46
- Icon: BugAntIcon,
47
- title: 'Debug',
48
- description: 'Debug node ids',
49
- },
50
44
  ] as const;
51
45
 
52
46
  interface StoryKeepToolModeProps {
@@ -54,9 +48,10 @@ interface StoryKeepToolModeProps {
54
48
  }
55
49
 
56
50
  const StoryKeepToolMode = ({ isContext }: StoryKeepToolModeProps) => {
57
- //const signal = useStore(settingsPanelStore);
58
51
  const ctx = getCtx();
59
52
  const { value: toolModeVal } = useStore(ctx.toolModeValStore);
53
+ const showGuids = useStore(ctx.showGuids);
54
+ const navRef = useRef<HTMLElement>(null);
60
55
 
61
56
  const hasTitle = useStore(ctx.hasTitle);
62
57
  const hasPanes = useStore(ctx.hasPanes);
@@ -64,6 +59,8 @@ const StoryKeepToolMode = ({ isContext }: StoryKeepToolModeProps) => {
64
59
  const className =
65
60
  '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';
66
61
  const classNameActive = 'w-8 h-8 py-1.5 rounded-md bg-myblue text-white';
62
+ const classNameDebugActive =
63
+ 'w-8 h-8 py-1.5 rounded-md bg-orange-500 text-white';
67
64
 
68
65
  const currentToolMode =
69
66
  storykeepToolModes.find((mode) => mode.key === toolModeVal) ??
@@ -72,49 +69,65 @@ const StoryKeepToolMode = ({ isContext }: StoryKeepToolModeProps) => {
72
69
  const handleClick = (mode: ToolModeVal) => {
73
70
  settingsPanelStore.set(null);
74
71
  ctx.toolModeValStore.set({ value: mode });
75
- ctx.showGuids.set(mode === `debug`);
76
72
  ctx.notifyNode('root');
77
73
  };
78
74
 
79
- // Escape key listener
75
+ const handleDebugToggle = () => {
76
+ ctx.showGuids.set(!showGuids);
77
+ ctx.notifyNode('root');
78
+ };
79
+
80
80
  useEffect(() => {
81
81
  const handleEscapeKey = (event: KeyboardEvent) => {
82
82
  if (event.key === 'Escape') {
83
83
  ctx.toolModeValStore.set({ value: 'text' });
84
- console.log('Tool mode reset to text via Escape');
84
+ ctx.notifyNode('root');
85
85
  }
86
86
  };
87
+
87
88
  document.addEventListener('keydown', handleEscapeKey);
89
+
88
90
  return () => {
89
91
  document.removeEventListener('keydown', handleEscapeKey);
90
92
  };
91
93
  }, [ctx]);
92
94
 
93
- if (!hasTitle || (!hasPanes && !isContext)) return null;
95
+ if (!hasTitle || (!hasPanes && !isContext)) {
96
+ return null;
97
+ }
94
98
 
95
99
  return (
96
- <nav
97
- id="mainNav"
98
- className="z-102 bg-mywhite fixed bottom-0 left-0 right-0 pt-1.5 md:sticky md:bottom-auto md:left-0 md:top-24 md:h-screen md:w-16 md:pt-0"
99
- >
100
- <div className="flex flex-wrap justify-around gap-4 py-3.5 md:mt-0 md:flex-col md:items-center md:gap-8 md:space-x-0 md:space-y-2 md:py-2">
101
- <div className="text-mydarkgrey h-16 text-center text-sm font-bold">
102
- mode:
103
- <div className="font-action text-myblue pt-1.5 text-center text-xs">
104
- {currentToolMode.title}
100
+ <>
101
+ <nav
102
+ id="mainNav"
103
+ ref={navRef}
104
+ className="z-102 bg-mywhite md:bg-mywhite/70 fixed bottom-0 left-0 right-0 p-1.5 md:bottom-2 md:right-auto md:h-auto md:w-auto md:rounded-r-xl md:border md:border-black/5 md:p-2 md:shadow-lg md:backdrop-blur-sm"
105
+ >
106
+ <div className="flex flex-wrap justify-around gap-4 py-0.5 md:flex-nowrap md:justify-start md:gap-4 md:p-0">
107
+ <div className="text-mydarkgrey text-center text-sm font-bold">
108
+ mode:
109
+ <div className="font-action text-myblue pt-1.5 text-center text-xs">
110
+ {currentToolMode.title}
111
+ </div>
105
112
  </div>
106
- </div>
107
- {storykeepToolModes.map(({ key, Icon, description }) => (
108
- <div title={description} key={key}>
109
- {key === toolModeVal ? (
110
- <Icon className={classNameActive} />
111
- ) : (
112
- <Icon className={className} onClick={() => handleClick(key)} />
113
- )}
113
+ {storykeepToolModes.map(({ key, Icon, description }) => (
114
+ <div title={description} key={key}>
115
+ {key === toolModeVal ? (
116
+ <Icon className={classNameActive} />
117
+ ) : (
118
+ <Icon className={className} onClick={() => handleClick(key)} />
119
+ )}
120
+ </div>
121
+ ))}
122
+ <div title="Toggle debug node ids" key="debug">
123
+ <BugAntIcon
124
+ className={showGuids ? classNameDebugActive : className}
125
+ onClick={handleDebugToggle}
126
+ />
114
127
  </div>
115
- ))}
116
- </div>
117
- </nav>
128
+ </div>
129
+ </nav>
130
+ </>
118
131
  );
119
132
  };
120
133
 
@@ -14,7 +14,9 @@ import {
14
14
  type SnapshotData,
15
15
  } from '@/components/compositor/preview/PaneSnapshotGenerator';
16
16
  import { createEmptyStorykeep } from '@/utils/compositor/nodesHelper';
17
+ import { tailwindToHex } from '@/utils/compositor/tailwindColors';
17
18
  import { getTemplateVisualBreakPane } from '@/utils/compositor/TemplatePanes';
19
+ import { SvgBreaks } from '@/constants/shapes';
18
20
  import {
19
21
  PaneAddMode,
20
22
  type PaneNode,
@@ -243,8 +245,16 @@ const AddPaneBreakPanel = ({
243
245
  }
244
246
  }
245
247
 
246
- template.bgColour = belowColor;
247
- const svgFill = aboveColor === belowColor ? 'black' : aboveColor;
248
+ const shapeName = `kCz${variant}`;
249
+ const isFlipped = SvgBreaks[shapeName]?.flipped || false;
250
+ template.bgColour = tailwindToHex(
251
+ isFlipped ? aboveColor : belowColor,
252
+ null
253
+ );
254
+ const svgFill = tailwindToHex(
255
+ isFlipped ? belowColor : aboveColor === belowColor ? 'black' : aboveColor,
256
+ null
257
+ );
248
258
 
249
259
  if (template.bgPane) {
250
260
  if (template.bgPane.type === 'visual-break') {
@@ -62,7 +62,12 @@ const AddPaneCodeHookPanel = ({
62
62
  }, [filteredHooks]);
63
63
 
64
64
  const isHookAvailable = (hookName: string) => {
65
- if (hookName === 'featured-content' || hookName === 'list-content') {
65
+ if (
66
+ (hookName === 'featured-content' ||
67
+ hookName === 'list-content' ||
68
+ hookName === 'featured-article') &&
69
+ !hasStoryFragments
70
+ ) {
66
71
  return hasStoryFragments;
67
72
  }
68
73
  return true;
@@ -153,7 +158,8 @@ const AddPaneCodeHookPanel = ({
153
158
  font-weight: bold;
154
159
  }
155
160
  .hook-item-available:hover {
156
- background-color: rgba(8, 145, 178, 0.1); /* bg-cyan-600/10 */
161
+ background-color: #0891b2;
162
+ color: #fff;
157
163
  }
158
164
  .hook-item-disabled {
159
165
  background-color: #f9fafb; /* bg-gray-50 */
@@ -165,7 +165,7 @@ ${additionalInstructions}`;
165
165
  Content has been generated successfully! Click "Apply
166
166
  Content" to use this content with your selected design.
167
167
  </p>
168
- <div className="max-h-[60vh] overflow-y-auto rounded-md border border-gray-200 bg-gray-50 p-4">
168
+ <div className="overflow-y-auto rounded-md border border-gray-200 bg-gray-50 p-4">
169
169
  <pre className="whitespace-pre-wrap font-mono text-sm text-gray-800">
170
170
  {generatedContent}
171
171
  </pre>
@@ -9,10 +9,13 @@ import {
9
9
  isContextPaneNode,
10
10
  hasBeliefPayload,
11
11
  } from '@/utils/compositor/typeGuards';
12
- import { settingsPanelStore, viewportKeyStore } from '@/stores/storykeep';
12
+ import {
13
+ settingsPanelStore,
14
+ viewportKeyStore,
15
+ fullContentMapStore,
16
+ } from '@/stores/storykeep';
13
17
  import { getCtx } from '@/stores/nodes';
14
18
  import PaneTitlePanel from './PanePanel_title';
15
- import PaneSlugPanel from './PanePanel_slug';
16
19
  import PaneMagicPathPanel from './PanePanel_path';
17
20
  import PaneImpressionPanel from './PanePanel_impression';
18
21
  import { PaneConfigMode, type PaneNode } from '@/types/compositorTypes';
@@ -30,7 +33,7 @@ const ConfigPanePanel = ({ nodeId }: ConfigPanePanelProps) => {
30
33
  const reorderMode = toolMode.value === `move`;
31
34
  const isActiveMode =
32
35
  activePaneMode.panel === 'settings' && activePaneMode.paneId === nodeId;
33
-
36
+ const $contentMap = useStore(fullContentMapStore);
34
37
  const $viewportKey = useStore(viewportKeyStore);
35
38
  const isMobile = $viewportKey.value === `mobile`;
36
39
 
@@ -68,6 +71,7 @@ const ConfigPanePanel = ({ nodeId }: ConfigPanePanelProps) => {
68
71
  };
69
72
 
70
73
  const handleCodeHookConfig = () => {
74
+ ctx.toolModeValStore.set({ value: 'styles' });
71
75
  settingsPanelStore.set({
72
76
  action: 'setup-codehook',
73
77
  nodeId: nodeId,
@@ -103,8 +107,6 @@ const ConfigPanePanel = ({ nodeId }: ConfigPanePanelProps) => {
103
107
 
104
108
  if (mode === PaneConfigMode.TITLE) {
105
109
  return <PaneTitlePanel nodeId={nodeId} setMode={setSaveMode} />;
106
- } else if (mode === PaneConfigMode.SLUG) {
107
- return <PaneSlugPanel nodeId={nodeId} setMode={setSaveMode} />;
108
110
  } else if (mode === PaneConfigMode.PATH) {
109
111
  return <PaneMagicPathPanel nodeId={nodeId} setMode={setSaveMode} />;
110
112
  } else if (mode === PaneConfigMode.IMPRESSION) {
@@ -136,28 +138,16 @@ const ConfigPanePanel = ({ nodeId }: ConfigPanePanelProps) => {
136
138
  <>
137
139
  {!isTemplate && (
138
140
  <>
139
- <button
140
- onClick={() => setSaveMode(PaneConfigMode.TITLE)}
141
- className={buttonClass}
142
- >
143
- Pane Title
144
- {!isMobile && (
145
- <>
146
- : <strong>{paneNode.title}</strong>
147
- </>
148
- )}
149
- </button>
150
- <button
151
- onClick={() => setSaveMode(PaneConfigMode.SLUG)}
152
- className={buttonClass}
153
- >
154
- Slug
155
- {!isMobile && (
156
- <>
157
- : <strong>{paneNode.slug}</strong>
158
- </>
159
- )}
160
- </button>
141
+ {$contentMap.some(
142
+ (item) => item.type === 'Pane' && item.id === nodeId
143
+ ) && (
144
+ <button
145
+ onClick={() => setSaveMode(PaneConfigMode.TITLE)}
146
+ className={buttonClass}
147
+ >
148
+ ID
149
+ </button>
150
+ )}
161
151
  <button
162
152
  onClick={() => setSaveMode(PaneConfigMode.IMPRESSION)}
163
153
  className={buttonClass}
@@ -4,7 +4,7 @@ import { RadioGroup } from '@ark-ui/react/radio-group';
4
4
  import CheckCircleIcon from '@heroicons/react/20/solid/CheckCircleIcon';
5
5
  import CubeTransparentIcon from '@heroicons/react/24/outline/CubeTransparentIcon';
6
6
  import DocumentIcon from '@heroicons/react/24/outline/DocumentIcon';
7
- import NewspaperIcon from '@heroicons/react/24/outline/NewspaperIcon';
7
+ //import NewspaperIcon from '@heroicons/react/24/outline/NewspaperIcon';
8
8
  import ExclamationTriangleIcon from '@heroicons/react/24/outline/ExclamationTriangleIcon';
9
9
  import AddPanePanel from './AddPanePanel';
10
10
  import PageCreationGen from './PageGen';
@@ -75,21 +75,21 @@ export const PageCreationSelector = ({
75
75
  : []),
76
76
  ];
77
77
 
78
- const featuredMode = {
79
- id: 'featured',
80
- name: 'Featured Content home page',
81
- description:
82
- 'A layout with a prominent hero section showcasing a featured article and grid of additional top articles',
83
- icon: NewspaperIcon,
84
- active: true,
85
- disabled: validPagesCount < 3,
86
- disabledReason:
87
- validPagesCount === 0
88
- ? 'Not yet available; no pages with SEO metadata found.'
89
- : `Not yet available; requires at least 3 pages with SEO metadata (currently ${validPagesCount}).`,
90
- };
91
-
92
- return [...baseModesWithoutFeature, featuredMode] as CreationMode[];
78
+ //const featuredMode = {
79
+ // id: 'featured',
80
+ // name: 'Featured Content home page',
81
+ // description:
82
+ // 'A layout with a prominent hero section showcasing a featured article and grid of additional top articles',
83
+ // icon: NewspaperIcon,
84
+ // active: true,
85
+ // disabled: validPagesCount < 3,
86
+ // disabledReason:
87
+ // validPagesCount === 0
88
+ // ? 'Not yet available; no pages with SEO metadata found.'
89
+ // : `Not yet available; requires at least 3 pages with SEO metadata (currently ${validPagesCount}).`,
90
+ //};
91
+
92
+ return [...baseModesWithoutFeature /*, featuredMode */] as CreationMode[];
93
93
  }, [validPagesCount]);
94
94
 
95
95
  const handleContinue = () => {
@@ -2,13 +2,13 @@ import { useState } from 'react';
2
2
  import type { ReactNode } from 'react';
3
3
  import { RadioGroup } from '@ark-ui/react/radio-group';
4
4
  import { ulid } from 'ulid';
5
- import FeaturedContentPreview from '@/components/compositor/preview/FeaturedContentPreview';
6
- import ListContentPreview from '@/components/compositor/preview/ListContentPreview';
7
5
  import VisualBreakPreview from '@/components/compositor/preview/VisualBreakPreview';
8
6
  import { getTemplateVisualBreakPane } from '@/utils/compositor/TemplatePanes';
9
7
  import { fullContentMapStore } from '@/stores/storykeep';
10
8
  import type { NodesContext } from '@/stores/nodes';
11
9
  import { findUniqueSlug } from '@/utils/helpers';
10
+ import { tailwindToHex } from '@/utils/compositor/tailwindColors';
11
+ import { SvgBreaks } from '@/constants/shapes';
12
12
  import type { StoryFragmentNode, TemplatePane } from '@/types/compositorTypes';
13
13
 
14
14
  // Layout options with IDs, labels, and descriptions
@@ -95,19 +95,14 @@ const PageCreationSpecial = ({
95
95
  const featuredContentPane: TemplatePane = {
96
96
  id: ulid(),
97
97
  nodeType: 'Pane',
98
- title: 'Featured Content',
99
- slug: findUniqueSlug(`featured-content`, existingSlugs),
98
+ title: 'Featured Article',
99
+ slug: findUniqueSlug(`featured-article`, existingSlugs),
100
100
  isDecorative: false,
101
101
  parentId: nodeId,
102
- codeHookTarget: 'featured-content',
102
+ codeHookTarget: 'featured-article',
103
103
  codeHookPayload: {
104
104
  options: JSON.stringify({
105
- title: 'Featured Content',
106
- featured: true,
107
- slugs: '',
108
- category: '',
109
- limit: '6',
110
- showDate: 'true',
105
+ title: 'Featured Article',
111
106
  }),
112
107
  },
113
108
  };
@@ -128,24 +123,36 @@ const PageCreationSpecial = ({
128
123
  const bgColor = breakVariant?.odd ? 'white' : 'gray-50';
129
124
  const fillColor = breakVariant?.odd ? 'gray-50' : 'white';
130
125
 
126
+ const shapeName = `kCz${selectedBreak}`;
127
+ const isFlipped = SvgBreaks[shapeName]?.flipped || false;
128
+
129
+ const finalBgColor = tailwindToHex(
130
+ isFlipped ? fillColor : bgColor,
131
+ null
132
+ );
133
+ const finalFillColor = tailwindToHex(
134
+ isFlipped ? bgColor : fillColor,
135
+ null
136
+ );
137
+
131
138
  // 2. Create Visual Break pane
132
139
  const visualBreakTemplate = getTemplateVisualBreakPane(selectedBreak);
133
140
  visualBreakTemplate.id = ulid();
134
141
  visualBreakTemplate.title = 'Visual Break';
135
142
  visualBreakTemplate.slug = `${storyfragment.slug}-visual-break`;
136
- visualBreakTemplate.bgColour = bgColor;
143
+ visualBreakTemplate.bgColour = finalBgColor;
137
144
 
138
145
  // Configure the SVG fill color
139
146
  if (visualBreakTemplate.bgPane) {
140
147
  if (visualBreakTemplate.bgPane.type === 'visual-break') {
141
148
  if (visualBreakTemplate.bgPane.breakDesktop) {
142
- visualBreakTemplate.bgPane.breakDesktop.svgFill = fillColor;
149
+ visualBreakTemplate.bgPane.breakDesktop.svgFill = finalFillColor;
143
150
  }
144
151
  if (visualBreakTemplate.bgPane.breakTablet) {
145
- visualBreakTemplate.bgPane.breakTablet.svgFill = fillColor;
152
+ visualBreakTemplate.bgPane.breakTablet.svgFill = finalFillColor;
146
153
  }
147
154
  if (visualBreakTemplate.bgPane.breakMobile) {
148
- visualBreakTemplate.bgPane.breakMobile.svgFill = fillColor;
155
+ visualBreakTemplate.bgPane.breakMobile.svgFill = finalFillColor;
149
156
  }
150
157
  }
151
158
  }
@@ -171,7 +178,10 @@ const PageCreationSpecial = ({
171
178
  isDecorative: false,
172
179
  parentId: nodeId,
173
180
  // For complete-home layout, match the background color with the visual break
174
- bgColour: selectedLayout === 'complete-home' ? 'gray-50' : 'white',
181
+ bgColour: tailwindToHex(
182
+ selectedLayout === 'complete-home' ? 'gray-50' : 'white',
183
+ null
184
+ ),
175
185
  codeHookTarget: 'list-content',
176
186
  codeHookPayload: {
177
187
  options: JSON.stringify({
@@ -299,39 +309,6 @@ const PageCreationSpecial = ({
299
309
  )}
300
310
  </div>
301
311
 
302
- <div className="mt-8 rounded-lg border bg-white p-4 shadow-md">
303
- <h3 className="mb-4 text-lg font-bold">Preview</h3>
304
-
305
- {selectedLayout === 'featured-only' && <FeaturedContentPreview />}
306
-
307
- {selectedLayout === 'featured-list' && (
308
- <div>
309
- <FeaturedContentPreview />
310
- <ListContentPreview bgColour="#ffffff" />
311
- </div>
312
- )}
313
-
314
- {selectedLayout === 'complete-home' && (
315
- <div>
316
- <FeaturedContentPreview />
317
- <VisualBreakPreview
318
- bgColour={
319
- breakVariants.find((b) => b.id === selectedBreak)?.odd
320
- ? '#ffffff'
321
- : '#f1f5f9'
322
- }
323
- fillColour={
324
- breakVariants.find((b) => b.id === selectedBreak)?.odd
325
- ? '#f1f5f9'
326
- : '#ffffff'
327
- }
328
- variant={selectedBreak}
329
- />
330
- <ListContentPreview bgColour="#f1f5f9" />
331
- </div>
332
- )}
333
- </div>
334
-
335
312
  <div className="mt-6 flex justify-end gap-3">
336
313
  <button
337
314
  onClick={() => {
@@ -12,6 +12,8 @@ import {
12
12
  getIntroDesign,
13
13
  getWithArtpackImageDesign,
14
14
  } from '@/utils/compositor/templateMarkdownStyles';
15
+ import { tailwindToHex } from '@/utils/compositor/tailwindColors';
16
+ import { SvgBreaks } from '@/constants/shapes';
15
17
  import {
16
18
  parsePageMarkdown,
17
19
  validatePageMarkdown,
@@ -243,8 +245,21 @@ export const PageCreationPreview = ({
243
245
  ? design.visualBreaks.odd()
244
246
  : design.visualBreaks.even();
245
247
 
246
- breakTemplate.bgColour = aboveColor;
247
- const svgFill = belowColor;
248
+ const breakData = breakTemplate.bgPane?.breakDesktop;
249
+ const shapeName = breakData
250
+ ? `${breakData.collection}${breakData.image}`
251
+ : '';
252
+ const isFlipped =
253
+ (shapeName && SvgBreaks[shapeName]?.flipped) || false;
254
+
255
+ breakTemplate.bgColour = tailwindToHex(
256
+ isFlipped ? belowColor : aboveColor,
257
+ null
258
+ );
259
+ const svgFill = tailwindToHex(
260
+ isFlipped ? aboveColor : belowColor,
261
+ null
262
+ );
248
263
  if (breakTemplate.bgPane) {
249
264
  if (breakTemplate.bgPane.breakDesktop) {
250
265
  breakTemplate.bgPane.breakDesktop.svgFill = svgFill;
@@ -60,7 +60,6 @@ const PaneMagicPathPanel = ({ nodeId, setMode }: PaneMagicPathPanelProps) => {
60
60
  if (!idsResponse.ok) throw new Error('Failed to fetch belief IDs');
61
61
 
62
62
  const idsResult = await idsResponse.json();
63
- // CORRECTED: The key from the backend is "beliefIds", not "beliefs"
64
63
  if (!idsResult.beliefIds || idsResult.beliefIds.length === 0) {
65
64
  setAvailableBeliefs([]);
66
65
  setIsLoading(false);
@@ -74,7 +73,6 @@ const PaneMagicPathPanel = ({ nodeId, setMode }: PaneMagicPathPanelProps) => {
74
73
  'Content-Type': 'application/json',
75
74
  'X-Tenant-ID': tenantId,
76
75
  },
77
- // CORRECTED: Pass the array from the correct key "beliefIds"
78
76
  body: JSON.stringify({ beliefIds: idsResult.beliefIds }),
79
77
  });
80
78
 
@@ -222,7 +220,7 @@ const PaneMagicPathPanel = ({ nodeId, setMode }: PaneMagicPathPanelProps) => {
222
220
  </div>
223
221
 
224
222
  <div className="flex w-full flex-wrap gap-8">
225
- <div className="min-w-[400px] flex-1">
223
+ <div className="flex-1">
226
224
  <MagicPathBuilder
227
225
  paths={heldPaths}
228
226
  setPaths={handleHeldPathsChange}
@@ -232,7 +230,7 @@ const PaneMagicPathPanel = ({ nodeId, setMode }: PaneMagicPathPanelProps) => {
232
230
  />
233
231
  </div>
234
232
 
235
- <div className="min-w-[400px] flex-1">
233
+ <div className="flex-1">
236
234
  <MagicPathBuilder
237
235
  paths={withheldPaths}
238
236
  setPaths={handleWithheldPathsChange}