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.
- package/dist/index.js +6 -32
- package/package.json +1 -1
- package/templates/src/components/codehooks/BunnyVideoSetup.tsx +1 -4
- package/templates/src/components/codehooks/FeaturedArticleSetup.tsx +0 -4
- package/templates/src/components/codehooks/ListContentSetup.tsx +1 -8
- package/templates/src/components/codehooks/ProductCardSetup.tsx +0 -2
- package/templates/src/components/codehooks/ProductGridSetup.tsx +0 -2
- package/templates/src/components/compositor/Compositor.tsx +3 -6
- package/templates/src/components/compositor/Node.tsx +13 -32
- package/templates/src/components/compositor/NodeWithGuid.tsx +49 -5
- package/templates/src/components/compositor/nodes/Pane.tsx +4 -21
- package/templates/src/components/compositor/nodes/Pane_DesignLibrary.tsx +27 -7
- package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag.tsx +3 -1
- package/templates/src/components/compositor/preview/OgImagePreview.tsx +0 -5
- package/templates/src/components/compositor/preview/PaneSnapshotGenerator.tsx +5 -6
- package/templates/src/components/compositor/preview/PanesPreviewGenerator.tsx +1 -0
- package/templates/src/components/edit/PanelSwitch.tsx +3 -24
- package/templates/src/components/edit/SettingsPanel.tsx +0 -1
- package/templates/src/components/edit/ToolMode.tsx +6 -14
- package/templates/src/components/edit/pane/AddPanePanel.tsx +45 -25
- package/templates/src/components/edit/pane/AddPanePanel_new.tsx +2 -8
- package/templates/src/components/edit/pane/AddPanePanel_paste.tsx +111 -0
- package/templates/src/components/edit/pane/RestylePaneModal.tsx +6 -13
- package/templates/src/components/edit/pane/steps/AiDesignStep.tsx +0 -5
- package/templates/src/components/edit/pane/steps/DesignLibraryStep.tsx +4 -11
- package/templates/src/components/edit/panels/StyleBreakPanel.tsx +1 -3
- package/templates/src/components/edit/panels/StyleElementPanel_update.tsx +0 -6
- package/templates/src/components/edit/panels/StyleImagePanel_update.tsx +0 -3
- package/templates/src/components/edit/panels/StyleLiElementPanel_update.tsx +0 -4
- package/templates/src/components/edit/panels/StyleLinkPanel_config.tsx +8 -5
- package/templates/src/components/edit/panels/StyleLinkPanel_update.tsx +1 -2
- package/templates/src/components/edit/panels/StyleParentPanel.tsx +1 -3
- package/templates/src/components/edit/panels/StyleParentPanel_update.tsx +2 -5
- package/templates/src/components/edit/panels/StyleWidgetPanel_config.tsx +2 -8
- package/templates/src/components/edit/panels/StyleWidgetPanel_update.tsx +0 -4
- package/templates/src/components/edit/state/SaveToLibraryModal.tsx +27 -16
- package/templates/src/components/edit/storyfragment/StoryFragmentConfigPanel.tsx +9 -26
- package/templates/src/components/edit/storyfragment/StoryFragmentPanel_og.tsx +7 -16
- package/templates/src/components/edit/storyfragment/StoryFragmentPanel_slug.tsx +5 -6
- package/templates/src/components/edit/widgets/InteractiveDisclosureWidget.tsx +0 -5
- package/templates/src/components/fields/BackgroundImageWrapper.tsx +1 -7
- package/templates/src/components/fields/ColorPickerCombo.tsx +8 -12
- package/templates/src/components/fields/ViewportComboBox.tsx +4 -6
- package/templates/src/stores/nodes.ts +14 -6
- package/templates/src/stores/storykeep.ts +3 -3
- package/templates/src/types/compositorTypes.ts +2 -0
- package/templates/src/utils/compositor/TemplatePanes.ts +0 -76
- package/templates/src/utils/compositor/aiPaneParser.ts +3 -1
- package/templates/src/utils/compositor/designLibraryHelper.ts +240 -17
- package/templates/src/utils/helpers.ts +5 -4
- package/utils/inject-files.ts +6 -32
- package/templates/src/components/compositor/preview/VisualBreakPreview.tsx +0 -154
- package/templates/src/components/edit/pane/PageGen_preview.tsx +0 -511
- package/templates/src/utils/compositor/processMarkdown.ts +0 -445
- package/templates/src/utils/compositor/templateMarkdownStyles.ts +0 -1273
|
@@ -8,7 +8,6 @@ import ColorPickerCombo from './ColorPickerCombo';
|
|
|
8
8
|
import { getCtx } from '@/stores/nodes';
|
|
9
9
|
import { hasArtpacksStore, settingsPanelStore } from '@/stores/storykeep';
|
|
10
10
|
import { cloneDeep } from '@/utils/helpers';
|
|
11
|
-
import type { BrandConfig } from '@/types/tractstack';
|
|
12
11
|
import type {
|
|
13
12
|
BgImageNode,
|
|
14
13
|
ArtpackImageNode,
|
|
@@ -18,7 +17,6 @@ import { isArtpackImageNode } from '@/utils/compositor/typeGuards';
|
|
|
18
17
|
|
|
19
18
|
export interface BackgroundImageWrapperProps {
|
|
20
19
|
paneId: string;
|
|
21
|
-
config?: BrandConfig;
|
|
22
20
|
}
|
|
23
21
|
|
|
24
22
|
const CheckIcon = () => (
|
|
@@ -51,10 +49,7 @@ const ChevronDownIcon = () => (
|
|
|
51
49
|
</svg>
|
|
52
50
|
);
|
|
53
51
|
|
|
54
|
-
const BackgroundImageWrapper = ({
|
|
55
|
-
paneId,
|
|
56
|
-
config,
|
|
57
|
-
}: BackgroundImageWrapperProps) => {
|
|
52
|
+
const BackgroundImageWrapper = ({ paneId }: BackgroundImageWrapperProps) => {
|
|
58
53
|
const ctx = getCtx();
|
|
59
54
|
const allNodes = useStore(ctx.allNodes);
|
|
60
55
|
const $artpacks = useStore(hasArtpacksStore);
|
|
@@ -165,7 +160,6 @@ const BackgroundImageWrapper = ({
|
|
|
165
160
|
title="Pane Background Color"
|
|
166
161
|
defaultColor={(allNodes.get(paneId) as PaneNode)?.bgColour || ''}
|
|
167
162
|
onColorChange={handleColorChange}
|
|
168
|
-
config={config!}
|
|
169
163
|
allowNull={true}
|
|
170
164
|
/>
|
|
171
165
|
{!bgNode && (
|
|
@@ -12,12 +12,11 @@ import {
|
|
|
12
12
|
findClosestTailwindColor,
|
|
13
13
|
getComputedColor,
|
|
14
14
|
} from '@/utils/compositor/tailwindColors';
|
|
15
|
+
import { brandConfigStore } from '@/stores/storykeep';
|
|
15
16
|
import { debounce, useDropdownDirection } from '@/utils/helpers';
|
|
16
|
-
import type { BrandConfig } from '@/types/tractstack';
|
|
17
17
|
|
|
18
18
|
export interface ColorPickerProps {
|
|
19
19
|
title: string;
|
|
20
|
-
config: BrandConfig;
|
|
21
20
|
defaultColor: string;
|
|
22
21
|
onColorChange: (color: string) => void;
|
|
23
22
|
skipTailwind?: boolean;
|
|
@@ -28,14 +27,14 @@ const ColorPickerCombo = ({
|
|
|
28
27
|
title,
|
|
29
28
|
defaultColor,
|
|
30
29
|
onColorChange,
|
|
31
|
-
config,
|
|
32
30
|
skipTailwind = false,
|
|
33
31
|
allowNull = false,
|
|
34
32
|
}: ColorPickerProps) => {
|
|
33
|
+
const brandColors = brandConfigStore.get()?.BRAND_COLOURS || '';
|
|
35
34
|
const [hexColor, setHexColor] = useState(defaultColor);
|
|
36
35
|
const initialTailwindColor = skipTailwind
|
|
37
36
|
? ''
|
|
38
|
-
: hexToTailwind(defaultColor,
|
|
37
|
+
: hexToTailwind(defaultColor, brandColors) || '';
|
|
39
38
|
const [selectedTailwindColor, setSelectedTailwindColor] =
|
|
40
39
|
useState(initialTailwindColor);
|
|
41
40
|
const [query, setQuery] = useState('');
|
|
@@ -79,10 +78,7 @@ const ColorPickerCombo = ({
|
|
|
79
78
|
setHexColor(computedColor);
|
|
80
79
|
|
|
81
80
|
if (!skipTailwind) {
|
|
82
|
-
const exactTailwindColor = hexToTailwind(
|
|
83
|
-
computedColor,
|
|
84
|
-
config.BRAND_COLOURS
|
|
85
|
-
);
|
|
81
|
+
const exactTailwindColor = hexToTailwind(computedColor, brandColors);
|
|
86
82
|
if (exactTailwindColor) {
|
|
87
83
|
setSelectedTailwindColor(exactTailwindColor);
|
|
88
84
|
setQuery('');
|
|
@@ -101,7 +97,7 @@ const ColorPickerCombo = ({
|
|
|
101
97
|
|
|
102
98
|
onColorChange(computedColor);
|
|
103
99
|
}, 16),
|
|
104
|
-
[onColorChange, skipTailwind,
|
|
100
|
+
[onColorChange, skipTailwind, brandColors]
|
|
105
101
|
);
|
|
106
102
|
|
|
107
103
|
// Handle Tailwind color selection
|
|
@@ -114,12 +110,12 @@ const ColorPickerCombo = ({
|
|
|
114
110
|
setQuery(''); // Clear query after selection
|
|
115
111
|
|
|
116
112
|
const newHexColor = getComputedColor(
|
|
117
|
-
tailwindToHex(`bg-${newTailwindColor}`,
|
|
113
|
+
tailwindToHex(`bg-${newTailwindColor}`, brandColors || null)
|
|
118
114
|
);
|
|
119
115
|
setHexColor(newHexColor);
|
|
120
116
|
onColorChange(newHexColor);
|
|
121
117
|
},
|
|
122
|
-
[onColorChange,
|
|
118
|
+
[onColorChange, brandColors, skipTailwind]
|
|
123
119
|
);
|
|
124
120
|
|
|
125
121
|
// New function to handle color removal
|
|
@@ -259,7 +255,7 @@ const ColorPickerCombo = ({
|
|
|
259
255
|
style={{
|
|
260
256
|
backgroundColor: tailwindToHex(
|
|
261
257
|
`bg-${color}`,
|
|
262
|
-
|
|
258
|
+
brandColors
|
|
263
259
|
),
|
|
264
260
|
}}
|
|
265
261
|
/>
|
|
@@ -13,9 +13,8 @@ import DevicePhoneMobileIcon from '@heroicons/react/24/outline/DevicePhoneMobile
|
|
|
13
13
|
import DeviceTabletIcon from '@heroicons/react/24/outline/DeviceTabletIcon';
|
|
14
14
|
import ComputerDesktopIcon from '@heroicons/react/24/outline/ComputerDesktopIcon';
|
|
15
15
|
import { classNames } from '@/utils/helpers';
|
|
16
|
-
import { settingsPanelStore } from '@/stores/storykeep';
|
|
16
|
+
import { brandConfigStore, settingsPanelStore } from '@/stores/storykeep';
|
|
17
17
|
import { tailwindToHex, colorValues } from '@/utils/compositor/tailwindColors';
|
|
18
|
-
import type { BrandConfig } from '@/types/tractstack';
|
|
19
18
|
|
|
20
19
|
interface ViewportComboBoxProps {
|
|
21
20
|
value: string;
|
|
@@ -29,7 +28,6 @@ interface ViewportComboBoxProps {
|
|
|
29
28
|
allowNegative?: boolean;
|
|
30
29
|
isNegative?: boolean;
|
|
31
30
|
isInferred?: boolean;
|
|
32
|
-
config: BrandConfig;
|
|
33
31
|
}
|
|
34
32
|
|
|
35
33
|
const ViewportComboBox = ({
|
|
@@ -40,8 +38,8 @@ const ViewportComboBox = ({
|
|
|
40
38
|
allowNegative = false,
|
|
41
39
|
isNegative = false,
|
|
42
40
|
isInferred = false,
|
|
43
|
-
config,
|
|
44
41
|
}: ViewportComboBoxProps) => {
|
|
42
|
+
const brandColors = brandConfigStore.get()?.BRAND_COLOURS || '';
|
|
45
43
|
const [internalValue, setInternalValue] = useState(value);
|
|
46
44
|
const [query, setQuery] = useState('');
|
|
47
45
|
const [isNowNegative, setIsNowNegative] = useState(isNegative);
|
|
@@ -174,7 +172,7 @@ const ViewportComboBox = ({
|
|
|
174
172
|
style={{
|
|
175
173
|
backgroundColor: tailwindToHex(
|
|
176
174
|
internalValue,
|
|
177
|
-
|
|
175
|
+
brandColors
|
|
178
176
|
),
|
|
179
177
|
}}
|
|
180
178
|
/>
|
|
@@ -218,7 +216,7 @@ const ViewportComboBox = ({
|
|
|
218
216
|
style={{
|
|
219
217
|
backgroundColor: tailwindToHex(
|
|
220
218
|
item,
|
|
221
|
-
|
|
219
|
+
brandColors
|
|
222
220
|
),
|
|
223
221
|
}}
|
|
224
222
|
/>
|
|
@@ -103,7 +103,6 @@ export class NodesContext {
|
|
|
103
103
|
toolAddModeStore = map<{ value: ToolAddMode }>({
|
|
104
104
|
value: 'p',
|
|
105
105
|
});
|
|
106
|
-
showGuids = atom<boolean>(false);
|
|
107
106
|
|
|
108
107
|
/**
|
|
109
108
|
* Sets an edit lock on a specific node to prevent re-renders during editing
|
|
@@ -1784,10 +1783,15 @@ export class NodesContext {
|
|
|
1784
1783
|
ownerId
|
|
1785
1784
|
);
|
|
1786
1785
|
|
|
1787
|
-
// Remove bgPane from the pane object if it exists, as it's now a separate node
|
|
1788
1786
|
if (duplicatedPane.bgPane) {
|
|
1789
1787
|
delete duplicatedPane.bgPane;
|
|
1790
1788
|
}
|
|
1789
|
+
if (duplicatedPane.markdown) {
|
|
1790
|
+
delete duplicatedPane.markdown;
|
|
1791
|
+
}
|
|
1792
|
+
if (duplicatedPane.gridLayout) {
|
|
1793
|
+
delete duplicatedPane.gridLayout;
|
|
1794
|
+
}
|
|
1791
1795
|
|
|
1792
1796
|
this.addNode(duplicatedPane as PaneNode);
|
|
1793
1797
|
this.addNodes(allNodes);
|
|
@@ -1837,11 +1841,15 @@ export class NodesContext {
|
|
|
1837
1841
|
duplicatedPaneId
|
|
1838
1842
|
);
|
|
1839
1843
|
|
|
1840
|
-
// Remove bgPane from the pane object if it exists, as it's now a separate node
|
|
1841
|
-
// This preserves the original logic
|
|
1842
1844
|
if (duplicatedPane.bgPane) {
|
|
1843
1845
|
delete duplicatedPane.bgPane;
|
|
1844
1846
|
}
|
|
1847
|
+
if (duplicatedPane.markdown) {
|
|
1848
|
+
delete duplicatedPane.markdown;
|
|
1849
|
+
}
|
|
1850
|
+
if (duplicatedPane.gridLayout) {
|
|
1851
|
+
delete duplicatedPane.gridLayout;
|
|
1852
|
+
}
|
|
1845
1853
|
|
|
1846
1854
|
const storyFragmentNode = ownerNode as StoryFragmentNode;
|
|
1847
1855
|
let specificIdx = -1;
|
|
@@ -2009,7 +2017,7 @@ export class NodesContext {
|
|
|
2009
2017
|
|
|
2010
2018
|
let autoCreatedMarkdownNode: MarkdownPaneFragmentNode | null = null;
|
|
2011
2019
|
|
|
2012
|
-
console.log(`--- [TRAP - TEMPLATE BEFORE] ---`, cloneDeep(node));
|
|
2020
|
+
//console.log(`--- [TRAP - TEMPLATE BEFORE] ---`, cloneDeep(node));
|
|
2013
2021
|
// 3. HANDLE EMPTY PANE BY AUTO-CREATING A MARKDOWN NODE
|
|
2014
2022
|
if (targetNode.nodeType === 'Pane') {
|
|
2015
2023
|
// Create a minimal markdown node to act as the container
|
|
@@ -2087,7 +2095,7 @@ export class NodesContext {
|
|
|
2087
2095
|
);
|
|
2088
2096
|
}
|
|
2089
2097
|
|
|
2090
|
-
console.log(`--- [TRAP - FLATTENED AFTER] ---`, cloneDeep(flattenedNodes));
|
|
2098
|
+
//console.log(`--- [TRAP - FLATTENED AFTER] ---`, cloneDeep(flattenedNodes));
|
|
2091
2099
|
|
|
2092
2100
|
// 5. PERFORM REMAINING STATE MUTATIONS
|
|
2093
2101
|
if (originalPaneNode) {
|
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
FullContentMapItem,
|
|
7
7
|
Theme,
|
|
8
8
|
ArtpacksStore,
|
|
9
|
+
BrandConfig,
|
|
9
10
|
} from '@/types/tractstack';
|
|
10
11
|
import type { SettingsPanelSignal, ViewportKey } from '@/types/compositorTypes';
|
|
11
12
|
import type {
|
|
@@ -21,9 +22,6 @@ export const fullContentMapStore = atom<FullContentMapItem[]>([]);
|
|
|
21
22
|
export const hasArtpacksStore = map<ArtpacksStore>({});
|
|
22
23
|
export const urlParamsStore = atom<Record<string, string | boolean>>({});
|
|
23
24
|
export const canonicalURLStore = atom<string>('');
|
|
24
|
-
export const brandColourStore = atom<string>(
|
|
25
|
-
'10120d,fcfcfc,f58333,c8df8c,293f58,a7b1b7,393d34,e3e3e3'
|
|
26
|
-
);
|
|
27
25
|
export const preferredThemeStore = atom<Theme>('light');
|
|
28
26
|
|
|
29
27
|
export const hasAssemblyAIStore = atom<boolean>(false);
|
|
@@ -156,6 +154,8 @@ export const resetStoryKeepState = () => {
|
|
|
156
154
|
canRedoStore.set(false);
|
|
157
155
|
};
|
|
158
156
|
|
|
157
|
+
export const brandConfigStore = atom<BrandConfig | null>(null);
|
|
158
|
+
|
|
159
159
|
export const settingsPanelStore = atom<SettingsPanelSignal | null>(null);
|
|
160
160
|
export const stylePanelTargetMemoryStore = atom<Map<string, number>>(
|
|
161
161
|
new Map<string, number>()
|
|
@@ -43,6 +43,7 @@ export enum PaneAddMode {
|
|
|
43
43
|
BREAK = 'BREAK',
|
|
44
44
|
REUSE = 'REUSE',
|
|
45
45
|
CODEHOOK = 'CODEHOOK',
|
|
46
|
+
PASTE = 'PASTE',
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
export enum PaneConfigMode {
|
|
@@ -478,6 +479,7 @@ export type StorageGridLayoutNode = {
|
|
|
478
479
|
tablet: number;
|
|
479
480
|
desktop: number;
|
|
480
481
|
};
|
|
482
|
+
nodes?: StorageMarkdown[];
|
|
481
483
|
};
|
|
482
484
|
|
|
483
485
|
export type StoragePane = Omit<
|
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
//TemplateAsideNode,
|
|
3
|
-
TemplateH2Node,
|
|
4
|
-
TemplateH3Node,
|
|
5
|
-
TemplatePNode,
|
|
6
|
-
} from './TemplateNodes';
|
|
7
|
-
import { getTemplateSimpleMarkdown } from './TemplateMarkdowns';
|
|
8
|
-
import { getColor, tailwindToHex } from './tailwindColors';
|
|
9
1
|
import type { TemplatePane } from '@/types/compositorTypes';
|
|
10
|
-
import type { Theme } from '@/types/tractstack';
|
|
11
2
|
|
|
12
3
|
export const getTemplateVisualBreakPane = (variant: string) => {
|
|
13
4
|
// colour will be set on insert based on adjacent nodes
|
|
@@ -31,70 +22,3 @@ export const getTemplateVisualBreakPane = (variant: string) => {
|
|
|
31
22
|
},
|
|
32
23
|
} as TemplatePane;
|
|
33
24
|
};
|
|
34
|
-
|
|
35
|
-
export const getTemplateSimplePane = (
|
|
36
|
-
theme: Theme,
|
|
37
|
-
brand: string,
|
|
38
|
-
useOdd: boolean = false
|
|
39
|
-
) => {
|
|
40
|
-
return {
|
|
41
|
-
nodeType: 'Pane',
|
|
42
|
-
title: '',
|
|
43
|
-
slug: '',
|
|
44
|
-
bgColour: tailwindToHex(
|
|
45
|
-
getColor(
|
|
46
|
-
{
|
|
47
|
-
light: !useOdd ? 'brand-2' : 'white',
|
|
48
|
-
'light-bw': !useOdd ? 'white' : 'brand-2',
|
|
49
|
-
'light-bold': !useOdd ? 'brand-2' : 'white',
|
|
50
|
-
dark: !useOdd ? 'black' : 'brand-1',
|
|
51
|
-
'dark-bw': !useOdd ? 'black' : 'brand-1',
|
|
52
|
-
'dark-bold': !useOdd ? 'brand-1' : 'black',
|
|
53
|
-
},
|
|
54
|
-
theme
|
|
55
|
-
),
|
|
56
|
-
brand
|
|
57
|
-
),
|
|
58
|
-
markdown: {
|
|
59
|
-
...getTemplateSimpleMarkdown(theme),
|
|
60
|
-
nodes: [
|
|
61
|
-
{ ...TemplateH2Node, copy: 'H2 node in simple pane' },
|
|
62
|
-
{ ...TemplatePNode, copy: 'P node in simple pane' },
|
|
63
|
-
{ ...TemplateH3Node, copy: 'H3 node in simple pane' },
|
|
64
|
-
//{ ...TemplateAsideNode, copy: "Aside node in simple pane" },
|
|
65
|
-
],
|
|
66
|
-
},
|
|
67
|
-
} as TemplatePane;
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
export const getTemplateMarkdownPane = (
|
|
71
|
-
theme: Theme,
|
|
72
|
-
variant: string,
|
|
73
|
-
brand: string,
|
|
74
|
-
useOdd: boolean = false
|
|
75
|
-
) => {
|
|
76
|
-
console.log(`variant: ${variant}`);
|
|
77
|
-
return {
|
|
78
|
-
nodeType: 'Pane',
|
|
79
|
-
title: '',
|
|
80
|
-
slug: '',
|
|
81
|
-
bgColour: tailwindToHex(
|
|
82
|
-
getColor(
|
|
83
|
-
{
|
|
84
|
-
light: !useOdd ? 'brand-2' : 'white',
|
|
85
|
-
'light-bw': !useOdd ? 'white' : 'brand-2',
|
|
86
|
-
'light-bold': !useOdd ? 'brand-2' : 'white',
|
|
87
|
-
dark: !useOdd ? 'black' : 'brand-1',
|
|
88
|
-
'dark-bw': !useOdd ? 'black' : 'brand-1',
|
|
89
|
-
'dark-bold': !useOdd ? 'brand-1' : 'black',
|
|
90
|
-
},
|
|
91
|
-
theme
|
|
92
|
-
),
|
|
93
|
-
brand
|
|
94
|
-
),
|
|
95
|
-
markdown: {
|
|
96
|
-
...getTemplateSimpleMarkdown(theme),
|
|
97
|
-
markdownBody: `## add a catchy title here\n\nyour story continues... and continues... and continues... and continues... and continues... and continues... with nice layout and typography.\n\n[Try it now!](try) [Learn more](learn)\n`,
|
|
98
|
-
},
|
|
99
|
-
} as TemplatePane;
|
|
100
|
-
};
|
|
@@ -642,7 +642,7 @@ export const parseAiPane = (
|
|
|
642
642
|
): Omit<TemplatePane, 'nodes'> & {
|
|
643
643
|
nodes?: (TemplateMarkdown | GridLayoutNode)[];
|
|
644
644
|
} => {
|
|
645
|
-
console.log('--- ENTERING parseAiPane ---', { shellJson, copyHtml, layout });
|
|
645
|
+
//console.log('--- ENTERING parseAiPane ---', { shellJson, copyHtml, layout });
|
|
646
646
|
|
|
647
647
|
const shell: ShellJson = JSON.parse(shellJson);
|
|
648
648
|
const paneId = ulid();
|
|
@@ -697,6 +697,7 @@ export const parseAiPane = (
|
|
|
697
697
|
isDecorative: false,
|
|
698
698
|
gridLayout: gridLayoutNode,
|
|
699
699
|
};
|
|
700
|
+
/*
|
|
700
701
|
console.log({
|
|
701
702
|
shellDefaults: shellDefaults,
|
|
702
703
|
transformedParentClasses: transformedParentClasses,
|
|
@@ -704,6 +705,7 @@ export const parseAiPane = (
|
|
|
704
705
|
gridLayoutNode: gridLayoutNode,
|
|
705
706
|
templatePane: templatePane,
|
|
706
707
|
});
|
|
708
|
+
*/
|
|
707
709
|
return templatePane;
|
|
708
710
|
}
|
|
709
711
|
|
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
import { ulid } from 'ulid';
|
|
2
2
|
import { getCtx, type NodesContext } from '@/stores/nodes';
|
|
3
3
|
import { tailwindClasses } from '@/utils/compositor/tailwindClasses';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
4
|
+
import type {
|
|
5
|
+
PaneNode,
|
|
6
|
+
FlatNode,
|
|
7
|
+
MarkdownPaneFragmentNode,
|
|
8
|
+
GridLayoutNode,
|
|
9
|
+
StoragePane,
|
|
10
|
+
StorageNode,
|
|
11
|
+
StorageMarkdown,
|
|
12
|
+
StorageBgPane,
|
|
13
|
+
StorageGridLayoutNode,
|
|
14
|
+
ArtpackImageNode,
|
|
15
|
+
BgImageNode,
|
|
16
|
+
VisualBreakNode,
|
|
17
|
+
TemplatePane,
|
|
18
|
+
TemplateNode,
|
|
17
19
|
} from '@/types/compositorTypes';
|
|
18
20
|
import type {
|
|
19
21
|
BrandConfig,
|
|
@@ -108,14 +110,14 @@ export async function savePaneToLibrary(
|
|
|
108
110
|
category: string;
|
|
109
111
|
copyMode: CopyMode;
|
|
110
112
|
}
|
|
111
|
-
): Promise<
|
|
113
|
+
): Promise<BrandConfigState | null> {
|
|
112
114
|
const ctx = getCtx();
|
|
113
115
|
const { title, category, copyMode } = formData;
|
|
114
116
|
const paneNode = ctx.allNodes.get().get(paneId) as PaneNode;
|
|
115
117
|
|
|
116
118
|
if (!paneNode) {
|
|
117
119
|
console.error('savePaneToLibrary: PaneNode not found.');
|
|
118
|
-
return
|
|
120
|
+
return null;
|
|
119
121
|
}
|
|
120
122
|
|
|
121
123
|
const childNodes = ctx
|
|
@@ -208,10 +210,10 @@ export async function savePaneToLibrary(
|
|
|
208
210
|
|
|
209
211
|
try {
|
|
210
212
|
await saveBrandConfig(tenantId, backendDTO);
|
|
211
|
-
return
|
|
213
|
+
return updatedState;
|
|
212
214
|
} catch (error) {
|
|
213
215
|
console.error('Failed to save design library:', error);
|
|
214
|
-
return
|
|
216
|
+
return null;
|
|
215
217
|
}
|
|
216
218
|
}
|
|
217
219
|
|
|
@@ -417,3 +419,224 @@ export function convertTemplateToAIShell(template: TemplatePane): string {
|
|
|
417
419
|
|
|
418
420
|
return JSON.stringify(shell, null, 2);
|
|
419
421
|
}
|
|
422
|
+
|
|
423
|
+
export async function copyPaneToClipboard(paneId: string): Promise<boolean> {
|
|
424
|
+
const ctx = getCtx();
|
|
425
|
+
const paneNode = ctx.allNodes.get().get(paneId) as PaneNode;
|
|
426
|
+
|
|
427
|
+
const storagePane = convertLivePaneToStoragePane(paneId, ctx, {
|
|
428
|
+
title: paneNode?.title || 'Pasted Pane',
|
|
429
|
+
copyMode: 'retain',
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
if (!storagePane) {
|
|
433
|
+
return false;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
try {
|
|
437
|
+
const jsonPayload = JSON.stringify(storagePane, null, 2);
|
|
438
|
+
await navigator.clipboard.writeText(jsonPayload);
|
|
439
|
+
return true;
|
|
440
|
+
} catch (error) {
|
|
441
|
+
console.error('Failed to copy pane to clipboard:', error);
|
|
442
|
+
return false;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
function buildIdMap(node: any, map: Map<string, string>) {
|
|
447
|
+
if (!node || typeof node !== 'object') return;
|
|
448
|
+
|
|
449
|
+
if (node.id && !map.has(node.id)) {
|
|
450
|
+
map.set(node.id, ulid());
|
|
451
|
+
}
|
|
452
|
+
// Markdown nodes have a second unique identifier
|
|
453
|
+
if (node.markdownId && !map.has(node.markdownId)) {
|
|
454
|
+
map.set(node.markdownId, ulid());
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// Recursively traverse all possible child arrays/objects
|
|
458
|
+
if (node.markdowns) {
|
|
459
|
+
node.markdowns.forEach((n: any) => buildIdMap(n, map));
|
|
460
|
+
}
|
|
461
|
+
if (node.gridLayout) {
|
|
462
|
+
buildIdMap(node.gridLayout, map);
|
|
463
|
+
}
|
|
464
|
+
if (node.nodes) {
|
|
465
|
+
node.nodes.forEach((n: any) => buildIdMap(n, map));
|
|
466
|
+
}
|
|
467
|
+
if (node.bgPane) {
|
|
468
|
+
buildIdMap(node.bgPane, map);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
function applyIdMap(node: any, map: Map<string, string>) {
|
|
473
|
+
if (!node || typeof node !== 'object') return;
|
|
474
|
+
|
|
475
|
+
if (node.id && map.has(node.id)) {
|
|
476
|
+
node.id = map.get(node.id);
|
|
477
|
+
}
|
|
478
|
+
if (node.parentId && map.has(node.parentId)) {
|
|
479
|
+
node.parentId = map.get(node.parentId);
|
|
480
|
+
}
|
|
481
|
+
if (node.markdownId && map.has(node.markdownId)) {
|
|
482
|
+
node.markdownId = map.get(node.markdownId);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// Recursively traverse all possible child arrays/objects
|
|
486
|
+
if (node.markdowns) {
|
|
487
|
+
node.markdowns.forEach((n: any) => applyIdMap(n, map));
|
|
488
|
+
}
|
|
489
|
+
if (node.gridLayout) {
|
|
490
|
+
applyIdMap(node.gridLayout, map);
|
|
491
|
+
}
|
|
492
|
+
if (node.nodes) {
|
|
493
|
+
node.nodes.forEach((n: any) => applyIdMap(n, map));
|
|
494
|
+
}
|
|
495
|
+
if (node.bgPane) {
|
|
496
|
+
applyIdMap(node.bgPane, map);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
export function remapPaneIds(pane: StoragePane): StoragePane {
|
|
501
|
+
const idMap = new Map<string, string>();
|
|
502
|
+
// The input object may have come from JSON.parse, so we treat it as 'any' internally
|
|
503
|
+
const clone = JSON.parse(JSON.stringify(pane as any));
|
|
504
|
+
|
|
505
|
+
// First pass: Traverse the entire structure to build a complete map of old IDs to new IDs.
|
|
506
|
+
buildIdMap(clone, idMap);
|
|
507
|
+
|
|
508
|
+
// Second pass: Traverse again to apply the new IDs, ensuring parent-child relationships are correct.
|
|
509
|
+
applyIdMap(clone, idMap);
|
|
510
|
+
|
|
511
|
+
return clone as StoragePane;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
function convertLivePaneToStoragePane(
|
|
515
|
+
paneId: string,
|
|
516
|
+
ctx: NodesContext,
|
|
517
|
+
options: {
|
|
518
|
+
title: string;
|
|
519
|
+
copyMode: CopyMode;
|
|
520
|
+
}
|
|
521
|
+
): StoragePane | null {
|
|
522
|
+
const paneNode = ctx.allNodes.get().get(paneId) as PaneNode;
|
|
523
|
+
if (!paneNode) {
|
|
524
|
+
console.error('convertLivePaneToStoragePane: PaneNode not found.');
|
|
525
|
+
return null;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
const { title, copyMode } = options;
|
|
529
|
+
const childNodes = ctx
|
|
530
|
+
.getChildNodeIDs(paneId)
|
|
531
|
+
.map((id) => ctx.allNodes.get().get(id));
|
|
532
|
+
|
|
533
|
+
const markdownNode = childNodes.find((n) => n?.nodeType === 'Markdown') as
|
|
534
|
+
| MarkdownPaneFragmentNode
|
|
535
|
+
| undefined;
|
|
536
|
+
|
|
537
|
+
const gridLayoutNode = childNodes.find(
|
|
538
|
+
(n) => n?.nodeType === 'GridLayoutNode'
|
|
539
|
+
) as GridLayoutNode | undefined;
|
|
540
|
+
|
|
541
|
+
const bgPaneNode = childNodes.find((n) => n?.nodeType === 'BgPane') as
|
|
542
|
+
| ArtpackImageNode
|
|
543
|
+
| BgImageNode
|
|
544
|
+
| VisualBreakNode
|
|
545
|
+
| undefined;
|
|
546
|
+
|
|
547
|
+
let storageMarkdown: StorageMarkdown | undefined;
|
|
548
|
+
let storageGridLayout: StorageGridLayoutNode | undefined;
|
|
549
|
+
|
|
550
|
+
if (markdownNode) {
|
|
551
|
+
storageMarkdown = {
|
|
552
|
+
nodeType: 'Markdown',
|
|
553
|
+
type: 'markdown',
|
|
554
|
+
defaultClasses: markdownNode.defaultClasses || {},
|
|
555
|
+
parentClasses: markdownNode.parentClasses || [],
|
|
556
|
+
nodes:
|
|
557
|
+
copyMode !== 'blank'
|
|
558
|
+
? ctx
|
|
559
|
+
.getChildNodeIDs(markdownNode.id)
|
|
560
|
+
.map((childId) => {
|
|
561
|
+
const childNode = ctx.allNodes.get().get(childId) as FlatNode;
|
|
562
|
+
return convertLiveNodeToStorageNode(childNode, ctx, copyMode);
|
|
563
|
+
})
|
|
564
|
+
.filter((n): n is StorageNode => n !== null)
|
|
565
|
+
: [],
|
|
566
|
+
};
|
|
567
|
+
} else if (gridLayoutNode) {
|
|
568
|
+
const { id, parentId, isChanged, ...restOfGrid } = gridLayoutNode;
|
|
569
|
+
storageGridLayout = {
|
|
570
|
+
...restOfGrid,
|
|
571
|
+
nodes: ctx
|
|
572
|
+
.getChildNodeIDs(gridLayoutNode.id)
|
|
573
|
+
.map((columnId) => {
|
|
574
|
+
const columnNode = ctx.allNodes
|
|
575
|
+
.get()
|
|
576
|
+
.get(columnId) as MarkdownPaneFragmentNode;
|
|
577
|
+
if (!columnNode) return null;
|
|
578
|
+
|
|
579
|
+
const {
|
|
580
|
+
id,
|
|
581
|
+
parentId,
|
|
582
|
+
isChanged,
|
|
583
|
+
markdownId,
|
|
584
|
+
parentCss,
|
|
585
|
+
...restOfColumn
|
|
586
|
+
} = columnNode;
|
|
587
|
+
|
|
588
|
+
const storageColumn: StorageMarkdown = {
|
|
589
|
+
...restOfColumn,
|
|
590
|
+
nodeType: 'Markdown',
|
|
591
|
+
type: 'markdown',
|
|
592
|
+
nodes:
|
|
593
|
+
copyMode !== 'blank'
|
|
594
|
+
? ctx
|
|
595
|
+
.getChildNodeIDs(columnNode.id)
|
|
596
|
+
.map((childId) => {
|
|
597
|
+
const childNode = ctx.allNodes
|
|
598
|
+
.get()
|
|
599
|
+
.get(childId) as FlatNode;
|
|
600
|
+
return convertLiveNodeToStorageNode(
|
|
601
|
+
childNode,
|
|
602
|
+
ctx,
|
|
603
|
+
copyMode
|
|
604
|
+
);
|
|
605
|
+
})
|
|
606
|
+
.filter((n): n is StorageNode => n !== null)
|
|
607
|
+
: [],
|
|
608
|
+
};
|
|
609
|
+
return storageColumn;
|
|
610
|
+
})
|
|
611
|
+
.filter((n): n is StorageMarkdown => n !== null),
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
const storageBgPane: StorageBgPane | undefined = bgPaneNode
|
|
616
|
+
? { ...bgPaneNode }
|
|
617
|
+
: undefined;
|
|
618
|
+
|
|
619
|
+
if (storageBgPane) {
|
|
620
|
+
delete (storageBgPane as any).id;
|
|
621
|
+
delete (storageBgPane as any).parentId;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
const storagePane: StoragePane = {
|
|
625
|
+
nodeType: 'Pane',
|
|
626
|
+
title: title,
|
|
627
|
+
slug: '',
|
|
628
|
+
bgColour: paneNode.bgColour,
|
|
629
|
+
isDecorative: paneNode.isDecorative,
|
|
630
|
+
heightOffsetDesktop: paneNode.heightOffsetDesktop,
|
|
631
|
+
heightOffsetMobile: paneNode.heightOffsetMobile,
|
|
632
|
+
heightOffsetTablet: paneNode.heightOffsetTablet,
|
|
633
|
+
heightRatioDesktop: paneNode.heightRatioDesktop,
|
|
634
|
+
heightRatioMobile: paneNode.heightRatioMobile,
|
|
635
|
+
heightRatioTablet: paneNode.heightRatioTablet,
|
|
636
|
+
...(storageMarkdown ? { markdowns: [storageMarkdown] } : {}),
|
|
637
|
+
...(storageGridLayout ? { gridLayout: storageGridLayout } : {}),
|
|
638
|
+
...(storageBgPane ? { bgPane: storageBgPane } : {}),
|
|
639
|
+
};
|
|
640
|
+
|
|
641
|
+
return storagePane;
|
|
642
|
+
}
|
|
@@ -291,14 +291,15 @@ export function titleToSlug(title: string, maxLength: number = 50): string {
|
|
|
291
291
|
}
|
|
292
292
|
|
|
293
293
|
export function findUniqueSlug(slug: string, existingSlugs: string[]): string {
|
|
294
|
-
|
|
295
|
-
|
|
294
|
+
const tempSlug = slug || `story`;
|
|
295
|
+
if (!existingSlugs.includes(tempSlug)) {
|
|
296
|
+
return tempSlug;
|
|
296
297
|
}
|
|
297
298
|
let counter = 1;
|
|
298
|
-
let newSlug = `${
|
|
299
|
+
let newSlug = `${tempSlug}-${counter}`;
|
|
299
300
|
while (existingSlugs.includes(newSlug)) {
|
|
300
301
|
counter++;
|
|
301
|
-
newSlug = `${
|
|
302
|
+
newSlug = `${tempSlug}-${counter}`;
|
|
302
303
|
}
|
|
303
304
|
return newSlug;
|
|
304
305
|
}
|