astro-tractstack 2.1.2 → 2.2.0
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/custom/minimal/CodeHook.astro +1 -0
- package/templates/custom/with-examples/CodeHook.astro +1 -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 +5 -46
- package/templates/src/pages/api/css.ts +149 -0
- package/templates/src/pages/context/[...contextSlug].astro +1 -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
|
@@ -1,269 +0,0 @@
|
|
|
1
|
-
import { type CSSProperties, useEffect, useState } from 'react';
|
|
2
|
-
import { useStore } from '@nanostores/react';
|
|
3
|
-
import ArchiveBoxArrowDownIcon from '@heroicons/react/24/outline/ArchiveBoxArrowDownIcon';
|
|
4
|
-
import ArrowPathRoundedSquareIcon from '@heroicons/react/24/outline/ArrowPathRoundedSquareIcon';
|
|
5
|
-
import ArrowDownTrayIcon from '@heroicons/react/24/outline/ArrowDownTrayIcon';
|
|
6
|
-
import CheckIcon from '@heroicons/react/24/outline/CheckIcon';
|
|
7
|
-
import SparklesIcon from '@heroicons/react/24/solid/SparklesIcon';
|
|
8
|
-
import { viewportKeyStore } from '@/stores/storykeep';
|
|
9
|
-
import { getCtx } from '@/stores/nodes';
|
|
10
|
-
import { RenderChildren } from './RenderChildren';
|
|
11
|
-
import { CodeHookContainer } from './Pane';
|
|
12
|
-
import type { NodeProps } from '@/types/nodeProps';
|
|
13
|
-
import type { BgImageNode, ArtpackImageNode } from '@/types/compositorTypes';
|
|
14
|
-
import { SaveToLibraryModal } from '@/components/edit/state/SaveToLibraryModal';
|
|
15
|
-
import { RestylePaneModal } from '@/components/edit/pane/RestylePaneModal';
|
|
16
|
-
import { AiRestylePaneModal } from '@/components/edit/pane/AiRestylePaneModal';
|
|
17
|
-
import { selectionStore } from '@/stores/selection';
|
|
18
|
-
import { copyPaneToClipboard } from '@/utils/compositor/designLibraryHelper';
|
|
19
|
-
|
|
20
|
-
function getSizeClasses(
|
|
21
|
-
size: string,
|
|
22
|
-
side: 'image' | 'content',
|
|
23
|
-
viewport: string
|
|
24
|
-
): string {
|
|
25
|
-
if (viewport === 'mobile') {
|
|
26
|
-
return 'w-full';
|
|
27
|
-
}
|
|
28
|
-
switch (size) {
|
|
29
|
-
case 'narrow':
|
|
30
|
-
return side === 'image' ? 'w-1/3' : 'w-2/3';
|
|
31
|
-
case 'wide':
|
|
32
|
-
return side === 'image' ? 'w-2/3' : 'w-1/3';
|
|
33
|
-
default:
|
|
34
|
-
return 'w-1/2';
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export const Pane_DesignLibrary = (props: NodeProps) => {
|
|
39
|
-
const ctx = getCtx(props);
|
|
40
|
-
const { isRestyleModalOpen, isAiRestyleModalOpen } = useStore(
|
|
41
|
-
selectionStore,
|
|
42
|
-
{
|
|
43
|
-
keys: ['isRestyleModalOpen', 'isAiRestyleModalOpen'],
|
|
44
|
-
}
|
|
45
|
-
);
|
|
46
|
-
const [currentViewport, setCurrentViewport] = useState(
|
|
47
|
-
viewportKeyStore.get().value
|
|
48
|
-
);
|
|
49
|
-
|
|
50
|
-
useEffect(() => {
|
|
51
|
-
const unsubscribeViewport = viewportKeyStore.subscribe((newViewport) => {
|
|
52
|
-
setCurrentViewport(newViewport.value);
|
|
53
|
-
});
|
|
54
|
-
return () => unsubscribeViewport();
|
|
55
|
-
}, []);
|
|
56
|
-
|
|
57
|
-
const wrapperClasses = `grid ${ctx.getNodeClasses(props.nodeId, currentViewport)}`;
|
|
58
|
-
const contentClasses = 'relative w-full h-auto justify-self-start';
|
|
59
|
-
const contentStyles: CSSProperties = {
|
|
60
|
-
...ctx.getNodeCSSPropertiesStyles(props.nodeId),
|
|
61
|
-
gridArea: '1/1/1/1',
|
|
62
|
-
position: 'relative',
|
|
63
|
-
zIndex: 1,
|
|
64
|
-
};
|
|
65
|
-
const codeHookPayload = ctx.getNodeCodeHookPayload(props.nodeId);
|
|
66
|
-
const [children, setChildren] = useState<string[]>([
|
|
67
|
-
...ctx.getChildNodeIDs(props.nodeId),
|
|
68
|
-
]);
|
|
69
|
-
const [isSaveModalOpen, setIsSaveModalOpen] = useState(false);
|
|
70
|
-
const [wasCopied, setWasCopied] = useState(false);
|
|
71
|
-
const [renderCount, setRenderCount] = useState(0);
|
|
72
|
-
|
|
73
|
-
const getPaneId = (): string => `pane-${props.nodeId}`;
|
|
74
|
-
|
|
75
|
-
useEffect(() => {
|
|
76
|
-
const unsubscribe = ctx.notifications.subscribe(props.nodeId, () => {
|
|
77
|
-
setChildren([...ctx.getChildNodeIDs(props.nodeId)]);
|
|
78
|
-
setRenderCount((prev) => prev + 1);
|
|
79
|
-
});
|
|
80
|
-
return unsubscribe;
|
|
81
|
-
}, [props.nodeId, ctx.notifications]);
|
|
82
|
-
|
|
83
|
-
const handleRestyleClick = (e: React.MouseEvent) => {
|
|
84
|
-
e.stopPropagation();
|
|
85
|
-
selectionStore.setKey('paneToRestyleId', props.nodeId);
|
|
86
|
-
selectionStore.setKey('isRestyleModalOpen', true);
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
const handleAiRestyleClick = (e: React.MouseEvent) => {
|
|
90
|
-
e.stopPropagation();
|
|
91
|
-
selectionStore.setKey('paneToRestyleId', props.nodeId);
|
|
92
|
-
selectionStore.setKey('isAiRestyleModalOpen', true);
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
const handleSaveClick = (e: React.MouseEvent) => {
|
|
96
|
-
e.stopPropagation();
|
|
97
|
-
setIsSaveModalOpen(true);
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
const handleCopyToClipboard = async (e: React.MouseEvent) => {
|
|
101
|
-
e.stopPropagation();
|
|
102
|
-
const success = await copyPaneToClipboard(props.nodeId);
|
|
103
|
-
if (success) {
|
|
104
|
-
setWasCopied(true);
|
|
105
|
-
setTimeout(() => setWasCopied(false), 2000);
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
const Buttons = () => (
|
|
110
|
-
<div className="absolute left-2 top-2 z-10 flex flex-row gap-x-2">
|
|
111
|
-
{!props.isSandboxMode && (
|
|
112
|
-
<button
|
|
113
|
-
title="Save Pane to Design Library"
|
|
114
|
-
onClick={handleSaveClick}
|
|
115
|
-
className="flex h-10 w-10 items-center justify-center rounded-full bg-cyan-600 p-1.5 shadow-lg hover:bg-cyan-700"
|
|
116
|
-
>
|
|
117
|
-
<ArchiveBoxArrowDownIcon className="h-7 w-7 text-white" />
|
|
118
|
-
</button>
|
|
119
|
-
)}
|
|
120
|
-
<button
|
|
121
|
-
title="Re-Color"
|
|
122
|
-
onClick={handleAiRestyleClick}
|
|
123
|
-
className="flex h-10 w-10 items-center justify-center rounded-full bg-purple-600 p-1.5 shadow-lg hover:bg-purple-700"
|
|
124
|
-
>
|
|
125
|
-
<SparklesIcon className="h-5 w-5 text-white" />
|
|
126
|
-
</button>
|
|
127
|
-
<button
|
|
128
|
-
title="Restyle Pane from Design Library"
|
|
129
|
-
onClick={handleRestyleClick}
|
|
130
|
-
className="flex h-10 w-10 items-center justify-center rounded-full bg-blue-600 p-1.5 shadow-lg hover:bg-blue-700"
|
|
131
|
-
>
|
|
132
|
-
<ArrowPathRoundedSquareIcon className="h-7 w-7 text-white" />
|
|
133
|
-
</button>
|
|
134
|
-
<button
|
|
135
|
-
title="Copy Pane Design to Clipboard"
|
|
136
|
-
onClick={handleCopyToClipboard}
|
|
137
|
-
className={`flex h-10 w-10 items-center justify-center rounded-full p-1.5 shadow-lg transition-colors ${
|
|
138
|
-
wasCopied ? 'bg-green-500' : 'bg-gray-600 hover:bg-gray-700'
|
|
139
|
-
}`}
|
|
140
|
-
>
|
|
141
|
-
{wasCopied ? (
|
|
142
|
-
<CheckIcon className="h-7 w-7 text-white" />
|
|
143
|
-
) : (
|
|
144
|
-
<ArrowDownTrayIcon className="h-7 w-7 text-white" />
|
|
145
|
-
)}
|
|
146
|
-
</button>
|
|
147
|
-
</div>
|
|
148
|
-
);
|
|
149
|
-
|
|
150
|
-
const allNodes = ctx.allNodes.get();
|
|
151
|
-
const bgNode = children
|
|
152
|
-
.map((id) => allNodes.get(id))
|
|
153
|
-
.find(
|
|
154
|
-
(node) =>
|
|
155
|
-
node?.nodeType === 'BgPane' &&
|
|
156
|
-
'type' in node &&
|
|
157
|
-
(node.type === 'background-image' || node.type === 'artpack-image')
|
|
158
|
-
) as (BgImageNode | ArtpackImageNode) | undefined;
|
|
159
|
-
|
|
160
|
-
const useFlexLayout =
|
|
161
|
-
bgNode &&
|
|
162
|
-
(bgNode.position === 'leftBleed' || bgNode.position === 'rightBleed');
|
|
163
|
-
const deferFlexLayout =
|
|
164
|
-
bgNode && (bgNode.position === 'left' || bgNode.position === 'right');
|
|
165
|
-
|
|
166
|
-
const flexDirection =
|
|
167
|
-
currentViewport === 'mobile'
|
|
168
|
-
? 'flex-col'
|
|
169
|
-
: bgNode?.position === 'rightBleed'
|
|
170
|
-
? 'flex-row-reverse'
|
|
171
|
-
: 'flex-row';
|
|
172
|
-
|
|
173
|
-
return (
|
|
174
|
-
<div id={getPaneId()} className="pane min-h-16">
|
|
175
|
-
<div
|
|
176
|
-
id={ctx.getNodeSlug(props.nodeId)}
|
|
177
|
-
className={useFlexLayout ? '' : wrapperClasses}
|
|
178
|
-
>
|
|
179
|
-
{codeHookPayload ? (
|
|
180
|
-
<div className={contentClasses} style={contentStyles}>
|
|
181
|
-
<Buttons />
|
|
182
|
-
<CodeHookContainer payload={codeHookPayload} />
|
|
183
|
-
</div>
|
|
184
|
-
) : useFlexLayout ? (
|
|
185
|
-
<div
|
|
186
|
-
className={`flex flex-nowrap ${flexDirection} ${ctx.getNodeClasses(props.nodeId, currentViewport)}`}
|
|
187
|
-
>
|
|
188
|
-
<div
|
|
189
|
-
className={`relative overflow-hidden ${getSizeClasses(bgNode.size || 'equal', 'image', currentViewport)}`}
|
|
190
|
-
>
|
|
191
|
-
<RenderChildren
|
|
192
|
-
children={children.filter((id) => {
|
|
193
|
-
const node = allNodes.get(id);
|
|
194
|
-
return node?.nodeType === 'BgPane';
|
|
195
|
-
})}
|
|
196
|
-
nodeProps={props}
|
|
197
|
-
key={`bg-children-${props.nodeId}-${renderCount}`}
|
|
198
|
-
/>
|
|
199
|
-
</div>
|
|
200
|
-
<div
|
|
201
|
-
className={`${contentClasses} ${getSizeClasses(bgNode.size || 'equal', 'content', currentViewport)}`}
|
|
202
|
-
style={ctx.getNodeCSSPropertiesStyles(props.nodeId)}
|
|
203
|
-
onClick={(e) => {
|
|
204
|
-
ctx.setClickedNodeId(props.nodeId);
|
|
205
|
-
e.stopPropagation();
|
|
206
|
-
}}
|
|
207
|
-
>
|
|
208
|
-
<Buttons />
|
|
209
|
-
<RenderChildren
|
|
210
|
-
children={children.filter((id) => {
|
|
211
|
-
const node = allNodes.get(id);
|
|
212
|
-
return node?.nodeType !== 'BgPane';
|
|
213
|
-
})}
|
|
214
|
-
nodeProps={props}
|
|
215
|
-
key={`content-children-${props.nodeId}-${renderCount}`}
|
|
216
|
-
/>
|
|
217
|
-
</div>
|
|
218
|
-
</div>
|
|
219
|
-
) : deferFlexLayout ? (
|
|
220
|
-
<div
|
|
221
|
-
className={contentClasses}
|
|
222
|
-
style={contentStyles}
|
|
223
|
-
onClick={(e) => {
|
|
224
|
-
ctx.setClickedNodeId(props.nodeId);
|
|
225
|
-
e.stopPropagation();
|
|
226
|
-
}}
|
|
227
|
-
>
|
|
228
|
-
<Buttons />
|
|
229
|
-
<RenderChildren
|
|
230
|
-
children={children.filter((id) => {
|
|
231
|
-
const node = allNodes.get(id);
|
|
232
|
-
return node?.nodeType !== 'BgPane';
|
|
233
|
-
})}
|
|
234
|
-
nodeProps={props}
|
|
235
|
-
key={`content-children-${props.nodeId}-${renderCount}`}
|
|
236
|
-
/>
|
|
237
|
-
</div>
|
|
238
|
-
) : (
|
|
239
|
-
<div
|
|
240
|
-
className={contentClasses}
|
|
241
|
-
style={contentStyles}
|
|
242
|
-
onClick={(e) => {
|
|
243
|
-
ctx.setClickedNodeId(props.nodeId);
|
|
244
|
-
e.stopPropagation();
|
|
245
|
-
}}
|
|
246
|
-
>
|
|
247
|
-
<Buttons />
|
|
248
|
-
<RenderChildren
|
|
249
|
-
children={children}
|
|
250
|
-
nodeProps={props}
|
|
251
|
-
key={`render-children-${props.nodeId}-${renderCount}`}
|
|
252
|
-
/>
|
|
253
|
-
</div>
|
|
254
|
-
)}
|
|
255
|
-
</div>
|
|
256
|
-
{isSaveModalOpen && (
|
|
257
|
-
<SaveToLibraryModal
|
|
258
|
-
paneId={props.nodeId}
|
|
259
|
-
onClose={() => setIsSaveModalOpen(false)}
|
|
260
|
-
/>
|
|
261
|
-
)}
|
|
262
|
-
|
|
263
|
-
{isRestyleModalOpen && <RestylePaneModal />}
|
|
264
|
-
{isAiRestyleModalOpen && (
|
|
265
|
-
<AiRestylePaneModal isSandboxMode={props.isSandboxMode} />
|
|
266
|
-
)}
|
|
267
|
-
</div>
|
|
268
|
-
);
|
|
269
|
-
};
|
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
import { type CSSProperties, useEffect, useState } from 'react';
|
|
2
|
-
import TrashIcon from '@heroicons/react/24/outline/TrashIcon';
|
|
3
|
-
import { viewportKeyStore } from '@/stores/storykeep';
|
|
4
|
-
import { getCtx } from '@/stores/nodes';
|
|
5
|
-
import { RenderChildren } from './RenderChildren';
|
|
6
|
-
import { CodeHookContainer } from './Pane';
|
|
7
|
-
import type { NodeProps } from '@/types/nodeProps';
|
|
8
|
-
import type { BgImageNode, ArtpackImageNode } from '@/types/compositorTypes';
|
|
9
|
-
|
|
10
|
-
function getSizeClasses(
|
|
11
|
-
size: string,
|
|
12
|
-
side: 'image' | 'content',
|
|
13
|
-
viewport: string
|
|
14
|
-
): string {
|
|
15
|
-
if (viewport === 'mobile') {
|
|
16
|
-
return 'w-full';
|
|
17
|
-
}
|
|
18
|
-
switch (size) {
|
|
19
|
-
case 'narrow':
|
|
20
|
-
return side === 'image' ? 'w-1/3' : 'w-2/3';
|
|
21
|
-
case 'wide':
|
|
22
|
-
return side === 'image' ? 'w-2/3' : 'w-1/3';
|
|
23
|
-
default:
|
|
24
|
-
return 'w-1/2';
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export const PaneEraser = (props: NodeProps) => {
|
|
29
|
-
const ctx = getCtx(props);
|
|
30
|
-
const [currentViewport, setCurrentViewport] = useState(
|
|
31
|
-
viewportKeyStore.get().value
|
|
32
|
-
);
|
|
33
|
-
|
|
34
|
-
useEffect(() => {
|
|
35
|
-
const unsubscribeViewport = viewportKeyStore.subscribe((newViewport) => {
|
|
36
|
-
setCurrentViewport(newViewport.value);
|
|
37
|
-
});
|
|
38
|
-
return () => unsubscribeViewport();
|
|
39
|
-
}, []);
|
|
40
|
-
|
|
41
|
-
const wrapperClasses = `grid ${ctx.getNodeClasses(props.nodeId, currentViewport)}`;
|
|
42
|
-
const contentClasses = 'relative w-full h-auto justify-self-start';
|
|
43
|
-
const contentStyles: CSSProperties = {
|
|
44
|
-
...ctx.getNodeCSSPropertiesStyles(props.nodeId),
|
|
45
|
-
gridArea: '1/1/1/1',
|
|
46
|
-
position: 'relative',
|
|
47
|
-
zIndex: 1,
|
|
48
|
-
};
|
|
49
|
-
const codeHookPayload = ctx.getNodeCodeHookPayload(props.nodeId);
|
|
50
|
-
const [children, setChildren] = useState<string[]>([
|
|
51
|
-
...ctx.getChildNodeIDs(props.nodeId),
|
|
52
|
-
]);
|
|
53
|
-
const [renderCount, setRenderCount] = useState(0);
|
|
54
|
-
|
|
55
|
-
const getPaneId = (): string => `pane-${props.nodeId}`;
|
|
56
|
-
|
|
57
|
-
useEffect(() => {
|
|
58
|
-
const unsubscribe = ctx.notifications.subscribe(props.nodeId, () => {
|
|
59
|
-
setChildren([...ctx.getChildNodeIDs(props.nodeId)]);
|
|
60
|
-
setRenderCount((prev) => prev + 1);
|
|
61
|
-
});
|
|
62
|
-
return unsubscribe;
|
|
63
|
-
}, [props.nodeId, ctx.notifications]);
|
|
64
|
-
|
|
65
|
-
const DeleteButton = () => (
|
|
66
|
-
<button
|
|
67
|
-
title="Delete Pane"
|
|
68
|
-
onClick={(e) => {
|
|
69
|
-
ctx.setClickedNodeId(props.nodeId);
|
|
70
|
-
e.stopPropagation();
|
|
71
|
-
}}
|
|
72
|
-
className="absolute right-2 top-2 z-10 rounded-full bg-red-700 p-1.5 hover:bg-black"
|
|
73
|
-
>
|
|
74
|
-
<TrashIcon className="h-10 w-10 text-white" />
|
|
75
|
-
</button>
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
const allNodes = ctx.allNodes.get();
|
|
79
|
-
const bgNode = children
|
|
80
|
-
.map((id) => allNodes.get(id))
|
|
81
|
-
.find(
|
|
82
|
-
(node) =>
|
|
83
|
-
node?.nodeType === 'BgPane' &&
|
|
84
|
-
'type' in node &&
|
|
85
|
-
(node.type === 'background-image' || node.type === 'artpack-image')
|
|
86
|
-
) as (BgImageNode | ArtpackImageNode) | undefined;
|
|
87
|
-
|
|
88
|
-
const useFlexLayout =
|
|
89
|
-
bgNode &&
|
|
90
|
-
(bgNode.position === 'leftBleed' || bgNode.position === 'rightBleed');
|
|
91
|
-
const deferFlexLayout =
|
|
92
|
-
bgNode && (bgNode.position === 'left' || bgNode.position === 'right');
|
|
93
|
-
|
|
94
|
-
const flexDirection =
|
|
95
|
-
currentViewport === 'mobile'
|
|
96
|
-
? 'flex-col'
|
|
97
|
-
: bgNode?.position === 'rightBleed'
|
|
98
|
-
? 'flex-row-reverse'
|
|
99
|
-
: 'flex-row';
|
|
100
|
-
|
|
101
|
-
return (
|
|
102
|
-
<div id={getPaneId()} className="pane min-h-16">
|
|
103
|
-
<div
|
|
104
|
-
id={ctx.getNodeSlug(props.nodeId)}
|
|
105
|
-
className={useFlexLayout ? '' : wrapperClasses}
|
|
106
|
-
>
|
|
107
|
-
{codeHookPayload ? (
|
|
108
|
-
<div className={contentClasses} style={contentStyles}>
|
|
109
|
-
<DeleteButton />
|
|
110
|
-
<CodeHookContainer payload={codeHookPayload} />
|
|
111
|
-
</div>
|
|
112
|
-
) : useFlexLayout ? (
|
|
113
|
-
<div
|
|
114
|
-
className={`flex flex-nowrap ${flexDirection} ${ctx.getNodeClasses(props.nodeId, currentViewport)}`}
|
|
115
|
-
>
|
|
116
|
-
<div
|
|
117
|
-
className={`relative overflow-hidden ${getSizeClasses(bgNode.size || 'equal', 'image', currentViewport)}`}
|
|
118
|
-
>
|
|
119
|
-
<RenderChildren
|
|
120
|
-
children={children.filter((id) => {
|
|
121
|
-
const node = allNodes.get(id);
|
|
122
|
-
return node?.nodeType === 'BgPane';
|
|
123
|
-
})}
|
|
124
|
-
nodeProps={props}
|
|
125
|
-
key={`bg-children-${props.nodeId}-${renderCount}`}
|
|
126
|
-
/>
|
|
127
|
-
</div>
|
|
128
|
-
<div
|
|
129
|
-
className={`${contentClasses} ${getSizeClasses(bgNode.size || 'equal', 'content', currentViewport)}`}
|
|
130
|
-
style={ctx.getNodeCSSPropertiesStyles(props.nodeId)}
|
|
131
|
-
onClick={(e) => {
|
|
132
|
-
ctx.setClickedNodeId(props.nodeId);
|
|
133
|
-
e.stopPropagation();
|
|
134
|
-
}}
|
|
135
|
-
>
|
|
136
|
-
<DeleteButton />
|
|
137
|
-
<RenderChildren
|
|
138
|
-
children={children.filter((id) => {
|
|
139
|
-
const node = allNodes.get(id);
|
|
140
|
-
return node?.nodeType !== 'BgPane';
|
|
141
|
-
})}
|
|
142
|
-
nodeProps={props}
|
|
143
|
-
key={`content-children-${props.nodeId}-${renderCount}`}
|
|
144
|
-
/>
|
|
145
|
-
</div>
|
|
146
|
-
</div>
|
|
147
|
-
) : deferFlexLayout ? (
|
|
148
|
-
<div
|
|
149
|
-
className={contentClasses}
|
|
150
|
-
style={contentStyles}
|
|
151
|
-
onClick={(e) => {
|
|
152
|
-
ctx.setClickedNodeId(props.nodeId);
|
|
153
|
-
e.stopPropagation();
|
|
154
|
-
}}
|
|
155
|
-
>
|
|
156
|
-
<DeleteButton />
|
|
157
|
-
<RenderChildren
|
|
158
|
-
children={children.filter((id) => {
|
|
159
|
-
const node = allNodes.get(id);
|
|
160
|
-
return node?.nodeType !== 'BgPane';
|
|
161
|
-
})}
|
|
162
|
-
nodeProps={props}
|
|
163
|
-
key={`content-children-${props.nodeId}-${renderCount}`}
|
|
164
|
-
/>
|
|
165
|
-
</div>
|
|
166
|
-
) : (
|
|
167
|
-
<div
|
|
168
|
-
className={contentClasses}
|
|
169
|
-
style={contentStyles}
|
|
170
|
-
onClick={(e) => {
|
|
171
|
-
ctx.setClickedNodeId(props.nodeId);
|
|
172
|
-
e.stopPropagation();
|
|
173
|
-
}}
|
|
174
|
-
>
|
|
175
|
-
<DeleteButton />
|
|
176
|
-
<RenderChildren
|
|
177
|
-
children={children}
|
|
178
|
-
nodeProps={props}
|
|
179
|
-
key={`render-children-${props.nodeId}-${renderCount}`}
|
|
180
|
-
/>
|
|
181
|
-
</div>
|
|
182
|
-
)}
|
|
183
|
-
</div>
|
|
184
|
-
</div>
|
|
185
|
-
);
|
|
186
|
-
};
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { type CSSProperties, useEffect, useState } from 'react';
|
|
2
|
-
import PuzzlePieceIcon from '@heroicons/react/24/outline/PuzzlePieceIcon';
|
|
3
|
-
import { viewportKeyStore } from '@/stores/storykeep';
|
|
4
|
-
import { getCtx } from '@/stores/nodes';
|
|
5
|
-
import { RenderChildren } from './RenderChildren';
|
|
6
|
-
import { CodeHookContainer } from './Pane';
|
|
7
|
-
import { type NodeProps } from '@/types/nodeProps';
|
|
8
|
-
|
|
9
|
-
export const PaneLayout = (props: NodeProps) => {
|
|
10
|
-
const wrapperClasses = `grid ${getCtx(props).getNodeClasses(props.nodeId, viewportKeyStore.get().value)}`;
|
|
11
|
-
const contentClasses = 'relative w-full h-auto justify-self-start';
|
|
12
|
-
const contentStyles: CSSProperties = {
|
|
13
|
-
...getCtx(props).getNodeCSSPropertiesStyles(props.nodeId),
|
|
14
|
-
gridArea: '1/1/1/1',
|
|
15
|
-
};
|
|
16
|
-
const codeHookPayload = getCtx(props).getNodeCodeHookPayload(props.nodeId);
|
|
17
|
-
const [children, setChildren] = useState<string[]>([
|
|
18
|
-
...getCtx(props).getChildNodeIDs(props.nodeId),
|
|
19
|
-
]);
|
|
20
|
-
|
|
21
|
-
const getPaneId = (): string => `pane-${props.nodeId}`;
|
|
22
|
-
|
|
23
|
-
useEffect(() => {
|
|
24
|
-
const unsubscribe = getCtx(props).notifications.subscribe(
|
|
25
|
-
props.nodeId,
|
|
26
|
-
() => {
|
|
27
|
-
console.log(
|
|
28
|
-
'notification received data update for page node: ' + props.nodeId
|
|
29
|
-
);
|
|
30
|
-
setChildren([...getCtx(props).getChildNodeIDs(props.nodeId)]);
|
|
31
|
-
}
|
|
32
|
-
);
|
|
33
|
-
return unsubscribe;
|
|
34
|
-
}, []);
|
|
35
|
-
|
|
36
|
-
return (
|
|
37
|
-
<div id={getPaneId()} className="pane min-h-16">
|
|
38
|
-
<div
|
|
39
|
-
id={getCtx(props).getNodeSlug(props.nodeId)}
|
|
40
|
-
className={wrapperClasses}
|
|
41
|
-
>
|
|
42
|
-
<div
|
|
43
|
-
className={contentClasses}
|
|
44
|
-
style={contentStyles}
|
|
45
|
-
onClick={(e) => {
|
|
46
|
-
getCtx(props).setClickedNodeId(props.nodeId);
|
|
47
|
-
e.stopPropagation();
|
|
48
|
-
}}
|
|
49
|
-
onDoubleClick={(e) => {
|
|
50
|
-
getCtx(props).setClickedNodeId(props.nodeId, true);
|
|
51
|
-
e.stopPropagation();
|
|
52
|
-
}}
|
|
53
|
-
>
|
|
54
|
-
{!props.isSandboxMode && (
|
|
55
|
-
<button
|
|
56
|
-
title="Apply New Layout"
|
|
57
|
-
onClick={(e) => {
|
|
58
|
-
getCtx(props).setClickedNodeId(props.nodeId);
|
|
59
|
-
e.stopPropagation();
|
|
60
|
-
}}
|
|
61
|
-
onDoubleClick={(e) => {
|
|
62
|
-
getCtx(props).setClickedNodeId(props.nodeId, true);
|
|
63
|
-
e.stopPropagation();
|
|
64
|
-
}}
|
|
65
|
-
className="absolute right-2 top-2 z-10 rounded-full bg-cyan-700 p-1.5 hover:bg-black"
|
|
66
|
-
>
|
|
67
|
-
<PuzzlePieceIcon className="h-10 w-10 text-white" />
|
|
68
|
-
</button>
|
|
69
|
-
)}
|
|
70
|
-
{codeHookPayload ? (
|
|
71
|
-
<CodeHookContainer payload={codeHookPayload} />
|
|
72
|
-
) : (
|
|
73
|
-
<RenderChildren children={children} nodeProps={props} />
|
|
74
|
-
)}
|
|
75
|
-
</div>
|
|
76
|
-
</div>
|
|
77
|
-
</div>
|
|
78
|
-
);
|
|
79
|
-
};
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import TrashIcon from '@heroicons/react/24/outline/TrashIcon';
|
|
2
|
-
import { getCtx } from '@/stores/nodes';
|
|
3
|
-
import { viewportKeyStore } from '@/stores/storykeep';
|
|
4
|
-
import { RenderChildren } from '../RenderChildren';
|
|
5
|
-
import type { NodeProps } from '@/types/nodeProps';
|
|
6
|
-
|
|
7
|
-
export const NodeAEraser = (props: NodeProps) => {
|
|
8
|
-
return (
|
|
9
|
-
<button
|
|
10
|
-
className={` ${getCtx(props).getNodeClasses(props.nodeId, viewportKeyStore.get().value)} eraser-child group relative transition-all before:pointer-events-none before:absolute before:inset-0 before:opacity-50 before:outline-dashed before:outline-4 before:outline-red-700 hover:before:bg-red-600/50 hover:before:opacity-100 focus:before:bg-red-600/50 focus:before:opacity-100`}
|
|
11
|
-
title="Delete Link"
|
|
12
|
-
onClick={(e) => {
|
|
13
|
-
getCtx(props).setClickedNodeId(props.nodeId);
|
|
14
|
-
e.stopPropagation();
|
|
15
|
-
}}
|
|
16
|
-
>
|
|
17
|
-
<div className="absolute right-2 top-2 rounded-full bg-red-700 p-0.5">
|
|
18
|
-
<TrashIcon className="h-5 w-5 text-white" />
|
|
19
|
-
</div>
|
|
20
|
-
<RenderChildren
|
|
21
|
-
children={getCtx(props).getChildNodeIDs(props.nodeId)}
|
|
22
|
-
nodeProps={props}
|
|
23
|
-
/>
|
|
24
|
-
</button>
|
|
25
|
-
);
|
|
26
|
-
};
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { type JSX, type MouseEvent, type KeyboardEvent } from 'react';
|
|
2
|
-
import TrashIcon from '@heroicons/react/24/outline/TrashIcon';
|
|
3
|
-
import { getCtx } from '@/stores/nodes';
|
|
4
|
-
import { viewportKeyStore } from '@/stores/storykeep';
|
|
5
|
-
import { RenderChildren } from '../RenderChildren';
|
|
6
|
-
import { tagTitles } from '@/types/compositorTypes';
|
|
7
|
-
import type { NodeProps } from '@/types/nodeProps';
|
|
8
|
-
|
|
9
|
-
type NodeTagProps = NodeProps & { tagName: keyof JSX.IntrinsicElements };
|
|
10
|
-
|
|
11
|
-
export const NodeBasicTagEraser = (props: NodeTagProps) => {
|
|
12
|
-
const nodeId = props.nodeId;
|
|
13
|
-
const children = getCtx(props).getChildNodeIDs(props.nodeId);
|
|
14
|
-
|
|
15
|
-
const Tag = props.tagName;
|
|
16
|
-
const tagTitle =
|
|
17
|
-
tagTitles[props.tagName as keyof typeof tagTitles] || props.tagName;
|
|
18
|
-
|
|
19
|
-
const handleClick = (e: MouseEvent) => {
|
|
20
|
-
e.stopPropagation();
|
|
21
|
-
getCtx(props).setClickedNodeId(nodeId);
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
const EraserUI = () => (
|
|
25
|
-
<div
|
|
26
|
-
className={`pointer-events-none absolute left-2 top-2 z-50 flex items-center gap-2 transition-opacity`}
|
|
27
|
-
>
|
|
28
|
-
<div className="rounded-full bg-gray-200 px-2 py-1 text-sm text-gray-800">
|
|
29
|
-
{tagTitle}
|
|
30
|
-
</div>
|
|
31
|
-
<div className="flex items-center gap-1 rounded bg-white px-2 py-1 text-sm text-red-700 shadow-sm transition-colors group-focus-within:bg-red-700 group-focus-within:text-white group-hover:bg-red-700 group-hover:text-white">
|
|
32
|
-
<TrashIcon className="h-4 w-4" />
|
|
33
|
-
</div>
|
|
34
|
-
</div>
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
return (
|
|
38
|
-
<div
|
|
39
|
-
className="group relative cursor-pointer"
|
|
40
|
-
onClick={handleClick}
|
|
41
|
-
role="button"
|
|
42
|
-
tabIndex={0}
|
|
43
|
-
onKeyDown={(e: KeyboardEvent) => {
|
|
44
|
-
if (e.key === 'Enter' || e.key === ' ') {
|
|
45
|
-
e.preventDefault();
|
|
46
|
-
handleClick(e as unknown as MouseEvent);
|
|
47
|
-
}
|
|
48
|
-
}}
|
|
49
|
-
>
|
|
50
|
-
<div className="absolute inset-0">
|
|
51
|
-
<div className="h-full w-full opacity-50 mix-blend-difference outline-dashed outline-4 outline-red-700 group-focus-within:opacity-100 group-hover:opacity-100" />
|
|
52
|
-
</div>
|
|
53
|
-
<EraserUI />
|
|
54
|
-
<Tag
|
|
55
|
-
className={`${getCtx(props).getNodeClasses(nodeId, viewportKeyStore.get().value)} pt-12`}
|
|
56
|
-
>
|
|
57
|
-
<RenderChildren children={children} nodeProps={props} />
|
|
58
|
-
</Tag>
|
|
59
|
-
</div>
|
|
60
|
-
);
|
|
61
|
-
};
|