astro-tractstack 2.1.3 → 2.2.1
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/README.md +54 -266
- package/bin/create-tractstack.js +9 -6
- package/dist/index.js +109 -71
- package/package.json +4 -2
- package/templates/css/custom.css +5 -0
- package/templates/icons/code.svg +18 -0
- package/templates/icons/li.svg +4 -0
- package/templates/icons/link.svg +22 -0
- package/templates/icons/p.svg +3 -0
- package/templates/src/client/app.js +80 -1
- package/templates/src/components/Footer.astro +1 -1
- package/templates/src/components/codehooks/BunnyVideoSetup.tsx +6 -6
- package/templates/src/components/codehooks/EpinetDurationSelector.tsx +3 -3
- package/templates/src/components/codehooks/FeaturedArticleSetup.tsx +1 -1
- package/templates/src/components/codehooks/ListContentSetup.tsx +2 -2
- package/templates/src/components/codehooks/ProductCardSetup.tsx +1 -1
- package/templates/src/components/codehooks/ProductGridSetup.tsx +2 -2
- package/templates/src/components/codehooks/SandboxRegisterForm.tsx +3 -3
- package/templates/src/components/compositor/Compositor.tsx +25 -9
- package/templates/src/components/compositor/Node.tsx +168 -496
- package/templates/src/components/compositor/PanelVisibilityWrapper.tsx +1 -0
- package/templates/src/components/compositor/elements/SignUp.tsx +1 -1
- package/templates/src/components/compositor/elements/YouTubeWrapper.tsx +2 -0
- package/templates/src/components/compositor/nodes/CreativePane.tsx +262 -0
- package/templates/src/components/compositor/nodes/GhostInsertBlock.tsx +4 -6
- package/templates/src/components/compositor/nodes/GridLayout.tsx +4 -2
- package/templates/src/components/compositor/nodes/Markdown.tsx +18 -3
- package/templates/src/components/compositor/nodes/Pane.tsx +11 -5
- package/templates/src/components/compositor/nodes/RenderChildren.tsx +1 -1
- package/templates/src/components/compositor/nodes/tagElements/NodeAnchorComponent.tsx +5 -5
- package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag.tsx +90 -42
- package/templates/src/components/compositor/nodes/tagElements/NodeImg.tsx +2 -0
- package/templates/src/components/compositor/nodes/tagElements/NodeText.tsx +27 -1
- package/templates/src/components/compositor/preview/PaneSnapshotGenerator.tsx +10 -8
- package/templates/src/components/compositor/tools/NodeOverlay.tsx +224 -0
- package/templates/src/components/compositor/tools/PaneOverlay.tsx +122 -0
- package/templates/src/components/edit/Header.tsx +68 -9
- package/templates/src/components/edit/PanelSwitch.tsx +42 -4
- package/templates/src/components/edit/SettingsPanel.tsx +2 -3
- package/templates/src/components/edit/ToolMode.tsx +1 -31
- package/templates/src/components/edit/pane/AddPanePanel_break.tsx +2 -2
- package/templates/src/components/edit/pane/AddPanePanel_codehook.tsx +1 -1
- package/templates/src/components/edit/pane/AddPanePanel_new.tsx +193 -659
- package/templates/src/components/edit/pane/AddPanePanel_reuse.tsx +15 -82
- package/templates/src/components/edit/pane/AiRestylePaneModal.tsx +95 -45
- package/templates/src/components/edit/pane/ConfigPanePanel.tsx +137 -49
- package/templates/src/components/edit/pane/RestylePaneModal.tsx +1 -1
- package/templates/src/components/edit/pane/steps/AiCreativeDesignStep.tsx +375 -0
- package/templates/src/components/edit/pane/steps/AiDesignStep.tsx +1 -23
- package/templates/src/components/edit/pane/steps/AiLibraryCopyStep.tsx +327 -0
- package/templates/src/components/edit/pane/steps/AiRefineDesignStep.tsx +267 -0
- package/templates/src/components/edit/pane/steps/AiStandardDesignStep.tsx +371 -0
- package/templates/src/components/edit/pane/steps/CopyInputStep.tsx +201 -76
- package/templates/src/components/edit/pane/steps/CreativeInjectStep.tsx +141 -0
- package/templates/src/components/edit/panels/CreativeImagePanel.tsx +435 -0
- package/templates/src/components/edit/panels/CreativeLinkPanel.tsx +110 -0
- package/templates/src/components/edit/panels/StyleCodeHookPanel.tsx +1 -1
- package/templates/src/components/edit/panels/StyleParentPanel.tsx +118 -126
- package/templates/src/components/edit/panels/StyleParentPanel_add.tsx +3 -2
- package/templates/src/components/edit/panels/StyleParentPanel_deleteLayer.tsx +1 -0
- package/templates/src/components/edit/panels/StyleParentPanel_remove.tsx +3 -1
- package/templates/src/components/edit/panels/StyleParentPanel_update.tsx +3 -1
- package/templates/src/components/edit/panels/StyleWidgetPanel.tsx +1 -1
- package/templates/src/components/edit/state/SaveModal.tsx +19 -787
- package/templates/src/components/edit/state/SaveToLibraryModal.tsx +2 -2
- package/templates/src/components/edit/storyfragment/StoryFragmentPanel_menu.tsx +1 -1
- package/templates/src/components/edit/widgets/BunnyWidget.tsx +5 -5
- package/templates/src/components/edit/widgets/InteractiveDisclosureWidget.tsx +1 -1
- package/templates/src/components/edit/widgets/SignupWidget.tsx +1 -1
- package/templates/src/components/fields/ActionBuilderTimeSelector.tsx +1 -1
- package/templates/src/components/fields/ArtpackImage.tsx +11 -3
- package/templates/src/components/fields/BackgroundImage.tsx +8 -0
- package/templates/src/components/fields/BackgroundImageWrapper.tsx +15 -9
- package/templates/src/components/fields/ImageUpload.tsx +6 -0
- package/templates/src/components/form/ActionBuilderField.tsx +15 -5
- package/templates/src/components/form/ActionBuilderSlugSelector.tsx +1 -1
- package/templates/src/components/form/ColorPicker.tsx +1 -1
- package/templates/src/components/form/EnumSelect.tsx +1 -1
- package/templates/src/components/form/NumberInput.tsx +1 -1
- package/templates/src/components/form/StringArrayInput.tsx +1 -1
- package/templates/src/components/form/StringInput.tsx +1 -1
- package/templates/src/components/form/UnsavedChangesBar.tsx +1 -1
- package/templates/src/components/form/advanced/APIConfigSection.tsx +2 -2
- package/templates/src/components/form/advanced/AuthConfigSection.tsx +2 -2
- package/templates/src/components/profile/ProfileCreate.tsx +1 -1
- package/templates/src/components/profile/ProfileEdit.tsx +1 -1
- package/templates/src/components/storykeep/Dashboard_Advanced.tsx +2 -2
- package/templates/src/components/storykeep/controls/content/BeliefForm.tsx +1 -1
- package/templates/src/components/storykeep/controls/content/ContentSummary.tsx +2 -2
- package/templates/src/components/storykeep/controls/content/KnownResourceTable.tsx +1 -1
- package/templates/src/components/storykeep/controls/content/ManageContent.tsx +6 -6
- package/templates/src/components/storykeep/controls/content/MenuForm.tsx +1 -1
- package/templates/src/components/storykeep/controls/content/PaneTable.tsx +358 -0
- package/templates/src/components/storykeep/controls/content/ResourceTable.tsx +1 -1
- package/templates/src/constants/prompts.json +18 -10
- package/templates/src/constants.ts +3 -0
- package/templates/src/hooks/usePaneFragments.ts +60 -0
- package/templates/src/lib/session.ts +71 -16
- package/templates/src/pages/[...slug].astro +4 -46
- package/templates/src/pages/api/css.ts +149 -0
- package/templates/src/pages/maint.astro +1 -1
- package/templates/src/pages/storykeep/login.astro +2 -2
- package/templates/src/stores/nodes.ts +162 -49
- package/templates/src/stores/orphanAnalysis.ts +6 -30
- package/templates/src/stores/previews.ts +7 -0
- package/templates/src/stores/storykeep.ts +0 -8
- package/templates/src/types/compositorTypes.ts +53 -10
- package/templates/src/utils/compositor/aiGeneration.ts +93 -0
- package/templates/src/utils/compositor/allowInsert.ts +2 -0
- package/templates/src/utils/compositor/htmlAst.ts +704 -0
- package/templates/src/utils/compositor/nodesHelper.ts +281 -102
- package/templates/src/utils/compositor/savePipeline.ts +893 -0
- package/templates/src/utils/etl/index.ts +3 -0
- package/templates/src/utils/etl/transformer.ts +10 -0
- package/templates/src/utils/helpers.ts +101 -0
- package/utils/inject-files.ts +100 -62
- package/templates/icons/text.svg +0 -6
- package/templates/src/components/compositor/NodeWithGuid.tsx +0 -69
- package/templates/src/components/compositor/nodes/GridLayout_eraser.tsx +0 -33
- package/templates/src/components/compositor/nodes/Markdown_eraser.tsx +0 -56
- package/templates/src/components/compositor/nodes/Pane_DesignLibrary.tsx +0 -269
- package/templates/src/components/compositor/nodes/Pane_eraser.tsx +0 -186
- package/templates/src/components/compositor/nodes/Pane_layout.tsx +0 -79
- package/templates/src/components/compositor/nodes/tagElements/NodeA_eraser.tsx +0 -26
- package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag_eraser.tsx +0 -61
- package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag_insert.tsx +0 -120
- package/templates/src/components/compositor/nodes/tagElements/NodeBasicTag_settings.tsx +0 -62
- package/templates/src/components/compositor/nodes/tagElements/NodeButton_eraser.tsx +0 -26
|
@@ -9,6 +9,11 @@ import allowInsert from '@/utils/compositor/allowInsert';
|
|
|
9
9
|
import { reservedSlugs } from '@/constants';
|
|
10
10
|
import { NodesHistory, PatchOp } from '@/stores/nodesHistory';
|
|
11
11
|
import { moveNodeAtLocationInContext } from '@/utils/compositor/nodesHelper';
|
|
12
|
+
import {
|
|
13
|
+
rehydrateChildrenFromHtml,
|
|
14
|
+
regenerateCreativePane,
|
|
15
|
+
extractFileIdsFromAst,
|
|
16
|
+
} from '@/utils/compositor/htmlAst';
|
|
12
17
|
import { MarkdownGenerator } from '@/utils/compositor/nodesMarkdownGenerator';
|
|
13
18
|
import {
|
|
14
19
|
hasButtonPayload,
|
|
@@ -19,6 +24,8 @@ import {
|
|
|
19
24
|
toTag,
|
|
20
25
|
} from '@/utils/compositor/typeGuards';
|
|
21
26
|
import { startLoadingAnimation } from '@/utils/helpers';
|
|
27
|
+
import { lispLexer } from '@/utils/actions/lispLexer';
|
|
28
|
+
import { preParseAction } from '@/utils/actions/preParse_Action';
|
|
22
29
|
import { settingsPanelStore } from '@/stores/storykeep';
|
|
23
30
|
import {
|
|
24
31
|
PaneAddMode,
|
|
@@ -26,6 +33,7 @@ import {
|
|
|
26
33
|
ContextPaneMode,
|
|
27
34
|
} from '@/types/compositorTypes';
|
|
28
35
|
import type {
|
|
36
|
+
EditableElementMetadata,
|
|
29
37
|
PanelState,
|
|
30
38
|
BaseNode,
|
|
31
39
|
FlatNode,
|
|
@@ -55,7 +63,6 @@ import type {
|
|
|
55
63
|
} from '@/types/compositorTypes';
|
|
56
64
|
import type { NodeProps, WidgetProps } from '@/types/nodeProps';
|
|
57
65
|
import type { CSSProperties } from 'react';
|
|
58
|
-
import { selectionStore } from '@/stores/selection';
|
|
59
66
|
import type { SelectionRange, SelectionStoreState } from '@/stores/selection';
|
|
60
67
|
import type { CompositorProps } from '@/components/compositor/Compositor';
|
|
61
68
|
|
|
@@ -83,6 +90,7 @@ export class NodesContext {
|
|
|
83
90
|
allNodes = atom<Map<string, BaseNode>>(new Map<string, BaseNode>());
|
|
84
91
|
impressionNodes = atom<Set<ImpressionNode>>(new Set<ImpressionNode>());
|
|
85
92
|
parentNodes = atom<Map<string, string[]>>(new Map<string, string[]>());
|
|
93
|
+
showSaveBypass = atom<boolean>(false);
|
|
86
94
|
hasTitle = atom<boolean>(false);
|
|
87
95
|
hasPanes = atom<boolean>(false);
|
|
88
96
|
isTemplate = atom<boolean>(false);
|
|
@@ -342,15 +350,6 @@ export class NodesContext {
|
|
|
342
350
|
|
|
343
351
|
// click handler based on toolModeVal
|
|
344
352
|
switch (toolModeVal) {
|
|
345
|
-
case `styles`:
|
|
346
|
-
const selection = selectionStore.get();
|
|
347
|
-
if (!selection.isActive)
|
|
348
|
-
handleClickEventDefault(
|
|
349
|
-
node,
|
|
350
|
-
dblClick,
|
|
351
|
-
this.clickedParentLayer.get()
|
|
352
|
-
);
|
|
353
|
-
break;
|
|
354
353
|
case `text`:
|
|
355
354
|
if (
|
|
356
355
|
dblClick &&
|
|
@@ -358,7 +357,6 @@ export class NodesContext {
|
|
|
358
357
|
'tagName' in node &&
|
|
359
358
|
(node.tagName === 'a' || node.tagName === 'button')
|
|
360
359
|
) {
|
|
361
|
-
this.toolModeValStore.set({ value: 'styles' });
|
|
362
360
|
handleClickEventDefault(
|
|
363
361
|
node,
|
|
364
362
|
dblClick,
|
|
@@ -366,7 +364,6 @@ export class NodesContext {
|
|
|
366
364
|
);
|
|
367
365
|
}
|
|
368
366
|
if (dblClick && ![`Markdown`].includes(node.nodeType)) {
|
|
369
|
-
this.toolModeValStore.set({ value: 'styles' });
|
|
370
367
|
handleClickEventDefault(
|
|
371
368
|
node,
|
|
372
369
|
dblClick,
|
|
@@ -374,10 +371,6 @@ export class NodesContext {
|
|
|
374
371
|
);
|
|
375
372
|
}
|
|
376
373
|
break;
|
|
377
|
-
case `eraser`:
|
|
378
|
-
this.handleEraseEvent(node.id);
|
|
379
|
-
this.deleteNode(node.id);
|
|
380
|
-
break;
|
|
381
374
|
default:
|
|
382
375
|
}
|
|
383
376
|
// reset on parentLayer
|
|
@@ -562,34 +555,38 @@ export class NodesContext {
|
|
|
562
555
|
|
|
563
556
|
applyShellToPane(paneId: string, template: TemplatePane) {
|
|
564
557
|
const allNodes = new Map(this.allNodes.get());
|
|
565
|
-
const
|
|
566
|
-
if (!
|
|
558
|
+
const originalPane = allNodes.get(paneId);
|
|
559
|
+
if (!originalPane) return;
|
|
560
|
+
|
|
561
|
+
const paneNode = cloneDeep(originalPane) as PaneNode;
|
|
562
|
+
paneNode.isChanged = true;
|
|
567
563
|
|
|
568
564
|
if (template.bgColour) {
|
|
569
565
|
paneNode.bgColour = template.bgColour;
|
|
570
|
-
|
|
566
|
+
}
|
|
567
|
+
if (template.htmlAst) {
|
|
568
|
+
paneNode.htmlAst = template.htmlAst;
|
|
571
569
|
}
|
|
572
570
|
|
|
571
|
+
allNodes.set(paneId, paneNode);
|
|
572
|
+
|
|
573
573
|
const childrenIds = this.getChildNodeIDs(paneId);
|
|
574
574
|
|
|
575
|
-
const
|
|
575
|
+
const gridNodeRaw = childrenIds
|
|
576
576
|
.map((id) => allNodes.get(id))
|
|
577
|
-
.find((n) => n?.nodeType === 'GridLayoutNode')
|
|
578
|
-
| GridLayoutNode
|
|
579
|
-
| undefined;
|
|
577
|
+
.find((n) => n?.nodeType === 'GridLayoutNode');
|
|
580
578
|
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
.
|
|
579
|
+
if (gridNodeRaw && template.gridLayout) {
|
|
580
|
+
const gridLayoutNode = cloneDeep(gridNodeRaw) as GridLayoutNode;
|
|
581
|
+
gridLayoutNode.isChanged = true;
|
|
584
582
|
|
|
585
|
-
if (gridLayoutNode && template.gridLayout) {
|
|
586
583
|
if (template.gridLayout.parentClasses) {
|
|
587
584
|
gridLayoutNode.parentClasses = template.gridLayout.parentClasses;
|
|
588
585
|
}
|
|
589
586
|
if (template.gridLayout.defaultClasses) {
|
|
590
587
|
gridLayoutNode.defaultClasses = template.gridLayout.defaultClasses;
|
|
591
588
|
}
|
|
592
|
-
gridLayoutNode.
|
|
589
|
+
allNodes.set(gridLayoutNode.id, gridLayoutNode);
|
|
593
590
|
|
|
594
591
|
if (
|
|
595
592
|
template.gridLayout.nodes &&
|
|
@@ -599,30 +596,134 @@ export class NodesContext {
|
|
|
599
596
|
|
|
600
597
|
columnIds.forEach((colId, index) => {
|
|
601
598
|
const templateCol = template.gridLayout!.nodes![index];
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
599
|
+
const colNodeRaw = allNodes.get(colId);
|
|
600
|
+
if (templateCol && colNodeRaw) {
|
|
601
|
+
const liveColNode = cloneDeep(
|
|
602
|
+
colNodeRaw
|
|
603
|
+
) as MarkdownPaneFragmentNode;
|
|
604
|
+
liveColNode.gridClasses = templateCol.gridClasses;
|
|
605
|
+
liveColNode.isChanged = true;
|
|
606
|
+
allNodes.set(colId, liveColNode);
|
|
608
607
|
}
|
|
609
608
|
});
|
|
610
609
|
}
|
|
611
|
-
} else
|
|
612
|
-
const
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
610
|
+
} else {
|
|
611
|
+
const markdownNodes = childrenIds
|
|
612
|
+
.map((id) => allNodes.get(id))
|
|
613
|
+
.filter(
|
|
614
|
+
(n) => n?.nodeType === 'Markdown'
|
|
615
|
+
) as MarkdownPaneFragmentNode[];
|
|
616
|
+
|
|
617
|
+
if (markdownNodes.length > 0 && template.markdown) {
|
|
618
|
+
const primaryMarkdown = cloneDeep(markdownNodes[0]);
|
|
619
|
+
primaryMarkdown.isChanged = true;
|
|
620
|
+
|
|
621
|
+
if (template.markdown.parentClasses) {
|
|
622
|
+
primaryMarkdown.parentClasses = template.markdown.parentClasses;
|
|
623
|
+
}
|
|
624
|
+
if (template.markdown.defaultClasses) {
|
|
625
|
+
primaryMarkdown.defaultClasses = template.markdown.defaultClasses;
|
|
626
|
+
}
|
|
627
|
+
allNodes.set(primaryMarkdown.id, primaryMarkdown);
|
|
619
628
|
}
|
|
620
|
-
primaryMarkdown.isChanged = true;
|
|
621
629
|
}
|
|
622
630
|
|
|
623
631
|
this.allNodes.set(allNodes);
|
|
624
632
|
this.notifyNode(paneId);
|
|
625
633
|
this.notifyNode('root');
|
|
634
|
+
this.showSaveBypass.set(true);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
async updateCreativeAsset(
|
|
638
|
+
paneId: string,
|
|
639
|
+
astId: string,
|
|
640
|
+
updates: Partial<EditableElementMetadata>
|
|
641
|
+
) {
|
|
642
|
+
const allNodes = new Map(this.allNodes.get());
|
|
643
|
+
const originalPane = allNodes.get(paneId);
|
|
644
|
+
|
|
645
|
+
if (!originalPane || originalPane.nodeType !== 'Pane') return;
|
|
646
|
+
|
|
647
|
+
const paneNode = cloneDeep(originalPane) as PaneNode;
|
|
648
|
+
if (!paneNode.htmlAst) return;
|
|
649
|
+
|
|
650
|
+
if (updates.tagName === 'a' && updates.buttonPayload?.callbackPayload) {
|
|
651
|
+
try {
|
|
652
|
+
const config = (window as any).TRACTSTACK_CONFIG || {};
|
|
653
|
+
const lexed = lispLexer(updates.buttonPayload.callbackPayload);
|
|
654
|
+
|
|
655
|
+
const resolvedHref = preParseAction(
|
|
656
|
+
lexed,
|
|
657
|
+
paneNode.slug,
|
|
658
|
+
!!paneNode.isContextPane,
|
|
659
|
+
config
|
|
660
|
+
);
|
|
661
|
+
|
|
662
|
+
if (resolvedHref) {
|
|
663
|
+
updates.href = resolvedHref;
|
|
664
|
+
}
|
|
665
|
+
} catch (e) {
|
|
666
|
+
console.warn('[Nodes] Failed to resolve href from ActionLisp:', e);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
let newHtmlAst = await regenerateCreativePane(
|
|
671
|
+
paneNode.htmlAst,
|
|
672
|
+
astId,
|
|
673
|
+
updates
|
|
674
|
+
);
|
|
675
|
+
|
|
676
|
+
if (newHtmlAst.editableElements[astId]) {
|
|
677
|
+
newHtmlAst.editableElements[astId] = {
|
|
678
|
+
...newHtmlAst.editableElements[astId],
|
|
679
|
+
...updates,
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
paneNode.htmlAst = newHtmlAst;
|
|
684
|
+
paneNode.isChanged = true;
|
|
685
|
+
|
|
686
|
+
this.modifyNodes([paneNode]);
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
updateCreativePane(paneId: string, containerId: string, htmlContent: string) {
|
|
690
|
+
const allNodes = new Map(this.allNodes.get());
|
|
691
|
+
const originalPane = allNodes.get(paneId);
|
|
692
|
+
|
|
693
|
+
// 1. Validation and Clone (matching applyShellToPane pattern)
|
|
694
|
+
if (!originalPane || originalPane.nodeType !== 'Pane') return;
|
|
695
|
+
|
|
696
|
+
// Deep clone ensures we don't mutate state outside the atom update
|
|
697
|
+
const paneNode = cloneDeep(originalPane) as PaneNode;
|
|
698
|
+
|
|
699
|
+
// Guard: Ensure we are in HTML mode
|
|
700
|
+
if (!('htmlAst' in paneNode) || !paneNode.htmlAst) return;
|
|
701
|
+
|
|
702
|
+
// 2. Logic: Rehydrate and Patch
|
|
703
|
+
const newChildren = rehydrateChildrenFromHtml(htmlContent);
|
|
704
|
+
|
|
705
|
+
// Recursive patcher to find the container in the cloned tree
|
|
706
|
+
const patchNode = (nodes: any[]): boolean => {
|
|
707
|
+
for (const node of nodes) {
|
|
708
|
+
if (node.id === containerId) {
|
|
709
|
+
node.children = newChildren;
|
|
710
|
+
return true;
|
|
711
|
+
}
|
|
712
|
+
if (node.children && node.children.length > 0) {
|
|
713
|
+
if (patchNode(node.children)) return true;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
return false;
|
|
717
|
+
};
|
|
718
|
+
|
|
719
|
+
// 3. Commit
|
|
720
|
+
if (patchNode(paneNode.htmlAst.tree)) {
|
|
721
|
+
paneNode.isChanged = true;
|
|
722
|
+
allNodes.set(paneId, paneNode);
|
|
723
|
+
this.allNodes.set(allNodes);
|
|
724
|
+
this.notifyNode(paneId);
|
|
725
|
+
this.showSaveBypass.set(true);
|
|
726
|
+
}
|
|
626
727
|
}
|
|
627
728
|
|
|
628
729
|
/**
|
|
@@ -2001,9 +2102,7 @@ export class NodesContext {
|
|
|
2001
2102
|
});
|
|
2002
2103
|
break;
|
|
2003
2104
|
}
|
|
2004
|
-
|
|
2005
|
-
this.toolModeValStore.set({ value: 'text' });
|
|
2006
|
-
else this.toolModeValStore.set({ value: 'styles' });
|
|
2105
|
+
this.toolModeValStore.set({ value: 'text' });
|
|
2007
2106
|
this.notifyNode('root');
|
|
2008
2107
|
}
|
|
2009
2108
|
|
|
@@ -2585,7 +2684,15 @@ export class NodesContext {
|
|
|
2585
2684
|
getPaneImageFileIds(paneId: string): string[] {
|
|
2586
2685
|
const paneNode = this.allNodes.get().get(paneId);
|
|
2587
2686
|
if (!paneNode || paneNode.nodeType !== 'Pane') return [];
|
|
2687
|
+
const pane = paneNode as PaneNode;
|
|
2688
|
+
|
|
2689
|
+
// 1. Extract from Creative AST (if present)
|
|
2690
|
+
let creativeFileIds: string[] = [];
|
|
2691
|
+
if (pane.htmlAst) {
|
|
2692
|
+
creativeFileIds = extractFileIdsFromAst(pane.htmlAst);
|
|
2693
|
+
}
|
|
2588
2694
|
|
|
2695
|
+
// 2. Extract from Standard Nodes (TagElement, BgPane)
|
|
2589
2696
|
const allNodes = this.getNodesRecursively(paneNode);
|
|
2590
2697
|
|
|
2591
2698
|
const embeddedFileIds = allNodes
|
|
@@ -2612,7 +2719,10 @@ export class NodesContext {
|
|
|
2612
2719
|
.map((node) => node.fileId)
|
|
2613
2720
|
.filter((id): id is string => id !== undefined);
|
|
2614
2721
|
|
|
2615
|
-
|
|
2722
|
+
// 3. Merge unique IDs
|
|
2723
|
+
return Array.from(
|
|
2724
|
+
new Set([...embeddedFileIds, ...bgFileIds, ...creativeFileIds])
|
|
2725
|
+
);
|
|
2616
2726
|
}
|
|
2617
2727
|
|
|
2618
2728
|
getPaneImagesMap(): Record<string, string[]> {
|
|
@@ -3193,6 +3303,11 @@ export class NodesContext {
|
|
|
3193
3303
|
paneTemplate: TemplatePane,
|
|
3194
3304
|
newPaneId: string
|
|
3195
3305
|
): BaseNode[] {
|
|
3306
|
+
if (paneTemplate.htmlAst) {
|
|
3307
|
+
// No nodes when in htmlAst mode
|
|
3308
|
+
return [];
|
|
3309
|
+
}
|
|
3310
|
+
|
|
3196
3311
|
let allNodes: BaseNode[] = [];
|
|
3197
3312
|
|
|
3198
3313
|
// 1. Process Markdown Content
|
|
@@ -3350,8 +3465,6 @@ export class NodesContext {
|
|
|
3350
3465
|
};
|
|
3351
3466
|
allNodes.push(bgPaneNode);
|
|
3352
3467
|
}
|
|
3353
|
-
// This helper only processes nodes, it doesn't modify the paneTemplate.
|
|
3354
|
-
// The deletion of `duplicatedPane.bgPane` will remain in `addTemplatePane`.
|
|
3355
3468
|
}
|
|
3356
3469
|
|
|
3357
3470
|
return allNodes;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { atom } from 'nanostores';
|
|
1
|
+
import { atom, computed } from 'nanostores';
|
|
2
2
|
|
|
3
3
|
export interface OrphanAnalysisData {
|
|
4
4
|
storyFragments: Record<string, string[]>;
|
|
@@ -39,35 +39,11 @@ const defaultOrphanState: OrphanAnalysisState = {
|
|
|
39
39
|
lastFetched: null,
|
|
40
40
|
};
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
},
|
|
48
|
-
|
|
49
|
-
subscribe: (callback: (value: OrphanAnalysisState) => void) => {
|
|
50
|
-
const tenantId = getCurrentTenantId();
|
|
51
|
-
return tenantOrphanAnalysis.subscribe((analysis) => {
|
|
52
|
-
callback(analysis[tenantId] || defaultOrphanState);
|
|
53
|
-
});
|
|
54
|
-
},
|
|
55
|
-
lc: 0,
|
|
56
|
-
listen: function (callback: any) {
|
|
57
|
-
return this.subscribe(callback);
|
|
58
|
-
},
|
|
59
|
-
notify: function () {},
|
|
60
|
-
off: function () {},
|
|
61
|
-
get value() {
|
|
62
|
-
return this.get();
|
|
63
|
-
},
|
|
64
|
-
set: function () {}, // Orphan store is read-only for components
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
return store;
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
export const orphanAnalysisStore = createOrphanAnalysisStore();
|
|
42
|
+
// Computed store that slices the state for the current tenant
|
|
43
|
+
export const orphanAnalysisStore = computed(tenantOrphanAnalysis, (states) => {
|
|
44
|
+
const tenantId = getCurrentTenantId();
|
|
45
|
+
return states[tenantId] || defaultOrphanState;
|
|
46
|
+
});
|
|
71
47
|
|
|
72
48
|
function updateTenantState(
|
|
73
49
|
tenantId: string,
|
|
@@ -32,14 +32,6 @@ export const pendingHomePageSlugStore = atom<string | null>(null);
|
|
|
32
32
|
export const saasModalOpenStore = atom<boolean>(false);
|
|
33
33
|
export const sandboxTokenStore = atom<string | null>(null);
|
|
34
34
|
|
|
35
|
-
export type ToolModeVal =
|
|
36
|
-
| 'styles'
|
|
37
|
-
| 'text'
|
|
38
|
-
| 'insert'
|
|
39
|
-
| 'eraser'
|
|
40
|
-
| 'move'
|
|
41
|
-
| 'debug';
|
|
42
|
-
|
|
43
35
|
export type ToolAddMode =
|
|
44
36
|
| 'p'
|
|
45
37
|
| 'h2'
|
|
@@ -4,15 +4,7 @@ export type LispToken = string | number | LispToken[];
|
|
|
4
4
|
|
|
5
5
|
export type ViewportKey = 'mobile' | 'tablet' | 'desktop' | 'auto';
|
|
6
6
|
export type ViewportAuto = 'mobile' | 'tablet' | 'desktop';
|
|
7
|
-
export type ToolModeVal =
|
|
8
|
-
| 'styles'
|
|
9
|
-
| 'text'
|
|
10
|
-
| 'insert'
|
|
11
|
-
| 'eraser'
|
|
12
|
-
| 'move'
|
|
13
|
-
| 'layout'
|
|
14
|
-
| 'designLibrary'
|
|
15
|
-
| 'debug';
|
|
7
|
+
export type ToolModeVal = 'text' | 'insert';
|
|
16
8
|
|
|
17
9
|
export const toolAddModes = [
|
|
18
10
|
'p',
|
|
@@ -27,6 +19,7 @@ export const toolAddModes = [
|
|
|
27
19
|
'interactiveDisclosure',
|
|
28
20
|
'identify',
|
|
29
21
|
'toggle',
|
|
22
|
+
'span',
|
|
30
23
|
//"aside",
|
|
31
24
|
] as const;
|
|
32
25
|
export type ToolAddMode = (typeof toolAddModes)[number];
|
|
@@ -55,6 +48,51 @@ export enum PaneConfigMode {
|
|
|
55
48
|
CODEHOOK = 'CODEHOOK',
|
|
56
49
|
}
|
|
57
50
|
|
|
51
|
+
export interface HtmlAstNode {
|
|
52
|
+
tag: string;
|
|
53
|
+
attrs?: Record<string, string>;
|
|
54
|
+
children?: HtmlAstNode[];
|
|
55
|
+
text?: string;
|
|
56
|
+
id?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export type CreativeButtonPayload = {
|
|
60
|
+
callbackPayload: string;
|
|
61
|
+
isExternalUrl?: boolean;
|
|
62
|
+
bunnyPayload?: {
|
|
63
|
+
t: string;
|
|
64
|
+
videoId: string | null;
|
|
65
|
+
slug?: string;
|
|
66
|
+
isContext?: boolean;
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export interface EditableElementMetadata {
|
|
71
|
+
astId: string;
|
|
72
|
+
tagName: string;
|
|
73
|
+
src?: string;
|
|
74
|
+
srcSet?: string;
|
|
75
|
+
fileId?: string;
|
|
76
|
+
base64Data?: string;
|
|
77
|
+
alt?: string;
|
|
78
|
+
href?: string;
|
|
79
|
+
buttonPayload?: CreativeButtonPayload;
|
|
80
|
+
isCssBackground?: boolean;
|
|
81
|
+
collection?: string;
|
|
82
|
+
image?: string;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface CreativePanePayload {
|
|
86
|
+
css: string;
|
|
87
|
+
viewportCss: {
|
|
88
|
+
xs: string;
|
|
89
|
+
md: string;
|
|
90
|
+
xl: string;
|
|
91
|
+
};
|
|
92
|
+
tree: HtmlAstNode[];
|
|
93
|
+
editableElements: Record<string, EditableElementMetadata>;
|
|
94
|
+
}
|
|
95
|
+
|
|
58
96
|
export enum StoryFragmentMode {
|
|
59
97
|
DEFAULT = 'DEFAULT',
|
|
60
98
|
SLUG = 'SLUG',
|
|
@@ -73,6 +111,7 @@ export type SettingsPanelSignal = {
|
|
|
73
111
|
nodeId: string;
|
|
74
112
|
childId?: string;
|
|
75
113
|
layer?: number;
|
|
114
|
+
view?: string;
|
|
76
115
|
className?: string;
|
|
77
116
|
minimized?: boolean;
|
|
78
117
|
expanded?: boolean;
|
|
@@ -146,7 +185,8 @@ export type Tag =
|
|
|
146
185
|
| 'belief'
|
|
147
186
|
| 'identify'
|
|
148
187
|
| 'toggle'
|
|
149
|
-
| 'code'
|
|
188
|
+
| 'code'
|
|
189
|
+
| 'span';
|
|
150
190
|
|
|
151
191
|
export const tagTitles: Record<Tag, string> = {
|
|
152
192
|
p: 'Paragraph',
|
|
@@ -168,6 +208,7 @@ export const tagTitles: Record<Tag, string> = {
|
|
|
168
208
|
belief: 'Belief Select Widget',
|
|
169
209
|
toggle: 'Belief Toggle Widget',
|
|
170
210
|
identify: 'Identify As Widget',
|
|
211
|
+
span: 'Creative Span',
|
|
171
212
|
};
|
|
172
213
|
|
|
173
214
|
export type NodeType =
|
|
@@ -232,6 +273,7 @@ export interface PaneNode extends BaseNode {
|
|
|
232
273
|
codeHookPayload?: {
|
|
233
274
|
[key: string]: string;
|
|
234
275
|
};
|
|
276
|
+
htmlAst?: CreativePanePayload;
|
|
235
277
|
heldBeliefs?: BeliefDatum;
|
|
236
278
|
withheldBeliefs?: BeliefDatum;
|
|
237
279
|
}
|
|
@@ -566,6 +608,7 @@ export type ParentBasePanelProps = {
|
|
|
566
608
|
parentNode?: FlatNode | PaneNode;
|
|
567
609
|
config?: BrandConfig | null;
|
|
568
610
|
layer?: number;
|
|
611
|
+
view?: string;
|
|
569
612
|
className?: string;
|
|
570
613
|
childId?: string;
|
|
571
614
|
availableCodeHooks?: string[];
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { TractStackAPI } from '@/utils/api';
|
|
2
|
+
import { sandboxTokenStore } from '@/stores/storykeep';
|
|
3
|
+
|
|
4
|
+
interface AiGenerationOptions {
|
|
5
|
+
prompt: string;
|
|
6
|
+
context: string;
|
|
7
|
+
expectJson: boolean;
|
|
8
|
+
isSandboxMode: boolean;
|
|
9
|
+
maxTokens?: number;
|
|
10
|
+
temperature?: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const callAskLemurAPI = async ({
|
|
14
|
+
prompt,
|
|
15
|
+
context,
|
|
16
|
+
expectJson,
|
|
17
|
+
isSandboxMode,
|
|
18
|
+
maxTokens = 5000,
|
|
19
|
+
temperature = 0.5,
|
|
20
|
+
}: AiGenerationOptions): Promise<string> => {
|
|
21
|
+
const tenantId =
|
|
22
|
+
(window as any).TRACTSTACK_CONFIG?.tenantId ||
|
|
23
|
+
import.meta.env.PUBLIC_TENANTID ||
|
|
24
|
+
'default';
|
|
25
|
+
|
|
26
|
+
const requestBody = {
|
|
27
|
+
prompt,
|
|
28
|
+
input_text: context,
|
|
29
|
+
final_model: '',
|
|
30
|
+
temperature,
|
|
31
|
+
max_tokens: maxTokens,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
let resultData: any;
|
|
35
|
+
|
|
36
|
+
if (isSandboxMode) {
|
|
37
|
+
const token = sandboxTokenStore.get();
|
|
38
|
+
const response = await fetch(`/api/sandbox`, {
|
|
39
|
+
method: 'POST',
|
|
40
|
+
headers: {
|
|
41
|
+
'Content-Type': 'application/json',
|
|
42
|
+
'X-Tenant-ID': tenantId,
|
|
43
|
+
'X-Sandbox-Token': token || '',
|
|
44
|
+
},
|
|
45
|
+
credentials: 'include',
|
|
46
|
+
body: JSON.stringify({ action: 'askLemur', payload: requestBody }),
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
if (!response.ok) {
|
|
50
|
+
const errorText = await response.text();
|
|
51
|
+
throw new Error(`Sandbox API failed: ${response.status} ${errorText}`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const json = await response.json();
|
|
55
|
+
if (!json.success) {
|
|
56
|
+
throw new Error(json.error || 'Sandbox generation failed');
|
|
57
|
+
}
|
|
58
|
+
resultData = json.data;
|
|
59
|
+
} else {
|
|
60
|
+
const api = new TractStackAPI(tenantId);
|
|
61
|
+
const response = await api.post('/api/v1/aai/askLemur', requestBody);
|
|
62
|
+
|
|
63
|
+
if (!response.success) {
|
|
64
|
+
throw new Error(
|
|
65
|
+
response.error || 'Generation failed to return valid response.'
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
resultData = response.data;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (!resultData?.response) {
|
|
72
|
+
throw new Error('Generation failed to return a response object.');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const rawResponseData = resultData.response;
|
|
76
|
+
|
|
77
|
+
if (expectJson && typeof rawResponseData === 'object') {
|
|
78
|
+
return JSON.stringify(rawResponseData);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (typeof rawResponseData === 'string') {
|
|
82
|
+
let responseString = rawResponseData;
|
|
83
|
+
// Clean up markdown code blocks if present to ensure clean parsing by caller
|
|
84
|
+
if (responseString.startsWith('```json')) {
|
|
85
|
+
responseString = responseString.slice(7, -3).trim();
|
|
86
|
+
} else if (responseString.startsWith('```html')) {
|
|
87
|
+
responseString = responseString.slice(7, -3).trim();
|
|
88
|
+
}
|
|
89
|
+
return responseString;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
throw new Error('Unexpected response format received from API.');
|
|
93
|
+
};
|