astro-tractstack 2.0.17 → 2.0.19
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 +18 -0
- package/package.json +1 -1
- package/templates/src/components/codehooks/FeaturedArticleSetup.tsx +1 -1
- package/templates/src/components/codehooks/ListContentSetup.tsx +1 -1
- package/templates/src/components/compositor/Compositor.tsx +1 -0
- package/templates/src/components/compositor/Node.tsx +41 -17
- package/templates/src/components/compositor/nodes/GhostInsertBlock.tsx +9 -6
- package/templates/src/components/compositor/nodes/GridLayout.tsx +124 -0
- package/templates/src/components/compositor/nodes/GridLayout_eraser.tsx +33 -0
- package/templates/src/components/compositor/nodes/Markdown.tsx +67 -37
- package/templates/src/components/compositor/nodes/Markdown_eraser.tsx +56 -0
- package/templates/src/components/compositor/preview/FeaturedArticlePreview.tsx +8 -2
- package/templates/src/components/edit/PanelSwitch.tsx +232 -75
- package/templates/src/components/edit/SettingsPanel.tsx +0 -1
- package/templates/src/components/edit/pane/AddPanePanel_codehook.tsx +3 -3
- package/templates/src/components/edit/pane/AddPanePanel_new.tsx +402 -167
- package/templates/src/components/edit/pane/AddPanePanel_reuse.tsx +2 -2
- package/templates/src/components/edit/pane/ConfigPanePanel.tsx +1 -7
- package/templates/src/components/edit/pane/PanePanel_impression.tsx +1 -1
- package/templates/src/components/edit/pane/RestylePaneModal.tsx +8 -5
- package/templates/src/components/edit/pane/steps/AiDesignStep.tsx +6 -6
- package/templates/src/components/edit/pane/steps/CopyInputStep.tsx +3 -3
- package/templates/src/components/edit/pane/steps/DesignLibraryStep.tsx +4 -4
- package/templates/src/components/edit/panels/StyleElementPanel.tsx +11 -4
- package/templates/src/components/edit/panels/StyleElementPanel_add.tsx +8 -8
- package/templates/src/components/edit/panels/StyleElementPanel_remove.tsx +14 -4
- package/templates/src/components/edit/panels/StyleElementPanel_update.tsx +16 -4
- package/templates/src/components/edit/panels/StyleImagePanel.tsx +7 -3
- package/templates/src/components/edit/panels/StyleImagePanel_add.tsx +9 -2
- package/templates/src/components/edit/panels/StyleImagePanel_remove.tsx +5 -2
- package/templates/src/components/edit/panels/StyleImagePanel_update.tsx +5 -2
- package/templates/src/components/edit/panels/StyleLiElementPanel.tsx +7 -3
- package/templates/src/components/edit/panels/StyleLiElementPanel_add.tsx +9 -2
- package/templates/src/components/edit/panels/StyleLiElementPanel_remove.tsx +5 -2
- package/templates/src/components/edit/panels/StyleLiElementPanel_update.tsx +5 -2
- package/templates/src/components/edit/panels/StyleParentPanel.tsx +530 -171
- package/templates/src/components/edit/panels/StyleParentPanel_add.tsx +77 -42
- package/templates/src/components/edit/panels/StyleParentPanel_deleteLayer.tsx +38 -22
- package/templates/src/components/edit/panels/StyleParentPanel_remove.tsx +171 -66
- package/templates/src/components/edit/panels/StyleParentPanel_update.tsx +166 -98
- package/templates/src/components/edit/panels/StyleWidgetPanel.tsx +7 -3
- package/templates/src/components/edit/panels/StyleWidgetPanel_add.tsx +9 -2
- package/templates/src/components/edit/panels/StyleWidgetPanel_remove.tsx +5 -2
- package/templates/src/components/edit/panels/StyleWidgetPanel_update.tsx +6 -2
- package/templates/src/components/edit/state/SaveModal.tsx +10 -2
- package/templates/src/components/edit/state/SaveToLibraryModal.tsx +6 -6
- package/templates/src/components/fields/PaneBreakShapeSelector.tsx +1 -1
- package/templates/src/components/widgets/ImpressionWrapper.tsx +4 -1
- package/templates/src/constants/prompts.json +23 -2
- package/templates/src/stores/nodes.ts +356 -212
- package/templates/src/stores/storykeep.ts +3 -1
- package/templates/src/types/compositorTypes.ts +56 -3
- package/templates/src/types/tractstack.ts +1 -0
- package/templates/src/utils/compositor/TemplateNodes.ts +8 -0
- package/templates/src/utils/compositor/aiPaneParser.ts +263 -83
- package/templates/src/utils/compositor/designLibraryHelper.ts +12 -9
- package/templates/src/utils/compositor/nodesHelper.ts +229 -0
- package/templates/src/utils/compositor/reduceNodesClassNames.ts +40 -1
- package/templates/src/utils/compositor/typeGuards.ts +7 -0
- package/templates/src/utils/etl/extractor.ts +1 -5
- package/templates/src/utils/etl/index.ts +1 -0
- package/templates/src/utils/etl/transformer.ts +70 -25
- package/utils/inject-files.ts +18 -0
|
@@ -157,7 +157,9 @@ export const resetStoryKeepState = () => {
|
|
|
157
157
|
};
|
|
158
158
|
|
|
159
159
|
export const settingsPanelStore = atom<SettingsPanelSignal | null>(null);
|
|
160
|
-
|
|
160
|
+
export const stylePanelTargetMemoryStore = atom<Map<string, number>>(
|
|
161
|
+
new Map<string, number>()
|
|
162
|
+
);
|
|
161
163
|
export const styleElementInfoStore = map<{
|
|
162
164
|
markdownParentId: string | null;
|
|
163
165
|
tagName: string | null;
|
|
@@ -76,6 +76,7 @@ export type SettingsPanelSignal = {
|
|
|
76
76
|
minimized?: boolean;
|
|
77
77
|
expanded?: boolean;
|
|
78
78
|
editLock?: number;
|
|
79
|
+
targetProperty?: 'parentClasses' | 'gridClasses';
|
|
79
80
|
};
|
|
80
81
|
|
|
81
82
|
export interface OgImageParams {
|
|
@@ -173,6 +174,7 @@ export type NodeType =
|
|
|
173
174
|
| 'Pane'
|
|
174
175
|
| 'StoryFragment'
|
|
175
176
|
| 'BgPane'
|
|
177
|
+
| 'GridLayoutNode'
|
|
176
178
|
| 'Markdown'
|
|
177
179
|
| 'TagElement'
|
|
178
180
|
| 'TractStack'
|
|
@@ -253,7 +255,12 @@ export interface TractStackNode extends BaseNode {
|
|
|
253
255
|
}
|
|
254
256
|
|
|
255
257
|
export interface PaneFragmentNode extends BaseNode {
|
|
256
|
-
type:
|
|
258
|
+
type:
|
|
259
|
+
| 'markdown'
|
|
260
|
+
| 'visual-break'
|
|
261
|
+
| 'background-image'
|
|
262
|
+
| 'artpack-image'
|
|
263
|
+
| 'grid-layout';
|
|
257
264
|
hiddenViewportMobile?: boolean;
|
|
258
265
|
hiddenViewportTablet?: boolean;
|
|
259
266
|
hiddenViewportDesktop?: boolean;
|
|
@@ -272,6 +279,22 @@ export interface MarkdownPaneFragmentNode extends PaneFragmentNode {
|
|
|
272
279
|
>;
|
|
273
280
|
parentClasses?: ParentClassesPayload;
|
|
274
281
|
parentCss?: string[];
|
|
282
|
+
gridClasses?: DefaultClassValue;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export interface GridLayoutNode extends PaneFragmentNode {
|
|
286
|
+
nodeType: 'GridLayoutNode';
|
|
287
|
+
type: 'grid-layout';
|
|
288
|
+
parentClasses?: ParentClassesPayload;
|
|
289
|
+
defaultClasses?: Record<
|
|
290
|
+
string,
|
|
291
|
+
{
|
|
292
|
+
mobile: Record<string, string>;
|
|
293
|
+
tablet: Record<string, string>;
|
|
294
|
+
desktop: Record<string, string>;
|
|
295
|
+
}
|
|
296
|
+
>;
|
|
297
|
+
gridColumns: { mobile: number; tablet: number; desktop: number };
|
|
275
298
|
}
|
|
276
299
|
|
|
277
300
|
export interface ArtpackImageNode extends PaneFragmentNode {
|
|
@@ -401,10 +424,15 @@ export type TemplateMarkdown = MarkdownPaneFragmentNode & {
|
|
|
401
424
|
markdownBody?: string;
|
|
402
425
|
};
|
|
403
426
|
|
|
427
|
+
export type TemplateGridLayout = GridLayoutNode & {
|
|
428
|
+
nodes?: TemplateMarkdown[];
|
|
429
|
+
};
|
|
430
|
+
|
|
404
431
|
export type TemplatePane = PaneNode & {
|
|
405
432
|
id?: string;
|
|
406
433
|
parentId?: string;
|
|
407
434
|
markdown?: TemplateMarkdown;
|
|
435
|
+
gridLayout?: TemplateGridLayout;
|
|
408
436
|
bgPane?: VisualBreakNode | ArtpackImageNode | BgImageNode;
|
|
409
437
|
};
|
|
410
438
|
|
|
@@ -440,6 +468,18 @@ export type StorageMarkdown = Omit<
|
|
|
440
468
|
nodes?: StorageNode[];
|
|
441
469
|
};
|
|
442
470
|
|
|
471
|
+
export type StorageGridLayoutNode = {
|
|
472
|
+
nodeType: string;
|
|
473
|
+
type: string;
|
|
474
|
+
defaultClasses?: Record<string, any>;
|
|
475
|
+
parentClasses?: Record<string, any>[];
|
|
476
|
+
gridColumns: {
|
|
477
|
+
mobile: number;
|
|
478
|
+
tablet: number;
|
|
479
|
+
desktop: number;
|
|
480
|
+
};
|
|
481
|
+
};
|
|
482
|
+
|
|
443
483
|
export type StoragePane = Omit<
|
|
444
484
|
PaneNode,
|
|
445
485
|
| 'id'
|
|
@@ -453,7 +493,8 @@ export type StoragePane = Omit<
|
|
|
453
493
|
| 'codeHookPayload'
|
|
454
494
|
| 'markdown'
|
|
455
495
|
> & {
|
|
456
|
-
|
|
496
|
+
markdowns?: StorageMarkdown[];
|
|
497
|
+
gridLayout?: StorageGridLayoutNode;
|
|
457
498
|
bgPane?: StorageBgPane;
|
|
458
499
|
};
|
|
459
500
|
|
|
@@ -481,7 +522,7 @@ export type LoadData = {
|
|
|
481
522
|
paneNodes?: PaneNode[];
|
|
482
523
|
tractstackNodes?: TractStackNode[];
|
|
483
524
|
childNodes?: (BaseNode | FlatNode)[];
|
|
484
|
-
paneFragmentNodes?: PaneFragmentNode[];
|
|
525
|
+
paneFragmentNodes?: (PaneFragmentNode | GridLayoutNode)[];
|
|
485
526
|
flatNodes?: FlatNode[];
|
|
486
527
|
impressionNodes?: ImpressionNode[];
|
|
487
528
|
beliefNodes?: BeliefNode[];
|
|
@@ -511,6 +552,18 @@ export interface BasePanelProps {
|
|
|
511
552
|
onTitleChange?: (title: string) => void;
|
|
512
553
|
}
|
|
513
554
|
|
|
555
|
+
export type ParentBasePanelProps = {
|
|
556
|
+
node: MarkdownPaneFragmentNode | GridLayoutNode | null;
|
|
557
|
+
parentNode?: FlatNode | PaneNode;
|
|
558
|
+
config?: BrandConfig | null;
|
|
559
|
+
layer?: number;
|
|
560
|
+
className?: string;
|
|
561
|
+
childId?: string;
|
|
562
|
+
availableCodeHooks?: string[];
|
|
563
|
+
onTitleChange?: (title: string) => void;
|
|
564
|
+
targetProperty?: 'parentClasses' | 'gridClasses';
|
|
565
|
+
};
|
|
566
|
+
|
|
514
567
|
interface WidgetParameterDefinition {
|
|
515
568
|
label: string;
|
|
516
569
|
defaultValue: string;
|
|
@@ -7,6 +7,7 @@ export const TemplateH2Node = {
|
|
|
7
7
|
{
|
|
8
8
|
copy: 'Catchy title',
|
|
9
9
|
tagName: 'text',
|
|
10
|
+
nodeType: 'TagElement',
|
|
10
11
|
},
|
|
11
12
|
],
|
|
12
13
|
} as TemplateNode;
|
|
@@ -18,6 +19,7 @@ export const TemplateH3Node = {
|
|
|
18
19
|
{
|
|
19
20
|
copy: 'Catchy sub-title',
|
|
20
21
|
tagName: 'text',
|
|
22
|
+
nodeType: 'TagElement',
|
|
21
23
|
},
|
|
22
24
|
],
|
|
23
25
|
} as TemplateNode;
|
|
@@ -29,6 +31,7 @@ export const TemplateH4Node = {
|
|
|
29
31
|
{
|
|
30
32
|
copy: 'Catchy sub-title',
|
|
31
33
|
tagName: 'text',
|
|
34
|
+
nodeType: 'TagElement',
|
|
32
35
|
},
|
|
33
36
|
],
|
|
34
37
|
} as TemplateNode;
|
|
@@ -40,6 +43,7 @@ export const TemplatePNode = {
|
|
|
40
43
|
{
|
|
41
44
|
copy: '...',
|
|
42
45
|
tagName: 'text',
|
|
46
|
+
nodeType: 'TagElement',
|
|
43
47
|
},
|
|
44
48
|
],
|
|
45
49
|
} as TemplateNode;
|
|
@@ -51,6 +55,7 @@ export const TemplateOLNode = {
|
|
|
51
55
|
{
|
|
52
56
|
copy: '...',
|
|
53
57
|
tagName: 'text',
|
|
58
|
+
nodeType: 'TagElement',
|
|
54
59
|
},
|
|
55
60
|
],
|
|
56
61
|
} as TemplateNode;
|
|
@@ -62,6 +67,7 @@ export const TemplateULNode = {
|
|
|
62
67
|
{
|
|
63
68
|
copy: '...',
|
|
64
69
|
tagName: 'text',
|
|
70
|
+
nodeType: 'TagElement',
|
|
65
71
|
},
|
|
66
72
|
],
|
|
67
73
|
} as TemplateNode;
|
|
@@ -73,6 +79,7 @@ export const TemplateLINode = {
|
|
|
73
79
|
{
|
|
74
80
|
copy: '...',
|
|
75
81
|
tagName: 'text',
|
|
82
|
+
nodeType: 'TagElement',
|
|
76
83
|
},
|
|
77
84
|
],
|
|
78
85
|
} as TemplateNode;
|
|
@@ -84,6 +91,7 @@ export const TemplateLINode = {
|
|
|
84
91
|
// {
|
|
85
92
|
// copy: "aside node",
|
|
86
93
|
// tagName: "text",
|
|
94
|
+
//nodeType: 'TagElement',
|
|
87
95
|
// },
|
|
88
96
|
// ],
|
|
89
97
|
//} as TemplateNode;
|
|
@@ -7,14 +7,23 @@ import type {
|
|
|
7
7
|
DefaultClasses,
|
|
8
8
|
ResponsiveClasses,
|
|
9
9
|
ButtonPayload,
|
|
10
|
+
GridLayoutNode,
|
|
10
11
|
} from '@/types/compositorTypes';
|
|
11
|
-
import { tailwindClasses } from '@/utils/compositor/tailwindClasses';
|
|
12
12
|
import { isDeepEqual } from '@/utils/helpers';
|
|
13
|
+
import { tailwindClasses } from '@/utils/compositor/tailwindClasses';
|
|
13
14
|
|
|
14
15
|
type LLMShellLayer = {
|
|
15
|
-
mobile?:
|
|
16
|
-
tablet?:
|
|
17
|
-
desktop?:
|
|
16
|
+
mobile?: string;
|
|
17
|
+
tablet?: string;
|
|
18
|
+
desktop?: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
type LLMColumnLayer = {
|
|
22
|
+
gridClasses: {
|
|
23
|
+
mobile?: string;
|
|
24
|
+
tablet?: string;
|
|
25
|
+
desktop?: string;
|
|
26
|
+
};
|
|
18
27
|
};
|
|
19
28
|
|
|
20
29
|
type LLMDefaultClasses = {
|
|
@@ -29,6 +38,7 @@ type ShellJson = {
|
|
|
29
38
|
bgColour: string;
|
|
30
39
|
parentClasses: LLMShellLayer[];
|
|
31
40
|
defaultClasses: LLMDefaultClasses;
|
|
41
|
+
columns?: LLMColumnLayer[];
|
|
32
42
|
};
|
|
33
43
|
|
|
34
44
|
type ParsedNode = {
|
|
@@ -36,12 +46,6 @@ type ParsedNode = {
|
|
|
36
46
|
responsiveClasses: ResponsiveClasses;
|
|
37
47
|
};
|
|
38
48
|
|
|
39
|
-
type ParentClassLayer = {
|
|
40
|
-
mobile: Record<string, string>;
|
|
41
|
-
tablet: Record<string, string>;
|
|
42
|
-
desktop: Record<string, string>;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
49
|
type DefaultClassValue = {
|
|
46
50
|
mobile: Record<string, string>;
|
|
47
51
|
tablet: Record<string, string>;
|
|
@@ -54,7 +58,6 @@ type ClassLookupValue = {
|
|
|
54
58
|
viewport: 'mobile' | 'tablet' | 'desktop';
|
|
55
59
|
};
|
|
56
60
|
|
|
57
|
-
let KEY_NORMALIZATION_LOOKUP: Map<string, string> | null = null;
|
|
58
61
|
let RESPONSIVE_CLASS_LOOKUP: Map<string, ClassLookupValue> | null = null;
|
|
59
62
|
let BUTTON_CLASS_LOOKUP: Map<string, { key: string; value: string }> | null =
|
|
60
63
|
null;
|
|
@@ -74,37 +77,6 @@ const ALLOWED_TAGS = new Set([
|
|
|
74
77
|
'a',
|
|
75
78
|
]);
|
|
76
79
|
|
|
77
|
-
function buildKeyNormalizationLookup(): Map<string, string> {
|
|
78
|
-
if (KEY_NORMALIZATION_LOOKUP) {
|
|
79
|
-
return KEY_NORMALIZATION_LOOKUP;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const keyMap = new Map<string, string>();
|
|
83
|
-
for (const key in tailwindClasses) {
|
|
84
|
-
keyMap.set(key.toLowerCase(), key);
|
|
85
|
-
}
|
|
86
|
-
KEY_NORMALIZATION_LOOKUP = keyMap;
|
|
87
|
-
return keyMap;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function normalizeKeys(
|
|
91
|
-
styleObj: Record<string, string> | undefined
|
|
92
|
-
): Record<string, string> {
|
|
93
|
-
if (!styleObj) return {};
|
|
94
|
-
|
|
95
|
-
const keyMap = buildKeyNormalizationLookup();
|
|
96
|
-
const normalized: Record<string, string> = {};
|
|
97
|
-
|
|
98
|
-
for (const key in styleObj) {
|
|
99
|
-
if (Object.prototype.hasOwnProperty.call(styleObj, key)) {
|
|
100
|
-
const lowerKey = key.toLowerCase();
|
|
101
|
-
const correctKey = keyMap.get(lowerKey);
|
|
102
|
-
normalized[correctKey || key] = styleObj[key];
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
return normalized;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
80
|
function buildResponsiveClassLookup(): Map<string, ClassLookupValue> {
|
|
109
81
|
if (RESPONSIVE_CLASS_LOOKUP) {
|
|
110
82
|
return RESPONSIVE_CLASS_LOOKUP;
|
|
@@ -406,6 +378,121 @@ function findMostCommonClasses(nodes: ParsedNode[]): ResponsiveClasses {
|
|
|
406
378
|
return classMap.get(mostCommonKey) || {};
|
|
407
379
|
}
|
|
408
380
|
|
|
381
|
+
/**
|
|
382
|
+
* Calculates the difference between a node's full style and the default theme for its tag.
|
|
383
|
+
* @returns An object with only the override styles, or undefined if there are no overrides.
|
|
384
|
+
*/
|
|
385
|
+
function calculateOverrides(
|
|
386
|
+
fullStyle: ResponsiveClasses,
|
|
387
|
+
defaultStyle: DefaultClassValue
|
|
388
|
+
): ResponsiveClasses | undefined {
|
|
389
|
+
const overrides: ResponsiveClasses = {};
|
|
390
|
+
const viewports: Array<keyof ResponsiveClasses> = [
|
|
391
|
+
'mobile',
|
|
392
|
+
'tablet',
|
|
393
|
+
'desktop',
|
|
394
|
+
];
|
|
395
|
+
|
|
396
|
+
for (const viewport of viewports) {
|
|
397
|
+
const fullVPStyle = fullStyle[viewport];
|
|
398
|
+
const defaultVPStyle = defaultStyle[viewport];
|
|
399
|
+
|
|
400
|
+
if (!fullVPStyle) {
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
for (const key in fullVPStyle) {
|
|
405
|
+
if (
|
|
406
|
+
Object.prototype.hasOwnProperty.call(fullVPStyle, key) &&
|
|
407
|
+
fullVPStyle[key] !== defaultVPStyle?.[key]
|
|
408
|
+
) {
|
|
409
|
+
if (!overrides[viewport]) {
|
|
410
|
+
overrides[viewport] = {};
|
|
411
|
+
}
|
|
412
|
+
overrides[viewport]![key] = fullVPStyle[key];
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
if (Object.keys(overrides).length > 0 && !isDeepEqual(overrides, {})) {
|
|
418
|
+
return overrides;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
return undefined;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Reconciles classes for a set of final TemplateNodes against a base theme.
|
|
426
|
+
* It discovers the true theme from the content, merges it into the base theme,
|
|
427
|
+
* and then mutates the nodes to only contain true override classes.
|
|
428
|
+
* @param nodes The array of TemplateNode objects to mutate.
|
|
429
|
+
* @param baseDefaults The base theme object to mutate.
|
|
430
|
+
*/
|
|
431
|
+
function reconcileClasses(
|
|
432
|
+
nodes: TemplateNode[],
|
|
433
|
+
baseDefaults: DefaultClasses
|
|
434
|
+
): void {
|
|
435
|
+
const nodesByTag: Record<string, TemplateNode[]> = {};
|
|
436
|
+
const tempParsedNodes: ParsedNode[] = [];
|
|
437
|
+
|
|
438
|
+
nodes.forEach((node) => {
|
|
439
|
+
// Create a temporary ParsedNode structure for analysis
|
|
440
|
+
const tempParsedNode: ParsedNode = {
|
|
441
|
+
flatNode: node,
|
|
442
|
+
responsiveClasses: node.overrideClasses || {},
|
|
443
|
+
};
|
|
444
|
+
tempParsedNodes.push(tempParsedNode);
|
|
445
|
+
|
|
446
|
+
const tagName = node.tagName;
|
|
447
|
+
if (!['p', 'h2', 'h3', 'h4', 'h5'].includes(tagName)) {
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
if (!nodesByTag[tagName]) {
|
|
451
|
+
nodesByTag[tagName] = [];
|
|
452
|
+
}
|
|
453
|
+
nodesByTag[tagName].push(node);
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
const finalDefaults = JSON.parse(JSON.stringify(baseDefaults));
|
|
457
|
+
|
|
458
|
+
for (const tagName in nodesByTag) {
|
|
459
|
+
const nodesForTag = nodesByTag[tagName];
|
|
460
|
+
const tempParsedForTag = nodesForTag.map((n) => ({
|
|
461
|
+
flatNode: n,
|
|
462
|
+
responsiveClasses: n.overrideClasses || {},
|
|
463
|
+
}));
|
|
464
|
+
|
|
465
|
+
const commonStyleForTag = findMostCommonClasses(tempParsedForTag);
|
|
466
|
+
const mergedDefault = mergeResponsive(
|
|
467
|
+
finalDefaults[tagName],
|
|
468
|
+
commonStyleForTag
|
|
469
|
+
);
|
|
470
|
+
finalDefaults[tagName] = ensureRequiredViewports(mergedDefault);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
nodes.forEach((node) => {
|
|
474
|
+
const tagName = node.tagName;
|
|
475
|
+
const defaultStyleForTag = finalDefaults[tagName];
|
|
476
|
+
const fullStyle = node.overrideClasses || {};
|
|
477
|
+
|
|
478
|
+
if (defaultStyleForTag) {
|
|
479
|
+
const overrides = calculateOverrides(fullStyle, defaultStyleForTag);
|
|
480
|
+
node.overrideClasses = overrides;
|
|
481
|
+
} else if (Object.keys(fullStyle).length > 0 && node.tagName !== 'span') {
|
|
482
|
+
if (!isDeepEqual(fullStyle, {})) {
|
|
483
|
+
node.overrideClasses = fullStyle;
|
|
484
|
+
} else {
|
|
485
|
+
node.overrideClasses = undefined;
|
|
486
|
+
}
|
|
487
|
+
} else if (node.tagName !== 'span') {
|
|
488
|
+
node.overrideClasses = undefined;
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
Object.keys(baseDefaults).forEach((key) => delete baseDefaults[key]);
|
|
493
|
+
Object.assign(baseDefaults, finalDefaults);
|
|
494
|
+
}
|
|
495
|
+
|
|
409
496
|
function ensureRequiredViewports(
|
|
410
497
|
responsive: ResponsiveClasses | undefined
|
|
411
498
|
): DefaultClassValue {
|
|
@@ -502,8 +589,6 @@ export function parseAiCopyHtml(
|
|
|
502
589
|
const allParsedNodes: ParsedNode[] = [];
|
|
503
590
|
walkDom(doc.body, markdownId, allParsedNodes, markdownId);
|
|
504
591
|
|
|
505
|
-
// When parsing copy in isolation, all classes are treated as potential overrides.
|
|
506
|
-
// The consumer is responsible for merging these with a set of defaults if needed.
|
|
507
592
|
return allParsedNodes.map((pNode) => {
|
|
508
593
|
if (
|
|
509
594
|
Object.keys(pNode.responsiveClasses).length > 0 &&
|
|
@@ -515,53 +600,148 @@ export function parseAiCopyHtml(
|
|
|
515
600
|
});
|
|
516
601
|
}
|
|
517
602
|
|
|
603
|
+
function transformClassesFromShellLayer(
|
|
604
|
+
layer: LLMShellLayer | LLMColumnLayer['gridClasses']
|
|
605
|
+
): DefaultClassValue {
|
|
606
|
+
const mobileClasses = sanitizeResponsiveClasses(layer.mobile);
|
|
607
|
+
|
|
608
|
+
const tabletString = layer.tablet
|
|
609
|
+
? layer.tablet
|
|
610
|
+
.split(/\s+/)
|
|
611
|
+
.filter(Boolean)
|
|
612
|
+
.map((c) => `md:${c}`)
|
|
613
|
+
.join(' ')
|
|
614
|
+
: undefined;
|
|
615
|
+
const tabletClasses = sanitizeResponsiveClasses(tabletString);
|
|
616
|
+
|
|
617
|
+
const desktopString = layer.desktop
|
|
618
|
+
? layer.desktop
|
|
619
|
+
.split(/\s+/)
|
|
620
|
+
.filter(Boolean)
|
|
621
|
+
.map((c) => `xl:${c}`)
|
|
622
|
+
.join(' ')
|
|
623
|
+
: undefined;
|
|
624
|
+
const desktopClasses = sanitizeResponsiveClasses(desktopString);
|
|
625
|
+
|
|
626
|
+
let merged = mergeResponsive(mobileClasses, tabletClasses);
|
|
627
|
+
merged = mergeResponsive(merged, desktopClasses);
|
|
628
|
+
|
|
629
|
+
return ensureRequiredViewports(merged);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
function transformParentClassesFromShell(
|
|
633
|
+
llmParentClasses: LLMShellLayer[]
|
|
634
|
+
): ParentClassesPayload {
|
|
635
|
+
return llmParentClasses.map((layer) => transformClassesFromShellLayer(layer));
|
|
636
|
+
}
|
|
637
|
+
|
|
518
638
|
export const parseAiPane = (
|
|
519
639
|
shellJson: string,
|
|
520
|
-
copyHtml: string,
|
|
640
|
+
copyHtml: string | string[],
|
|
521
641
|
layout: string
|
|
522
|
-
): TemplatePane
|
|
523
|
-
|
|
642
|
+
): Omit<TemplatePane, 'nodes'> & {
|
|
643
|
+
nodes?: (TemplateMarkdown | GridLayoutNode)[];
|
|
644
|
+
} => {
|
|
645
|
+
console.log('--- ENTERING parseAiPane ---', { shellJson, copyHtml, layout });
|
|
524
646
|
|
|
647
|
+
const shell: ShellJson = JSON.parse(shellJson);
|
|
525
648
|
const paneId = ulid();
|
|
526
|
-
const markdownId = ulid();
|
|
527
|
-
|
|
528
|
-
const transformedParentClasses: ParentClassesPayload = (
|
|
529
|
-
shell.parentClasses || []
|
|
530
|
-
).map(
|
|
531
|
-
(layer): ParentClassLayer => ({
|
|
532
|
-
mobile: normalizeKeys(layer.mobile),
|
|
533
|
-
tablet: normalizeKeys(layer.tablet),
|
|
534
|
-
desktop: normalizeKeys(layer.desktop),
|
|
535
|
-
})
|
|
536
|
-
);
|
|
537
649
|
|
|
538
|
-
|
|
650
|
+
// --- GRID LAYOUT PATH ---
|
|
651
|
+
if (shell.columns && Array.isArray(copyHtml)) {
|
|
652
|
+
const gridLayoutId = ulid();
|
|
653
|
+
const shellDefaults = parseDefaultClassesFromShell(shell.defaultClasses);
|
|
654
|
+
const transformedParentClasses = transformParentClassesFromShell(
|
|
655
|
+
shell.parentClasses || []
|
|
656
|
+
);
|
|
539
657
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
658
|
+
const childMarkdownNodes = shell.columns.map((column, index) => {
|
|
659
|
+
const markdownId = ulid();
|
|
660
|
+
const gridClasses = transformClassesFromShellLayer(column.gridClasses);
|
|
661
|
+
const templateNodes = parseAiCopyHtml(copyHtml[index] || '', markdownId);
|
|
662
|
+
|
|
663
|
+
const markdownNode: TemplateMarkdown = {
|
|
664
|
+
id: markdownId,
|
|
665
|
+
nodeType: 'Markdown',
|
|
666
|
+
parentId: gridLayoutId,
|
|
667
|
+
type: 'markdown',
|
|
668
|
+
markdownId: ulid(),
|
|
669
|
+
gridClasses,
|
|
670
|
+
nodes: templateNodes,
|
|
671
|
+
};
|
|
672
|
+
return markdownNode;
|
|
673
|
+
});
|
|
674
|
+
|
|
675
|
+
const gridLayoutNode: GridLayoutNode & { nodes: TemplateMarkdown[] } = {
|
|
676
|
+
id: gridLayoutId,
|
|
677
|
+
nodeType: 'GridLayoutNode',
|
|
678
|
+
parentId: paneId,
|
|
679
|
+
type: 'grid-layout',
|
|
680
|
+
parentClasses: transformedParentClasses,
|
|
681
|
+
defaultClasses: shellDefaults,
|
|
682
|
+
gridColumns: {
|
|
683
|
+
mobile: 1,
|
|
684
|
+
tablet: 2,
|
|
685
|
+
desktop: 2,
|
|
686
|
+
},
|
|
687
|
+
nodes: childMarkdownNodes,
|
|
688
|
+
};
|
|
689
|
+
|
|
690
|
+
const templatePane = {
|
|
691
|
+
id: paneId,
|
|
692
|
+
nodeType: 'Pane' as const,
|
|
693
|
+
parentId: '',
|
|
694
|
+
title: 'AI Pane',
|
|
695
|
+
slug: `ai-${paneId.slice(-4)}`,
|
|
696
|
+
bgColour: shell.bgColour,
|
|
697
|
+
isDecorative: false,
|
|
698
|
+
gridLayout: gridLayoutNode,
|
|
699
|
+
};
|
|
700
|
+
console.log({
|
|
701
|
+
shellDefaults: shellDefaults,
|
|
702
|
+
transformedParentClasses: transformedParentClasses,
|
|
703
|
+
childMarkdownNodes: childMarkdownNodes,
|
|
704
|
+
gridLayoutNode: gridLayoutNode,
|
|
705
|
+
templatePane: templatePane,
|
|
706
|
+
});
|
|
707
|
+
return templatePane;
|
|
708
|
+
}
|
|
549
709
|
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
710
|
+
// --- SINGLE-COLUMN LAYOUT PATH ---
|
|
711
|
+
if (typeof copyHtml === 'string') {
|
|
712
|
+
const markdownId = ulid();
|
|
713
|
+
const shellDefaults = parseDefaultClassesFromShell(shell.defaultClasses);
|
|
714
|
+
const transformedParentClasses = transformParentClassesFromShell(
|
|
715
|
+
shell.parentClasses || []
|
|
716
|
+
);
|
|
717
|
+
const templateNodes = parseAiCopyHtml(copyHtml, markdownId);
|
|
718
|
+
|
|
719
|
+
reconcileClasses(templateNodes, shellDefaults);
|
|
720
|
+
|
|
721
|
+
const markdownNode: TemplateMarkdown = {
|
|
722
|
+
id: markdownId,
|
|
723
|
+
nodeType: 'Markdown',
|
|
724
|
+
parentId: paneId,
|
|
725
|
+
type: 'markdown',
|
|
726
|
+
markdownId: ulid(),
|
|
727
|
+
parentClasses: transformedParentClasses,
|
|
728
|
+
defaultClasses: shellDefaults,
|
|
562
729
|
nodes: templateNodes,
|
|
563
|
-
}
|
|
564
|
-
|
|
730
|
+
};
|
|
731
|
+
|
|
732
|
+
const templatePane = {
|
|
733
|
+
id: paneId,
|
|
734
|
+
nodeType: 'Pane' as const,
|
|
735
|
+
parentId: '',
|
|
736
|
+
title: 'AI Pane',
|
|
737
|
+
slug: `ai-${paneId.slice(-4)}`,
|
|
738
|
+
bgColour: shell.bgColour,
|
|
739
|
+
isDecorative: false,
|
|
740
|
+
markdown: markdownNode,
|
|
741
|
+
};
|
|
742
|
+
return templatePane;
|
|
743
|
+
}
|
|
565
744
|
|
|
566
|
-
|
|
745
|
+
// Fallback for invalid input
|
|
746
|
+
throw new Error('Invalid input for parseAiPane');
|
|
567
747
|
};
|
|
@@ -172,13 +172,14 @@ export async function savePaneToLibrary(
|
|
|
172
172
|
heightRatioDesktop: paneNode.heightRatioDesktop,
|
|
173
173
|
heightRatioMobile: paneNode.heightRatioMobile,
|
|
174
174
|
heightRatioTablet: paneNode.heightRatioTablet,
|
|
175
|
-
|
|
176
|
-
bgPane: newStorageBgPane,
|
|
175
|
+
...(newStorageMarkdown ? { markdowns: [newStorageMarkdown] } : {}),
|
|
176
|
+
...(newStorageBgPane ? { bgPane: newStorageBgPane } : {}),
|
|
177
177
|
};
|
|
178
178
|
|
|
179
179
|
const newLibraryEntry: DesignLibraryEntry = {
|
|
180
180
|
category: category,
|
|
181
181
|
title: title,
|
|
182
|
+
markdownCount: 1,
|
|
182
183
|
template: newStoragePane,
|
|
183
184
|
};
|
|
184
185
|
|
|
@@ -244,10 +245,11 @@ export function mergeCopyIntoTemplate(
|
|
|
244
245
|
copy: ExtractedCopy
|
|
245
246
|
): StoragePane {
|
|
246
247
|
const newTemplate = { ...template };
|
|
247
|
-
if (newTemplate.
|
|
248
|
-
newTemplate.
|
|
248
|
+
if (newTemplate.markdowns) {
|
|
249
|
+
newTemplate.markdowns[0].nodes = copy;
|
|
249
250
|
} else if (copy.length > 0) {
|
|
250
|
-
newTemplate.
|
|
251
|
+
if (!newTemplate.markdowns) newTemplate.markdowns = [];
|
|
252
|
+
newTemplate.markdowns[0] = {
|
|
251
253
|
nodeType: 'Markdown',
|
|
252
254
|
type: 'markdown',
|
|
253
255
|
defaultClasses: {},
|
|
@@ -289,8 +291,8 @@ export function convertStorageToLiveTemplate(
|
|
|
289
291
|
const markdownId = ulid();
|
|
290
292
|
const flatNodeList: TemplateNode[] = [];
|
|
291
293
|
|
|
292
|
-
if (storagePane.
|
|
293
|
-
for (const storageNode of storagePane.
|
|
294
|
+
if (storagePane.markdowns && storagePane.markdowns[0].nodes) {
|
|
295
|
+
for (const storageNode of storagePane.markdowns[0].nodes) {
|
|
294
296
|
const processedNodes = processStorageNode(storageNode, markdownId);
|
|
295
297
|
flatNodeList.push(...processedNodes);
|
|
296
298
|
}
|
|
@@ -307,12 +309,13 @@ export function convertStorageToLiveTemplate(
|
|
|
307
309
|
};
|
|
308
310
|
}
|
|
309
311
|
|
|
312
|
+
const { gridLayout, ...restOfStoragePane } = storagePane;
|
|
310
313
|
const liveTemplatePane: TemplatePane = {
|
|
311
|
-
...
|
|
314
|
+
...restOfStoragePane,
|
|
312
315
|
id: paneId,
|
|
313
316
|
parentId: '',
|
|
314
317
|
markdown: {
|
|
315
|
-
...(storagePane.
|
|
318
|
+
...((storagePane.markdowns && storagePane.markdowns[0]) || {
|
|
316
319
|
nodeType: 'Markdown',
|
|
317
320
|
type: 'markdown',
|
|
318
321
|
defaultClasses: {},
|