astro-tractstack 2.0.18 → 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/package.json +1 -1
- package/templates/src/components/edit/pane/AddPanePanel_new.tsx +275 -62
- package/templates/src/components/edit/pane/RestylePaneModal.tsx +1 -1
- package/templates/src/components/edit/panels/StyleImagePanel.tsx +0 -1
- package/templates/src/constants/prompts.json +22 -1
- package/templates/src/stores/nodes.ts +287 -220
- package/templates/src/types/compositorTypes.ts +19 -1
- package/templates/src/types/tractstack.ts +1 -0
- package/templates/src/utils/compositor/aiPaneParser.ts +263 -83
- package/templates/src/utils/compositor/designLibraryHelper.ts +12 -9
|
@@ -38,6 +38,7 @@ import type {
|
|
|
38
38
|
PaneNode,
|
|
39
39
|
StoryFragmentNode,
|
|
40
40
|
Tag,
|
|
41
|
+
TemplateGridLayout,
|
|
41
42
|
TemplateMarkdown,
|
|
42
43
|
TemplateNode,
|
|
43
44
|
TemplatePane,
|
|
@@ -1445,124 +1446,126 @@ export class NodesContext {
|
|
|
1445
1446
|
}
|
|
1446
1447
|
break;
|
|
1447
1448
|
|
|
1448
|
-
case 'TagElement':
|
|
1449
|
-
{
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
desktop: {},
|
|
1455
|
-
};
|
|
1449
|
+
case 'TagElement': {
|
|
1450
|
+
const getButtonClasses = (node: FlatNode) => {
|
|
1451
|
+
return {
|
|
1452
|
+
mobile: strippedStyles(node.buttonPayload?.buttonClasses || {}),
|
|
1453
|
+
tablet: {},
|
|
1454
|
+
desktop: {},
|
|
1456
1455
|
};
|
|
1456
|
+
};
|
|
1457
1457
|
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
};
|
|
1458
|
+
const getHoverClasses = (node: FlatNode) => {
|
|
1459
|
+
return {
|
|
1460
|
+
mobile: strippedStyles(
|
|
1461
|
+
node.buttonPayload?.buttonHoverClasses || {}
|
|
1462
|
+
),
|
|
1463
|
+
tablet: {},
|
|
1464
|
+
desktop: {},
|
|
1466
1465
|
};
|
|
1466
|
+
};
|
|
1467
1467
|
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1468
|
+
if (hasButtonPayload(node)) {
|
|
1469
|
+
const [classesPayload] = processClassesForViewports(
|
|
1470
|
+
getButtonClasses(node),
|
|
1471
|
+
{},
|
|
1472
|
+
1
|
|
1473
|
+
);
|
|
1474
|
+
const [classesHoverPayload] = processClassesForViewports(
|
|
1475
|
+
getHoverClasses(node),
|
|
1476
|
+
{},
|
|
1477
|
+
1
|
|
1478
|
+
);
|
|
1479
|
+
return `${classesPayload?.length ? classesPayload[0] : ``} ${
|
|
1480
|
+
classesHoverPayload?.length
|
|
1481
|
+
? addHoverPrefix(classesHoverPayload[0])
|
|
1482
|
+
: ``
|
|
1483
|
+
}`;
|
|
1484
|
+
}
|
|
1485
1485
|
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1486
|
+
if ('tagName' in node && node.tagName === 'span') {
|
|
1487
|
+
const spanNode = node as FlatNode;
|
|
1488
|
+
const [all, mobile, tablet, desktop] = processClassesForViewports(
|
|
1489
|
+
{ mobile: {}, tablet: {}, desktop: {} },
|
|
1490
|
+
spanNode.overrideClasses || {},
|
|
1491
|
+
1
|
|
1492
|
+
);
|
|
1493
|
+
const outlineClass =
|
|
1494
|
+
this.toolModeValStore.get().value === 'styles'
|
|
1495
|
+
? ' outline outline-1 outline-dotted outline-gray-400/60'
|
|
1496
|
+
: '';
|
|
1497
1497
|
|
|
1498
|
-
|
|
1499
|
-
|
|
1498
|
+
const getClassString = (classes: string[]): string =>
|
|
1499
|
+
classes && classes.length > 0 ? classes[0] : '';
|
|
1500
1500
|
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
}
|
|
1501
|
+
if (isPreview) return getClassString(desktop) + outlineClass;
|
|
1502
|
+
switch (viewport) {
|
|
1503
|
+
case 'desktop':
|
|
1504
|
+
return getClassString(desktop) + outlineClass;
|
|
1505
|
+
case 'tablet':
|
|
1506
|
+
return getClassString(tablet) + outlineClass;
|
|
1507
|
+
case 'mobile':
|
|
1508
|
+
return getClassString(mobile) + outlineClass;
|
|
1509
|
+
default:
|
|
1510
|
+
return getClassString(all) + outlineClass;
|
|
1512
1511
|
}
|
|
1512
|
+
}
|
|
1513
1513
|
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1514
|
+
// Begin Default Class Lookup Logic
|
|
1515
|
+
const markdownParentId = this.getClosestNodeTypeFromId(
|
|
1516
|
+
nodeId,
|
|
1517
|
+
'Markdown'
|
|
1518
|
+
);
|
|
1519
|
+
if (!markdownParentId) break;
|
|
1520
1520
|
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
}
|
|
1521
|
+
const markdownParentNode = this.allNodes
|
|
1522
|
+
.get()
|
|
1523
|
+
.get(markdownParentId) as MarkdownPaneFragmentNode;
|
|
1524
|
+
if (!markdownParentNode) break;
|
|
1525
|
+
|
|
1526
|
+
const tagNameStr = (node as FlatNode).tagName as string;
|
|
1527
|
+
|
|
1528
|
+
// By default, assume the markdown node is the source of styles.
|
|
1529
|
+
let styleSourceNode: MarkdownPaneFragmentNode | GridLayoutNode =
|
|
1530
|
+
markdownParentNode;
|
|
1531
|
+
let styles = styleSourceNode.defaultClasses?.[tagNameStr];
|
|
1532
|
+
|
|
1533
|
+
// If the markdown node has no styles for this tag, check for a GridLayout grandparent.
|
|
1534
|
+
// This handles the case where the MarkdownNode is a column.
|
|
1535
|
+
if (!styles || Object.keys(styles.mobile).length === 0) {
|
|
1536
|
+
const grandparent = markdownParentNode.parentId
|
|
1537
|
+
? this.allNodes.get().get(markdownParentNode.parentId)
|
|
1538
|
+
: null;
|
|
1539
|
+
|
|
1540
|
+
if (grandparent && isGridLayoutNode(grandparent)) {
|
|
1541
|
+
styleSourceNode = grandparent;
|
|
1542
|
+
styles = styleSourceNode.defaultClasses?.[tagNameStr];
|
|
1544
1543
|
}
|
|
1544
|
+
}
|
|
1545
1545
|
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1546
|
+
const baseStyles =
|
|
1547
|
+
styles && styles.mobile
|
|
1548
|
+
? styles
|
|
1549
|
+
: { mobile: {}, tablet: {}, desktop: {} };
|
|
1550
|
+
|
|
1551
|
+
const [all, mobile, tablet, desktop] = processClassesForViewports(
|
|
1552
|
+
baseStyles,
|
|
1553
|
+
(node as FlatNode)?.overrideClasses || {},
|
|
1554
|
+
1
|
|
1555
|
+
);
|
|
1556
|
+
|
|
1557
|
+
if (isPreview) return desktop[0];
|
|
1558
|
+
switch (viewport) {
|
|
1559
|
+
case 'desktop':
|
|
1560
|
+
return desktop[0];
|
|
1561
|
+
case 'tablet':
|
|
1562
|
+
return tablet[0];
|
|
1563
|
+
case 'mobile':
|
|
1564
|
+
return mobile[0];
|
|
1565
|
+
default:
|
|
1566
|
+
return all[0];
|
|
1564
1567
|
}
|
|
1565
|
-
|
|
1568
|
+
}
|
|
1566
1569
|
|
|
1567
1570
|
case 'StoryFragment': {
|
|
1568
1571
|
const storyFragment = node as StoryFragmentNode;
|
|
@@ -1775,39 +1778,15 @@ export class NodesContext {
|
|
|
1775
1778
|
duplicatedPane.isChanged = true;
|
|
1776
1779
|
|
|
1777
1780
|
// Track all nodes that need to be added
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
duplicatedPane.markdown.id = pane?.markdown?.id || ulid();
|
|
1784
|
-
duplicatedPane.markdown.markdownId = pane?.markdown?.markdownId || ulid();
|
|
1785
|
-
duplicatedPane.markdown.parentId = ownerId;
|
|
1786
|
-
|
|
1787
|
-
let markdownNodes: TemplateNode[] = [];
|
|
1788
|
-
if (duplicatedPane.markdown.markdownBody) {
|
|
1789
|
-
const markdownGen = new MarkdownGenerator(this);
|
|
1790
|
-
markdownNodes = markdownGen.markdownToFlatNodes(
|
|
1791
|
-
duplicatedPane.markdown.markdownBody,
|
|
1792
|
-
duplicatedPane.markdown.id
|
|
1793
|
-
) as TemplateNode[];
|
|
1794
|
-
allNodes = [...allNodes, duplicatedPane.markdown, ...markdownNodes];
|
|
1795
|
-
}
|
|
1781
|
+
// Call the new helper to process markdown, gridLayout, and bgPane
|
|
1782
|
+
const allNodes: BaseNode[] = this._processPaneTemplate(
|
|
1783
|
+
duplicatedPane,
|
|
1784
|
+
ownerId
|
|
1785
|
+
);
|
|
1796
1786
|
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
typeof duplicatedPane.markdown.id === `string`
|
|
1801
|
-
) {
|
|
1802
|
-
duplicatedPane?.markdown.nodes?.forEach((node) => {
|
|
1803
|
-
const childrenNodes = this.setupTemplateNodeRecursively(
|
|
1804
|
-
node,
|
|
1805
|
-
duplicatedPane?.markdown?.id || ''
|
|
1806
|
-
);
|
|
1807
|
-
markdownNodes.push(...childrenNodes);
|
|
1808
|
-
});
|
|
1809
|
-
allNodes = [...allNodes, duplicatedPane.markdown, ...markdownNodes];
|
|
1810
|
-
}
|
|
1787
|
+
// Remove bgPane from the pane object if it exists, as it's now a separate node
|
|
1788
|
+
if (duplicatedPane.bgPane) {
|
|
1789
|
+
delete duplicatedPane.bgPane;
|
|
1811
1790
|
}
|
|
1812
1791
|
|
|
1813
1792
|
this.addNode(duplicatedPane as PaneNode);
|
|
@@ -1852,85 +1831,15 @@ export class NodesContext {
|
|
|
1852
1831
|
}
|
|
1853
1832
|
}
|
|
1854
1833
|
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
duplicatedPane.markdown.markdownId = pane?.markdown?.markdownId || ulid();
|
|
1861
|
-
duplicatedPane.markdown.parentId = duplicatedPaneId;
|
|
1862
|
-
|
|
1863
|
-
let markdownNodes: TemplateNode[] = [];
|
|
1864
|
-
if (duplicatedPane.markdown.markdownBody) {
|
|
1865
|
-
const markdownGen = new MarkdownGenerator(this);
|
|
1866
|
-
markdownNodes = markdownGen.markdownToFlatNodes(
|
|
1867
|
-
duplicatedPane.markdown.markdownBody,
|
|
1868
|
-
duplicatedPane.markdown.id
|
|
1869
|
-
) as TemplateNode[];
|
|
1870
|
-
allNodes = [...allNodes, duplicatedPane.markdown, ...markdownNodes];
|
|
1871
|
-
} else if (
|
|
1872
|
-
typeof duplicatedPane.markdown !== `undefined` &&
|
|
1873
|
-
typeof duplicatedPane.markdown.id === `string`
|
|
1874
|
-
) {
|
|
1875
|
-
// Create a map to track the original node ID to its duplicated node ID
|
|
1876
|
-
const oldToNewIdMap = new Map<string, string>();
|
|
1877
|
-
// First pass: Clone nodes and generate new IDs
|
|
1878
|
-
const nodesClone =
|
|
1879
|
-
duplicatedPane?.markdown?.nodes?.map((originalNode) => {
|
|
1880
|
-
const newNode = cloneDeep(originalNode);
|
|
1881
|
-
newNode.id = ulid();
|
|
1882
|
-
oldToNewIdMap.set(originalNode.id, newNode.id);
|
|
1883
|
-
return newNode;
|
|
1884
|
-
}) || [];
|
|
1885
|
-
// Second pass: Update parent IDs using the mapping
|
|
1886
|
-
nodesClone.forEach((node) => {
|
|
1887
|
-
// Special case for direct children of markdown
|
|
1888
|
-
if (node.parentId === pane?.markdown?.id) {
|
|
1889
|
-
node.parentId = duplicatedPane?.markdown?.id || '';
|
|
1890
|
-
} else {
|
|
1891
|
-
// For all other nodes, use the mapping to find the new parent ID
|
|
1892
|
-
const newParentId = oldToNewIdMap.get(node.parentId || '');
|
|
1893
|
-
if (newParentId) {
|
|
1894
|
-
node.parentId = newParentId;
|
|
1895
|
-
}
|
|
1896
|
-
}
|
|
1897
|
-
markdownNodes.push(node);
|
|
1898
|
-
});
|
|
1899
|
-
allNodes = [...allNodes, duplicatedPane.markdown, ...markdownNodes];
|
|
1900
|
-
}
|
|
1901
|
-
}
|
|
1834
|
+
// Call the new helper to process markdown, gridLayout, and bgPane
|
|
1835
|
+
const allNodes: BaseNode[] = this._processPaneTemplate(
|
|
1836
|
+
duplicatedPane,
|
|
1837
|
+
duplicatedPaneId
|
|
1838
|
+
);
|
|
1902
1839
|
|
|
1840
|
+
// Remove bgPane from the pane object if it exists, as it's now a separate node
|
|
1841
|
+
// This preserves the original logic
|
|
1903
1842
|
if (duplicatedPane.bgPane) {
|
|
1904
|
-
const bgPaneId = ulid();
|
|
1905
|
-
|
|
1906
|
-
if (duplicatedPane.bgPane.type === 'visual-break') {
|
|
1907
|
-
const visualBreakPane = duplicatedPane.bgPane as VisualBreakNode;
|
|
1908
|
-
const bgPaneNode: VisualBreakNode = {
|
|
1909
|
-
id: bgPaneId,
|
|
1910
|
-
nodeType: 'BgPane',
|
|
1911
|
-
parentId: duplicatedPaneId,
|
|
1912
|
-
type: 'visual-break',
|
|
1913
|
-
breakDesktop: visualBreakPane.breakDesktop,
|
|
1914
|
-
breakTablet: visualBreakPane.breakTablet,
|
|
1915
|
-
breakMobile: visualBreakPane.breakMobile,
|
|
1916
|
-
};
|
|
1917
|
-
allNodes.push(bgPaneNode);
|
|
1918
|
-
} else if (duplicatedPane.bgPane.type === 'artpack-image') {
|
|
1919
|
-
const artpackBgPane = duplicatedPane.bgPane as ArtpackImageNode;
|
|
1920
|
-
const bgPaneNode: ArtpackImageNode = {
|
|
1921
|
-
id: bgPaneId,
|
|
1922
|
-
nodeType: 'BgPane',
|
|
1923
|
-
parentId: duplicatedPaneId,
|
|
1924
|
-
type: 'artpack-image',
|
|
1925
|
-
collection: artpackBgPane.collection,
|
|
1926
|
-
image: artpackBgPane.image,
|
|
1927
|
-
src: artpackBgPane.src,
|
|
1928
|
-
srcSet: artpackBgPane.srcSet,
|
|
1929
|
-
alt: artpackBgPane.alt || `Artpack image`,
|
|
1930
|
-
objectFit: artpackBgPane.objectFit || 'cover',
|
|
1931
|
-
};
|
|
1932
|
-
allNodes.push(bgPaneNode);
|
|
1933
|
-
}
|
|
1934
1843
|
delete duplicatedPane.bgPane;
|
|
1935
1844
|
}
|
|
1936
1845
|
|
|
@@ -1973,10 +1882,14 @@ export class NodesContext {
|
|
|
1973
1882
|
this.addNodes(allNodes);
|
|
1974
1883
|
this.notifyNode(ownerId);
|
|
1975
1884
|
|
|
1885
|
+
// Combine the pane and all its child nodes for the history patch
|
|
1886
|
+
const nodesToHistory = [duplicatedPane as BaseNode, ...allNodes];
|
|
1887
|
+
|
|
1976
1888
|
this.history.addPatch({
|
|
1977
1889
|
op: PatchOp.ADD,
|
|
1978
1890
|
undo: (ctx) => {
|
|
1979
|
-
|
|
1891
|
+
// Delete all nodes created (pane + children)
|
|
1892
|
+
ctx.deleteNodes(nodesToHistory);
|
|
1980
1893
|
|
|
1981
1894
|
if (
|
|
1982
1895
|
storyFragmentNode &&
|
|
@@ -1988,8 +1901,6 @@ export class NodesContext {
|
|
|
1988
1901
|
);
|
|
1989
1902
|
storyFragmentNode.isChanged = storyFragmentWasChanged;
|
|
1990
1903
|
}
|
|
1991
|
-
|
|
1992
|
-
ctx.deleteNodes([duplicatedPane]);
|
|
1993
1904
|
},
|
|
1994
1905
|
redo: (ctx) => {
|
|
1995
1906
|
if (storyFragmentNode?.nodeType === 'StoryFragment') {
|
|
@@ -2005,13 +1916,13 @@ export class NodesContext {
|
|
|
2005
1916
|
storyFragmentNode.isChanged = true;
|
|
2006
1917
|
}
|
|
2007
1918
|
|
|
2008
|
-
|
|
1919
|
+
// Add all nodes back (pane + children)
|
|
1920
|
+
ctx.addNodes(nodesToHistory);
|
|
2009
1921
|
ctx.linkChildToParent(
|
|
2010
1922
|
duplicatedPane.id,
|
|
2011
1923
|
duplicatedPane.parentId,
|
|
2012
1924
|
specificIdx
|
|
2013
1925
|
);
|
|
2014
|
-
ctx.addNodes(allNodes);
|
|
2015
1926
|
},
|
|
2016
1927
|
});
|
|
2017
1928
|
|
|
@@ -2275,7 +2186,9 @@ export class NodesContext {
|
|
|
2275
2186
|
|
|
2276
2187
|
node.id = ulid();
|
|
2277
2188
|
node.parentId = parentId;
|
|
2278
|
-
|
|
2189
|
+
const thisNode = cloneDeep(node);
|
|
2190
|
+
delete thisNode.nodes;
|
|
2191
|
+
result.push(thisNode);
|
|
2279
2192
|
if ('nodes' in node && node.nodes) {
|
|
2280
2193
|
for (let i = 0; i < node.nodes.length; ++i) {
|
|
2281
2194
|
result = result.concat(
|
|
@@ -3224,6 +3137,160 @@ export class NodesContext {
|
|
|
3224
3137
|
|
|
3225
3138
|
return deletedNodes;
|
|
3226
3139
|
}
|
|
3140
|
+
|
|
3141
|
+
/**
|
|
3142
|
+
* Processes a TemplatePane's content (markdown, grid, or bgPane) and
|
|
3143
|
+
* returns a flat list of all nodes to be added to the store.
|
|
3144
|
+
* This is a de-duplicated helper used by addTemplatePane and addContextTemplatePane.
|
|
3145
|
+
* @param paneTemplate - The TemplatePane object to process.
|
|
3146
|
+
* @param newPaneId - The ID of the parent Pane node.
|
|
3147
|
+
* @returns An array of BaseNode objects to be added to allNodes.
|
|
3148
|
+
*/
|
|
3149
|
+
private _processPaneTemplate(
|
|
3150
|
+
paneTemplate: TemplatePane,
|
|
3151
|
+
newPaneId: string
|
|
3152
|
+
): BaseNode[] {
|
|
3153
|
+
let allNodes: BaseNode[] = [];
|
|
3154
|
+
|
|
3155
|
+
// 1. Process Markdown Content
|
|
3156
|
+
if (paneTemplate.markdown) {
|
|
3157
|
+
const duplicatedMarkdown = cloneDeep(
|
|
3158
|
+
paneTemplate.markdown
|
|
3159
|
+
) as TemplateMarkdown;
|
|
3160
|
+
duplicatedMarkdown.id = paneTemplate.markdown.id || ulid();
|
|
3161
|
+
duplicatedMarkdown.markdownId =
|
|
3162
|
+
paneTemplate.markdown.markdownId || ulid();
|
|
3163
|
+
duplicatedMarkdown.parentId = newPaneId;
|
|
3164
|
+
|
|
3165
|
+
let markdownNodes: TemplateNode[] = [];
|
|
3166
|
+
if (duplicatedMarkdown.markdownBody) {
|
|
3167
|
+
const markdownGen = new MarkdownGenerator(this);
|
|
3168
|
+
markdownNodes = markdownGen.markdownToFlatNodes(
|
|
3169
|
+
duplicatedMarkdown.markdownBody,
|
|
3170
|
+
duplicatedMarkdown.id
|
|
3171
|
+
) as TemplateNode[];
|
|
3172
|
+
allNodes = [...allNodes, duplicatedMarkdown, ...markdownNodes];
|
|
3173
|
+
} else if (
|
|
3174
|
+
typeof duplicatedMarkdown !== `undefined` &&
|
|
3175
|
+
typeof duplicatedMarkdown.id === `string`
|
|
3176
|
+
) {
|
|
3177
|
+
// Create a map to track the original node ID to its duplicated node ID
|
|
3178
|
+
const oldToNewIdMap = new Map<string, string>();
|
|
3179
|
+
// First pass: Clone nodes and generate new IDs
|
|
3180
|
+
const nodesClone =
|
|
3181
|
+
duplicatedMarkdown.nodes?.map((originalNode) => {
|
|
3182
|
+
const newNode = cloneDeep(originalNode);
|
|
3183
|
+
newNode.id = ulid();
|
|
3184
|
+
oldToNewIdMap.set(originalNode.id, newNode.id);
|
|
3185
|
+
return newNode;
|
|
3186
|
+
}) || [];
|
|
3187
|
+
// Second pass: Update parent IDs using the mapping
|
|
3188
|
+
nodesClone.forEach((node) => {
|
|
3189
|
+
// Special case for direct children of markdown
|
|
3190
|
+
if (node.parentId === paneTemplate.markdown?.id) {
|
|
3191
|
+
node.parentId = duplicatedMarkdown.id;
|
|
3192
|
+
} else {
|
|
3193
|
+
// For all other nodes, use the mapping to find the new parent ID
|
|
3194
|
+
const newParentId = oldToNewIdMap.get(node.parentId || '');
|
|
3195
|
+
if (newParentId) {
|
|
3196
|
+
node.parentId = newParentId;
|
|
3197
|
+
}
|
|
3198
|
+
}
|
|
3199
|
+
markdownNodes.push(node);
|
|
3200
|
+
});
|
|
3201
|
+
allNodes = [...allNodes, duplicatedMarkdown, ...markdownNodes];
|
|
3202
|
+
}
|
|
3203
|
+
|
|
3204
|
+
// 2. Process GridLayout Content
|
|
3205
|
+
} else if (paneTemplate.gridLayout) {
|
|
3206
|
+
const duplicatedGrid = cloneDeep(
|
|
3207
|
+
paneTemplate.gridLayout
|
|
3208
|
+
) as TemplateGridLayout;
|
|
3209
|
+
duplicatedGrid.id = paneTemplate.gridLayout.id || ulid();
|
|
3210
|
+
duplicatedGrid.parentId = newPaneId;
|
|
3211
|
+
allNodes.push(duplicatedGrid as GridLayoutNode);
|
|
3212
|
+
|
|
3213
|
+
// Map for all nodes within the grid
|
|
3214
|
+
const oldToNewIdMap = new Map<string, string>();
|
|
3215
|
+
|
|
3216
|
+
// First pass: Collect all column nodes and their descendant nodes
|
|
3217
|
+
const allOriginalNodes: TemplateNode[] = [];
|
|
3218
|
+
const columnNodes: TemplateMarkdown[] = [];
|
|
3219
|
+
|
|
3220
|
+
duplicatedGrid.nodes?.forEach((originalColumn) => {
|
|
3221
|
+
const newColumn = cloneDeep(originalColumn);
|
|
3222
|
+
newColumn.id = ulid();
|
|
3223
|
+
newColumn.markdownId = ulid();
|
|
3224
|
+
oldToNewIdMap.set(originalColumn.id, newColumn.id);
|
|
3225
|
+
columnNodes.push(newColumn);
|
|
3226
|
+
|
|
3227
|
+
originalColumn.nodes?.forEach((colNode) => {
|
|
3228
|
+
allOriginalNodes.push(colNode);
|
|
3229
|
+
});
|
|
3230
|
+
});
|
|
3231
|
+
|
|
3232
|
+
// Second pass: Clone all descendant nodes
|
|
3233
|
+
const allClonedDescendants = allOriginalNodes.map((originalNode) => {
|
|
3234
|
+
const newNode = cloneDeep(originalNode);
|
|
3235
|
+
newNode.id = ulid();
|
|
3236
|
+
oldToNewIdMap.set(originalNode.id, newNode.id);
|
|
3237
|
+
return newNode;
|
|
3238
|
+
});
|
|
3239
|
+
|
|
3240
|
+
// Third pass: Re-map parent IDs for columns
|
|
3241
|
+
columnNodes.forEach((col) => {
|
|
3242
|
+
col.parentId = duplicatedGrid.id;
|
|
3243
|
+
allNodes.push(col as MarkdownPaneFragmentNode);
|
|
3244
|
+
});
|
|
3245
|
+
|
|
3246
|
+
// Fourth pass: Re-map parent IDs for all descendants
|
|
3247
|
+
allClonedDescendants.forEach((node) => {
|
|
3248
|
+
const newParentId = oldToNewIdMap.get(node.parentId || '');
|
|
3249
|
+
if (newParentId) {
|
|
3250
|
+
node.parentId = newParentId;
|
|
3251
|
+
}
|
|
3252
|
+
allNodes.push(node);
|
|
3253
|
+
});
|
|
3254
|
+
}
|
|
3255
|
+
|
|
3256
|
+
// 3. Process Background Pane
|
|
3257
|
+
if (paneTemplate.bgPane) {
|
|
3258
|
+
const bgPaneId = ulid();
|
|
3259
|
+
|
|
3260
|
+
if (paneTemplate.bgPane.type === 'visual-break') {
|
|
3261
|
+
const visualBreakPane = paneTemplate.bgPane as VisualBreakNode;
|
|
3262
|
+
const bgPaneNode: VisualBreakNode = {
|
|
3263
|
+
id: bgPaneId,
|
|
3264
|
+
nodeType: 'BgPane',
|
|
3265
|
+
parentId: newPaneId,
|
|
3266
|
+
type: 'visual-break',
|
|
3267
|
+
breakDesktop: visualBreakPane.breakDesktop,
|
|
3268
|
+
breakTablet: visualBreakPane.breakTablet,
|
|
3269
|
+
breakMobile: visualBreakPane.breakMobile,
|
|
3270
|
+
};
|
|
3271
|
+
allNodes.push(bgPaneNode);
|
|
3272
|
+
} else if (paneTemplate.bgPane.type === 'artpack-image') {
|
|
3273
|
+
const artpackBgPane = paneTemplate.bgPane as ArtpackImageNode;
|
|
3274
|
+
const bgPaneNode: ArtpackImageNode = {
|
|
3275
|
+
id: bgPaneId,
|
|
3276
|
+
nodeType: 'BgPane',
|
|
3277
|
+
parentId: newPaneId,
|
|
3278
|
+
type: 'artpack-image',
|
|
3279
|
+
collection: artpackBgPane.collection,
|
|
3280
|
+
image: artpackBgPane.image,
|
|
3281
|
+
src: artpackBgPane.src,
|
|
3282
|
+
srcSet: artpackBgPane.srcSet,
|
|
3283
|
+
alt: artpackBgPane.alt || `Artpack image`,
|
|
3284
|
+
objectFit: artpackBgPane.objectFit || 'cover',
|
|
3285
|
+
};
|
|
3286
|
+
allNodes.push(bgPaneNode);
|
|
3287
|
+
}
|
|
3288
|
+
// This helper only processes nodes, it doesn't modify the paneTemplate.
|
|
3289
|
+
// The deletion of `duplicatedPane.bgPane` will remain in `addTemplatePane`.
|
|
3290
|
+
}
|
|
3291
|
+
|
|
3292
|
+
return allNodes;
|
|
3293
|
+
}
|
|
3227
3294
|
}
|
|
3228
3295
|
|
|
3229
3296
|
export const globalCtx: NodesContext = new NodesContext();
|
|
@@ -424,10 +424,15 @@ export type TemplateMarkdown = MarkdownPaneFragmentNode & {
|
|
|
424
424
|
markdownBody?: string;
|
|
425
425
|
};
|
|
426
426
|
|
|
427
|
+
export type TemplateGridLayout = GridLayoutNode & {
|
|
428
|
+
nodes?: TemplateMarkdown[];
|
|
429
|
+
};
|
|
430
|
+
|
|
427
431
|
export type TemplatePane = PaneNode & {
|
|
428
432
|
id?: string;
|
|
429
433
|
parentId?: string;
|
|
430
434
|
markdown?: TemplateMarkdown;
|
|
435
|
+
gridLayout?: TemplateGridLayout;
|
|
431
436
|
bgPane?: VisualBreakNode | ArtpackImageNode | BgImageNode;
|
|
432
437
|
};
|
|
433
438
|
|
|
@@ -463,6 +468,18 @@ export type StorageMarkdown = Omit<
|
|
|
463
468
|
nodes?: StorageNode[];
|
|
464
469
|
};
|
|
465
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
|
+
|
|
466
483
|
export type StoragePane = Omit<
|
|
467
484
|
PaneNode,
|
|
468
485
|
| 'id'
|
|
@@ -476,7 +493,8 @@ export type StoragePane = Omit<
|
|
|
476
493
|
| 'codeHookPayload'
|
|
477
494
|
| 'markdown'
|
|
478
495
|
> & {
|
|
479
|
-
|
|
496
|
+
markdowns?: StorageMarkdown[];
|
|
497
|
+
gridLayout?: StorageGridLayoutNode;
|
|
480
498
|
bgPane?: StorageBgPane;
|
|
481
499
|
};
|
|
482
500
|
|