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
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
hasTagName,
|
|
16
16
|
isDefined,
|
|
17
17
|
isValidTag,
|
|
18
|
+
isGridLayoutNode,
|
|
18
19
|
toTag,
|
|
19
20
|
} from '@/utils/compositor/typeGuards';
|
|
20
21
|
import { startLoadingAnimation } from '@/utils/helpers';
|
|
@@ -29,6 +30,7 @@ import type {
|
|
|
29
30
|
BaseNode,
|
|
30
31
|
FlatNode,
|
|
31
32
|
ImpressionNode,
|
|
33
|
+
GridLayoutNode,
|
|
32
34
|
MarkdownPaneFragmentNode,
|
|
33
35
|
MenuNode,
|
|
34
36
|
NodeType,
|
|
@@ -36,6 +38,7 @@ import type {
|
|
|
36
38
|
PaneNode,
|
|
37
39
|
StoryFragmentNode,
|
|
38
40
|
Tag,
|
|
41
|
+
TemplateGridLayout,
|
|
39
42
|
TemplateMarkdown,
|
|
40
43
|
TemplateNode,
|
|
41
44
|
TemplatePane,
|
|
@@ -1390,6 +1393,30 @@ export class NodesContext {
|
|
|
1390
1393
|
if (!node) return '';
|
|
1391
1394
|
|
|
1392
1395
|
switch (node.nodeType) {
|
|
1396
|
+
case 'GridLayoutNode': {
|
|
1397
|
+
const gridNode = node as GridLayoutNode;
|
|
1398
|
+
if (gridNode.parentClasses) {
|
|
1399
|
+
const [all, mobile, tablet, desktop] = processClassesForViewports(
|
|
1400
|
+
gridNode.parentClasses[depth],
|
|
1401
|
+
{}, // No override classes for GridLayout parent case
|
|
1402
|
+
1
|
|
1403
|
+
);
|
|
1404
|
+
|
|
1405
|
+
if (isPreview) return desktop[0];
|
|
1406
|
+
switch (viewport) {
|
|
1407
|
+
case 'desktop':
|
|
1408
|
+
return desktop[0];
|
|
1409
|
+
case 'tablet':
|
|
1410
|
+
return tablet[0];
|
|
1411
|
+
case 'mobile':
|
|
1412
|
+
return mobile[0];
|
|
1413
|
+
default:
|
|
1414
|
+
return all[0];
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
break;
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1393
1420
|
case 'Markdown':
|
|
1394
1421
|
{
|
|
1395
1422
|
const markdownFragment = node as MarkdownPaneFragmentNode;
|
|
@@ -1419,103 +1446,126 @@ export class NodesContext {
|
|
|
1419
1446
|
}
|
|
1420
1447
|
break;
|
|
1421
1448
|
|
|
1422
|
-
case 'TagElement':
|
|
1423
|
-
{
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
desktop: {},
|
|
1429
|
-
};
|
|
1449
|
+
case 'TagElement': {
|
|
1450
|
+
const getButtonClasses = (node: FlatNode) => {
|
|
1451
|
+
return {
|
|
1452
|
+
mobile: strippedStyles(node.buttonPayload?.buttonClasses || {}),
|
|
1453
|
+
tablet: {},
|
|
1454
|
+
desktop: {},
|
|
1430
1455
|
};
|
|
1456
|
+
};
|
|
1431
1457
|
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
};
|
|
1458
|
+
const getHoverClasses = (node: FlatNode) => {
|
|
1459
|
+
return {
|
|
1460
|
+
mobile: strippedStyles(
|
|
1461
|
+
node.buttonPayload?.buttonHoverClasses || {}
|
|
1462
|
+
),
|
|
1463
|
+
tablet: {},
|
|
1464
|
+
desktop: {},
|
|
1440
1465
|
};
|
|
1466
|
+
};
|
|
1441
1467
|
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
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
|
+
}
|
|
1459
1485
|
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
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
|
+
|
|
1498
|
+
const getClassString = (classes: string[]): string =>
|
|
1499
|
+
classes && classes.length > 0 ? classes[0] : '';
|
|
1500
|
+
|
|
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;
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1471
1513
|
|
|
1472
|
-
|
|
1473
|
-
|
|
1514
|
+
// Begin Default Class Lookup Logic
|
|
1515
|
+
const markdownParentId = this.getClosestNodeTypeFromId(
|
|
1516
|
+
nodeId,
|
|
1517
|
+
'Markdown'
|
|
1518
|
+
);
|
|
1519
|
+
if (!markdownParentId) break;
|
|
1474
1520
|
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
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];
|
|
1486
1543
|
}
|
|
1544
|
+
}
|
|
1487
1545
|
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
return tablet[0];
|
|
1510
|
-
case 'mobile':
|
|
1511
|
-
return mobile[0];
|
|
1512
|
-
default:
|
|
1513
|
-
return all[0];
|
|
1514
|
-
}
|
|
1515
|
-
}
|
|
1516
|
-
}
|
|
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];
|
|
1517
1567
|
}
|
|
1518
|
-
|
|
1568
|
+
}
|
|
1519
1569
|
|
|
1520
1570
|
case 'StoryFragment': {
|
|
1521
1571
|
const storyFragment = node as StoryFragmentNode;
|
|
@@ -1584,19 +1634,44 @@ export class NodesContext {
|
|
|
1584
1634
|
}
|
|
1585
1635
|
|
|
1586
1636
|
switch (node.nodeType) {
|
|
1587
|
-
case
|
|
1588
|
-
case
|
|
1589
|
-
case
|
|
1637
|
+
case 'GridLayoutNode':
|
|
1638
|
+
case 'TagElement':
|
|
1639
|
+
case 'BgPane':
|
|
1640
|
+
case 'Markdown': {
|
|
1641
|
+
const nodesToDirty: BaseNode[] = [];
|
|
1590
1642
|
const paneNodeId = this.getClosestNodeTypeFromId(node.id, 'Pane');
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1643
|
+
|
|
1644
|
+
if (paneNodeId) {
|
|
1645
|
+
const paneNode = this.allNodes.get().get(paneNodeId);
|
|
1646
|
+
if (paneNode && !paneNode.isChanged) {
|
|
1647
|
+
nodesToDirty.push({ ...paneNode, isChanged: true });
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
if (node.parentId) {
|
|
1652
|
+
const parentNode = this.allNodes.get().get(node.parentId);
|
|
1653
|
+
if (
|
|
1654
|
+
parentNode &&
|
|
1655
|
+
parentNode.nodeType === 'GridLayoutNode' &&
|
|
1656
|
+
!parentNode.isChanged
|
|
1657
|
+
) {
|
|
1658
|
+
if (!nodesToDirty.some((n) => n.id === parentNode.id)) {
|
|
1659
|
+
nodesToDirty.push({ ...parentNode, isChanged: true });
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
if (nodesToDirty.length > 0) {
|
|
1665
|
+
this.modifyNodes(nodesToDirty, {
|
|
1666
|
+
notify: false,
|
|
1667
|
+
recordHistory: false,
|
|
1668
|
+
});
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1597
1671
|
this.notifyNode(ROOT_NODE_NAME);
|
|
1598
1672
|
break;
|
|
1599
1673
|
}
|
|
1674
|
+
|
|
1600
1675
|
case `Menu`:
|
|
1601
1676
|
case `Pane`:
|
|
1602
1677
|
case `StoryFragment`:
|
|
@@ -1703,39 +1778,15 @@ export class NodesContext {
|
|
|
1703
1778
|
duplicatedPane.isChanged = true;
|
|
1704
1779
|
|
|
1705
1780
|
// Track all nodes that need to be added
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
duplicatedPane.markdown.id = pane?.markdown?.id || ulid();
|
|
1712
|
-
duplicatedPane.markdown.markdownId = pane?.markdown?.markdownId || ulid();
|
|
1713
|
-
duplicatedPane.markdown.parentId = ownerId;
|
|
1714
|
-
|
|
1715
|
-
let markdownNodes: TemplateNode[] = [];
|
|
1716
|
-
if (duplicatedPane.markdown.markdownBody) {
|
|
1717
|
-
const markdownGen = new MarkdownGenerator(this);
|
|
1718
|
-
markdownNodes = markdownGen.markdownToFlatNodes(
|
|
1719
|
-
duplicatedPane.markdown.markdownBody,
|
|
1720
|
-
duplicatedPane.markdown.id
|
|
1721
|
-
) as TemplateNode[];
|
|
1722
|
-
allNodes = [...allNodes, duplicatedPane.markdown, ...markdownNodes];
|
|
1723
|
-
}
|
|
1781
|
+
// Call the new helper to process markdown, gridLayout, and bgPane
|
|
1782
|
+
const allNodes: BaseNode[] = this._processPaneTemplate(
|
|
1783
|
+
duplicatedPane,
|
|
1784
|
+
ownerId
|
|
1785
|
+
);
|
|
1724
1786
|
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
typeof duplicatedPane.markdown.id === `string`
|
|
1729
|
-
) {
|
|
1730
|
-
duplicatedPane?.markdown.nodes?.forEach((node) => {
|
|
1731
|
-
const childrenNodes = this.setupTemplateNodeRecursively(
|
|
1732
|
-
node,
|
|
1733
|
-
duplicatedPane?.markdown?.id || ''
|
|
1734
|
-
);
|
|
1735
|
-
markdownNodes.push(...childrenNodes);
|
|
1736
|
-
});
|
|
1737
|
-
allNodes = [...allNodes, duplicatedPane.markdown, ...markdownNodes];
|
|
1738
|
-
}
|
|
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;
|
|
1739
1790
|
}
|
|
1740
1791
|
|
|
1741
1792
|
this.addNode(duplicatedPane as PaneNode);
|
|
@@ -1780,85 +1831,15 @@ export class NodesContext {
|
|
|
1780
1831
|
}
|
|
1781
1832
|
}
|
|
1782
1833
|
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
duplicatedPane.markdown.markdownId = pane?.markdown?.markdownId || ulid();
|
|
1789
|
-
duplicatedPane.markdown.parentId = duplicatedPaneId;
|
|
1790
|
-
|
|
1791
|
-
let markdownNodes: TemplateNode[] = [];
|
|
1792
|
-
if (duplicatedPane.markdown.markdownBody) {
|
|
1793
|
-
const markdownGen = new MarkdownGenerator(this);
|
|
1794
|
-
markdownNodes = markdownGen.markdownToFlatNodes(
|
|
1795
|
-
duplicatedPane.markdown.markdownBody,
|
|
1796
|
-
duplicatedPane.markdown.id
|
|
1797
|
-
) as TemplateNode[];
|
|
1798
|
-
allNodes = [...allNodes, duplicatedPane.markdown, ...markdownNodes];
|
|
1799
|
-
} else if (
|
|
1800
|
-
typeof duplicatedPane.markdown !== `undefined` &&
|
|
1801
|
-
typeof duplicatedPane.markdown.id === `string`
|
|
1802
|
-
) {
|
|
1803
|
-
// Create a map to track the original node ID to its duplicated node ID
|
|
1804
|
-
const oldToNewIdMap = new Map<string, string>();
|
|
1805
|
-
// First pass: Clone nodes and generate new IDs
|
|
1806
|
-
const nodesClone =
|
|
1807
|
-
duplicatedPane?.markdown?.nodes?.map((originalNode) => {
|
|
1808
|
-
const newNode = cloneDeep(originalNode);
|
|
1809
|
-
newNode.id = ulid();
|
|
1810
|
-
oldToNewIdMap.set(originalNode.id, newNode.id);
|
|
1811
|
-
return newNode;
|
|
1812
|
-
}) || [];
|
|
1813
|
-
// Second pass: Update parent IDs using the mapping
|
|
1814
|
-
nodesClone.forEach((node) => {
|
|
1815
|
-
// Special case for direct children of markdown
|
|
1816
|
-
if (node.parentId === pane?.markdown?.id) {
|
|
1817
|
-
node.parentId = duplicatedPane?.markdown?.id || '';
|
|
1818
|
-
} else {
|
|
1819
|
-
// For all other nodes, use the mapping to find the new parent ID
|
|
1820
|
-
const newParentId = oldToNewIdMap.get(node.parentId || '');
|
|
1821
|
-
if (newParentId) {
|
|
1822
|
-
node.parentId = newParentId;
|
|
1823
|
-
}
|
|
1824
|
-
}
|
|
1825
|
-
markdownNodes.push(node);
|
|
1826
|
-
});
|
|
1827
|
-
allNodes = [...allNodes, duplicatedPane.markdown, ...markdownNodes];
|
|
1828
|
-
}
|
|
1829
|
-
}
|
|
1834
|
+
// Call the new helper to process markdown, gridLayout, and bgPane
|
|
1835
|
+
const allNodes: BaseNode[] = this._processPaneTemplate(
|
|
1836
|
+
duplicatedPane,
|
|
1837
|
+
duplicatedPaneId
|
|
1838
|
+
);
|
|
1830
1839
|
|
|
1840
|
+
// Remove bgPane from the pane object if it exists, as it's now a separate node
|
|
1841
|
+
// This preserves the original logic
|
|
1831
1842
|
if (duplicatedPane.bgPane) {
|
|
1832
|
-
const bgPaneId = ulid();
|
|
1833
|
-
|
|
1834
|
-
if (duplicatedPane.bgPane.type === 'visual-break') {
|
|
1835
|
-
const visualBreakPane = duplicatedPane.bgPane as VisualBreakNode;
|
|
1836
|
-
const bgPaneNode: VisualBreakNode = {
|
|
1837
|
-
id: bgPaneId,
|
|
1838
|
-
nodeType: 'BgPane',
|
|
1839
|
-
parentId: duplicatedPaneId,
|
|
1840
|
-
type: 'visual-break',
|
|
1841
|
-
breakDesktop: visualBreakPane.breakDesktop,
|
|
1842
|
-
breakTablet: visualBreakPane.breakTablet,
|
|
1843
|
-
breakMobile: visualBreakPane.breakMobile,
|
|
1844
|
-
};
|
|
1845
|
-
allNodes.push(bgPaneNode);
|
|
1846
|
-
} else if (duplicatedPane.bgPane.type === 'artpack-image') {
|
|
1847
|
-
const artpackBgPane = duplicatedPane.bgPane as ArtpackImageNode;
|
|
1848
|
-
const bgPaneNode: ArtpackImageNode = {
|
|
1849
|
-
id: bgPaneId,
|
|
1850
|
-
nodeType: 'BgPane',
|
|
1851
|
-
parentId: duplicatedPaneId,
|
|
1852
|
-
type: 'artpack-image',
|
|
1853
|
-
collection: artpackBgPane.collection,
|
|
1854
|
-
image: artpackBgPane.image,
|
|
1855
|
-
src: artpackBgPane.src,
|
|
1856
|
-
srcSet: artpackBgPane.srcSet,
|
|
1857
|
-
alt: artpackBgPane.alt || `Artpack image`,
|
|
1858
|
-
objectFit: artpackBgPane.objectFit || 'cover',
|
|
1859
|
-
};
|
|
1860
|
-
allNodes.push(bgPaneNode);
|
|
1861
|
-
}
|
|
1862
1843
|
delete duplicatedPane.bgPane;
|
|
1863
1844
|
}
|
|
1864
1845
|
|
|
@@ -1901,10 +1882,14 @@ export class NodesContext {
|
|
|
1901
1882
|
this.addNodes(allNodes);
|
|
1902
1883
|
this.notifyNode(ownerId);
|
|
1903
1884
|
|
|
1885
|
+
// Combine the pane and all its child nodes for the history patch
|
|
1886
|
+
const nodesToHistory = [duplicatedPane as BaseNode, ...allNodes];
|
|
1887
|
+
|
|
1904
1888
|
this.history.addPatch({
|
|
1905
1889
|
op: PatchOp.ADD,
|
|
1906
1890
|
undo: (ctx) => {
|
|
1907
|
-
|
|
1891
|
+
// Delete all nodes created (pane + children)
|
|
1892
|
+
ctx.deleteNodes(nodesToHistory);
|
|
1908
1893
|
|
|
1909
1894
|
if (
|
|
1910
1895
|
storyFragmentNode &&
|
|
@@ -1916,8 +1901,6 @@ export class NodesContext {
|
|
|
1916
1901
|
);
|
|
1917
1902
|
storyFragmentNode.isChanged = storyFragmentWasChanged;
|
|
1918
1903
|
}
|
|
1919
|
-
|
|
1920
|
-
ctx.deleteNodes([duplicatedPane]);
|
|
1921
1904
|
},
|
|
1922
1905
|
redo: (ctx) => {
|
|
1923
1906
|
if (storyFragmentNode?.nodeType === 'StoryFragment') {
|
|
@@ -1933,13 +1916,13 @@ export class NodesContext {
|
|
|
1933
1916
|
storyFragmentNode.isChanged = true;
|
|
1934
1917
|
}
|
|
1935
1918
|
|
|
1936
|
-
|
|
1919
|
+
// Add all nodes back (pane + children)
|
|
1920
|
+
ctx.addNodes(nodesToHistory);
|
|
1937
1921
|
ctx.linkChildToParent(
|
|
1938
1922
|
duplicatedPane.id,
|
|
1939
1923
|
duplicatedPane.parentId,
|
|
1940
1924
|
specificIdx
|
|
1941
1925
|
);
|
|
1942
|
-
ctx.addNodes(allNodes);
|
|
1943
1926
|
},
|
|
1944
1927
|
});
|
|
1945
1928
|
|
|
@@ -1970,7 +1953,9 @@ export class NodesContext {
|
|
|
1970
1953
|
});
|
|
1971
1954
|
break;
|
|
1972
1955
|
}
|
|
1973
|
-
|
|
1956
|
+
if ([`p`, `h2`, `h3`, `h4`, `li`].includes(tagName))
|
|
1957
|
+
this.toolModeValStore.set({ value: 'text' });
|
|
1958
|
+
else this.toolModeValStore.set({ value: 'styles' });
|
|
1974
1959
|
this.notifyNode('root');
|
|
1975
1960
|
}
|
|
1976
1961
|
|
|
@@ -2024,6 +2009,7 @@ export class NodesContext {
|
|
|
2024
2009
|
|
|
2025
2010
|
let autoCreatedMarkdownNode: MarkdownPaneFragmentNode | null = null;
|
|
2026
2011
|
|
|
2012
|
+
console.log(`--- [TRAP - TEMPLATE BEFORE] ---`, cloneDeep(node));
|
|
2027
2013
|
// 3. HANDLE EMPTY PANE BY AUTO-CREATING A MARKDOWN NODE
|
|
2028
2014
|
if (targetNode.nodeType === 'Pane') {
|
|
2029
2015
|
// Create a minimal markdown node to act as the container
|
|
@@ -2101,6 +2087,8 @@ export class NodesContext {
|
|
|
2101
2087
|
);
|
|
2102
2088
|
}
|
|
2103
2089
|
|
|
2090
|
+
console.log(`--- [TRAP - FLATTENED AFTER] ---`, cloneDeep(flattenedNodes));
|
|
2091
|
+
|
|
2104
2092
|
// 5. PERFORM REMAINING STATE MUTATIONS
|
|
2105
2093
|
if (originalPaneNode) {
|
|
2106
2094
|
this.modifyNodes([{ ...originalPaneNode, isChanged: true }], {
|
|
@@ -2198,7 +2186,9 @@ export class NodesContext {
|
|
|
2198
2186
|
|
|
2199
2187
|
node.id = ulid();
|
|
2200
2188
|
node.parentId = parentId;
|
|
2201
|
-
|
|
2189
|
+
const thisNode = cloneDeep(node);
|
|
2190
|
+
delete thisNode.nodes;
|
|
2191
|
+
result.push(thisNode);
|
|
2202
2192
|
if ('nodes' in node && node.nodes) {
|
|
2203
2193
|
for (let i = 0; i < node.nodes.length; ++i) {
|
|
2204
2194
|
result = result.concat(
|
|
@@ -3147,6 +3137,160 @@ export class NodesContext {
|
|
|
3147
3137
|
|
|
3148
3138
|
return deletedNodes;
|
|
3149
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
|
+
}
|
|
3150
3294
|
}
|
|
3151
3295
|
|
|
3152
3296
|
export const globalCtx: NodesContext = new NodesContext();
|