astro-tractstack 2.1.3 → 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/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
|
@@ -135,7 +135,7 @@ export const SignUp = ({
|
|
|
135
135
|
</Select.Control>
|
|
136
136
|
<Portal>
|
|
137
137
|
<Select.Positioner>
|
|
138
|
-
<Select.Content className="
|
|
138
|
+
<Select.Content className="z-50 max-h-56 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black/5 focus:outline-none md:text-sm">
|
|
139
139
|
{personaCollection.items.map((option) => (
|
|
140
140
|
<Select.Item
|
|
141
141
|
key={option.id}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useEffect,
|
|
3
|
+
useState,
|
|
4
|
+
useRef,
|
|
5
|
+
type FocusEvent,
|
|
6
|
+
type KeyboardEvent,
|
|
7
|
+
} from 'react';
|
|
8
|
+
import { useStore } from '@nanostores/react';
|
|
9
|
+
import { renderedPreviews, updatePreview } from '@/stores/previews';
|
|
10
|
+
import { settingsPanelStore, viewportKeyStore } from '@/stores/storykeep';
|
|
11
|
+
import { getCtx } from '@/stores/nodes';
|
|
12
|
+
import type { CreativePanePayload } from '@/types/compositorTypes';
|
|
13
|
+
|
|
14
|
+
export interface CreativePaneProps {
|
|
15
|
+
nodeId: string;
|
|
16
|
+
htmlAst: CreativePanePayload;
|
|
17
|
+
isProtected?: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const viewportMap = {
|
|
21
|
+
mobile: 'xs',
|
|
22
|
+
tablet: 'md',
|
|
23
|
+
desktop: 'xl',
|
|
24
|
+
} as const;
|
|
25
|
+
|
|
26
|
+
export const CreativePane = ({
|
|
27
|
+
nodeId,
|
|
28
|
+
htmlAst,
|
|
29
|
+
isProtected = false,
|
|
30
|
+
}: CreativePaneProps) => {
|
|
31
|
+
const previews = useStore(renderedPreviews);
|
|
32
|
+
const { value: viewportKey } = useStore(viewportKeyStore);
|
|
33
|
+
const [loading, setLoading] = useState(false);
|
|
34
|
+
const [error, setError] = useState<string | null>(null);
|
|
35
|
+
const contentRef = useRef<HTMLDivElement>(null);
|
|
36
|
+
|
|
37
|
+
const activeViewport = viewportMap[viewportKey];
|
|
38
|
+
const htmlContent = previews[nodeId];
|
|
39
|
+
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
const controller = new AbortController();
|
|
42
|
+
const signal = controller.signal;
|
|
43
|
+
const fetchPreview = async () => {
|
|
44
|
+
if (!htmlAst?.tree) return;
|
|
45
|
+
setLoading(true);
|
|
46
|
+
setError(null);
|
|
47
|
+
try {
|
|
48
|
+
const tenantId =
|
|
49
|
+
(window as any).TRACTSTACK_CONFIG?.tenantId ||
|
|
50
|
+
import.meta.env.PUBLIC_TENANTID ||
|
|
51
|
+
'default';
|
|
52
|
+
|
|
53
|
+
const goBackend =
|
|
54
|
+
import.meta.env.PUBLIC_GO_BACKEND || 'http://localhost:8080';
|
|
55
|
+
|
|
56
|
+
const response = await fetch(
|
|
57
|
+
`${goBackend}/api/v1/fragments/ast-preview`,
|
|
58
|
+
{
|
|
59
|
+
method: 'POST',
|
|
60
|
+
headers: {
|
|
61
|
+
'Content-Type': 'application/json',
|
|
62
|
+
'X-Tenant-ID': tenantId,
|
|
63
|
+
},
|
|
64
|
+
body: JSON.stringify({
|
|
65
|
+
id: nodeId,
|
|
66
|
+
title: 'Editor Preview',
|
|
67
|
+
tree: htmlAst.tree,
|
|
68
|
+
}),
|
|
69
|
+
signal,
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
if (!response.ok) {
|
|
74
|
+
const text = await response.text();
|
|
75
|
+
throw new Error(
|
|
76
|
+
text || `Preview generation failed: ${response.status}`
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const html = await response.text();
|
|
81
|
+
|
|
82
|
+
if (!signal.aborted) {
|
|
83
|
+
updatePreview(nodeId, html);
|
|
84
|
+
}
|
|
85
|
+
} catch (err: any) {
|
|
86
|
+
if (err.name === 'AbortError') return;
|
|
87
|
+
|
|
88
|
+
console.error(`CreativePane fetch failed for ${nodeId}:`, err);
|
|
89
|
+
setError(err instanceof Error ? err.message : 'Unknown error');
|
|
90
|
+
} finally {
|
|
91
|
+
if (!signal.aborted) {
|
|
92
|
+
setLoading(false);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
fetchPreview();
|
|
98
|
+
|
|
99
|
+
return () => {
|
|
100
|
+
controller.abort();
|
|
101
|
+
};
|
|
102
|
+
}, [htmlAst?.css, htmlAst?.tree, nodeId]);
|
|
103
|
+
|
|
104
|
+
useEffect(() => {
|
|
105
|
+
const ctx = getCtx();
|
|
106
|
+
const unsubscribe = ctx.toolModeValStore.subscribe((state) => {
|
|
107
|
+
const container = contentRef.current;
|
|
108
|
+
if (!container) return;
|
|
109
|
+
|
|
110
|
+
const editables = container.querySelectorAll('[data-ast-id]');
|
|
111
|
+
|
|
112
|
+
if (state.value === 'text') {
|
|
113
|
+
editables.forEach((el) => {
|
|
114
|
+
const htmlEl = el as HTMLElement;
|
|
115
|
+
if (htmlEl.isContentEditable) return;
|
|
116
|
+
htmlEl.style.outline = '2px dotted #06b6d4';
|
|
117
|
+
htmlEl.style.outlineOffset = '2px';
|
|
118
|
+
|
|
119
|
+
const astId = htmlEl.getAttribute('data-ast-id');
|
|
120
|
+
if (!astId) return;
|
|
121
|
+
|
|
122
|
+
const existingIcon = container.querySelector(
|
|
123
|
+
`[data-proxy-for="${astId}"]`
|
|
124
|
+
);
|
|
125
|
+
if (existingIcon) return;
|
|
126
|
+
|
|
127
|
+
const icon = document.createElement('div');
|
|
128
|
+
icon.setAttribute('data-proxy-for', astId);
|
|
129
|
+
icon.style.position = 'absolute';
|
|
130
|
+
icon.style.zIndex = '1003';
|
|
131
|
+
icon.style.width = '24px';
|
|
132
|
+
icon.style.height = '24px';
|
|
133
|
+
icon.style.backgroundColor = '#06b6d4';
|
|
134
|
+
icon.style.borderRadius = '9999px';
|
|
135
|
+
icon.style.display = 'flex';
|
|
136
|
+
icon.style.alignItems = 'center';
|
|
137
|
+
icon.style.justifyContent = 'center';
|
|
138
|
+
icon.style.color = 'white';
|
|
139
|
+
icon.style.fontSize = '12px';
|
|
140
|
+
icon.style.boxShadow = '0 10px 15px -3px rgb(0 0 0 / 0.1)';
|
|
141
|
+
icon.style.cursor = 'pointer';
|
|
142
|
+
icon.innerHTML = '✎';
|
|
143
|
+
|
|
144
|
+
const rect = htmlEl.getBoundingClientRect();
|
|
145
|
+
const containerRect = container.getBoundingClientRect();
|
|
146
|
+
icon.style.top = `${rect.top - containerRect.top - 12}px`;
|
|
147
|
+
icon.style.left = `${rect.left - containerRect.left - 12}px`;
|
|
148
|
+
|
|
149
|
+
icon.onmouseenter = () => {
|
|
150
|
+
htmlEl.style.outline = '3px solid #06b6d4';
|
|
151
|
+
};
|
|
152
|
+
icon.onmouseleave = () => {
|
|
153
|
+
htmlEl.style.outline = '2px dotted #06b6d4';
|
|
154
|
+
};
|
|
155
|
+
icon.onclick = () => {
|
|
156
|
+
const meta = htmlAst.editableElements?.[astId];
|
|
157
|
+
if (meta) {
|
|
158
|
+
let action = '';
|
|
159
|
+
if (meta.isCssBackground) {
|
|
160
|
+
action = 'style-creative-bg';
|
|
161
|
+
} else if (meta.tagName === 'img') {
|
|
162
|
+
action = 'style-creative-img';
|
|
163
|
+
} else if (meta.tagName === 'a') {
|
|
164
|
+
action = 'style-creative-link';
|
|
165
|
+
} else if (meta.tagName === 'button') {
|
|
166
|
+
action = 'style-creative-btn';
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (action) {
|
|
170
|
+
settingsPanelStore.set({
|
|
171
|
+
action,
|
|
172
|
+
nodeId,
|
|
173
|
+
childId: astId,
|
|
174
|
+
expanded: true,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
container.appendChild(icon);
|
|
181
|
+
});
|
|
182
|
+
} else {
|
|
183
|
+
editables.forEach((el) => {
|
|
184
|
+
(el as HTMLElement).style.outline = '';
|
|
185
|
+
(el as HTMLElement).style.outlineOffset = '';
|
|
186
|
+
(el as HTMLElement).style.cursor = '';
|
|
187
|
+
});
|
|
188
|
+
const icons = container.querySelectorAll('[data-proxy-for]');
|
|
189
|
+
icons.forEach((icon) => icon.remove());
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
return () => unsubscribe();
|
|
194
|
+
}, [htmlContent]);
|
|
195
|
+
|
|
196
|
+
const handleKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {
|
|
197
|
+
const ctx = getCtx();
|
|
198
|
+
const mode = ctx.toolModeValStore.get().value;
|
|
199
|
+
if (mode !== 'text' || isProtected) return;
|
|
200
|
+
|
|
201
|
+
if (e.key === 'Enter') {
|
|
202
|
+
const target = e.target as HTMLElement;
|
|
203
|
+
if (target.isContentEditable) {
|
|
204
|
+
e.preventDefault();
|
|
205
|
+
target.blur();
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
const handleBlur = (e: FocusEvent<HTMLDivElement>) => {
|
|
211
|
+
const ctx = getCtx();
|
|
212
|
+
const mode = ctx.toolModeValStore.get().value;
|
|
213
|
+
if (mode !== 'text' || isProtected) return;
|
|
214
|
+
|
|
215
|
+
const target = e.target as HTMLElement;
|
|
216
|
+
const astId = target.getAttribute('data-ast-id');
|
|
217
|
+
|
|
218
|
+
if (astId && target.isContentEditable) {
|
|
219
|
+
const content = target.innerHTML;
|
|
220
|
+
ctx.updateCreativePane(nodeId, astId, content);
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
if (error) {
|
|
225
|
+
return (
|
|
226
|
+
<div className="flex h-full w-full items-center justify-center border border-dashed border-red-300 bg-red-50 p-4 text-sm text-red-500">
|
|
227
|
+
Preview Error: {error}
|
|
228
|
+
</div>
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (!htmlContent && loading) {
|
|
233
|
+
return (
|
|
234
|
+
<div
|
|
235
|
+
className="flex h-full w-full items-center justify-center bg-gray-50"
|
|
236
|
+
style={{ minHeight: '100px' }}
|
|
237
|
+
>
|
|
238
|
+
<div className="h-6 w-6 animate-spin rounded-full border-b-2 border-cyan-600"></div>
|
|
239
|
+
</div>
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (!htmlContent) return null;
|
|
244
|
+
const activeCss = htmlAst.viewportCss?.[activeViewport] || htmlAst.css;
|
|
245
|
+
|
|
246
|
+
return (
|
|
247
|
+
<>
|
|
248
|
+
<style dangerouslySetInnerHTML={{ __html: activeCss }} />
|
|
249
|
+
<div
|
|
250
|
+
ref={contentRef}
|
|
251
|
+
onKeyDown={handleKeyDown}
|
|
252
|
+
onBlur={handleBlur}
|
|
253
|
+
className="creative-pane-wrapper relative h-full w-full"
|
|
254
|
+
>
|
|
255
|
+
{isProtected && (
|
|
256
|
+
<div className="absolute inset-0 z-50 cursor-crosshair bg-transparent" />
|
|
257
|
+
)}
|
|
258
|
+
<div dangerouslySetInnerHTML={{ __html: htmlContent }} />
|
|
259
|
+
</div>
|
|
260
|
+
</>
|
|
261
|
+
);
|
|
262
|
+
};
|
|
@@ -108,7 +108,7 @@ export const GhostInsertBlock = memo((props: GhostInsertBlockProps) => {
|
|
|
108
108
|
);
|
|
109
109
|
|
|
110
110
|
const ElementButtons = () => (
|
|
111
|
-
<div className="
|
|
111
|
+
<div className="grid grid-cols-2 gap-2 p-2 md:grid-cols-3">
|
|
112
112
|
{$toolAddModes
|
|
113
113
|
.filter((mode) => !['p', 'h2', 'h3', 'h4'].includes(mode))
|
|
114
114
|
.filter((mode) => allowedModes.includes(mode as ToolAddMode))
|
|
@@ -169,11 +169,9 @@ export const GhostInsertBlock = memo((props: GhostInsertBlockProps) => {
|
|
|
169
169
|
) : (
|
|
170
170
|
<button
|
|
171
171
|
onClick={(e) => {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
setShowInsertOptions(true);
|
|
176
|
-
}
|
|
172
|
+
e.stopPropagation();
|
|
173
|
+
settingsPanelStore.set(null);
|
|
174
|
+
setShowInsertOptions(true);
|
|
177
175
|
}}
|
|
178
176
|
className="group w-full rounded-lg border-2 border-dashed border-cyan-500 bg-cyan-50 p-6 transition-colors hover:bg-cyan-100 dark:border-cyan-600 dark:bg-cyan-900 dark:hover:bg-cyan-800"
|
|
179
177
|
>
|
|
@@ -3,6 +3,7 @@ import { useStore } from '@nanostores/react';
|
|
|
3
3
|
import { getCtx } from '@/stores/nodes';
|
|
4
4
|
import { RenderChildren } from './RenderChildren';
|
|
5
5
|
import { isGridLayoutNode } from '@/utils/compositor/typeGuards';
|
|
6
|
+
import { PaneOverlay } from '@/components/compositor/tools/PaneOverlay';
|
|
6
7
|
import type { NodeProps } from '@/types/nodeProps';
|
|
7
8
|
import type { ParentClassesPayload } from '@/types/compositorTypes';
|
|
8
9
|
import { viewportKeyStore, settingsPanelStore } from '@/stores/storykeep';
|
|
@@ -108,17 +109,18 @@ export const GridLayout = (props: NodeProps) => {
|
|
|
108
109
|
getCtx(props).setClickedNodeId(props.nodeId, true);
|
|
109
110
|
e.stopPropagation();
|
|
110
111
|
}}
|
|
111
|
-
className={getCtx(props).getNodeClasses(
|
|
112
|
+
className={`${getCtx(props).getNodeClasses(
|
|
112
113
|
props.nodeId,
|
|
113
114
|
currentViewport,
|
|
114
115
|
i - 1
|
|
115
|
-
)}
|
|
116
|
+
)} group relative`}
|
|
116
117
|
style={
|
|
117
118
|
i === parentClassesLength
|
|
118
119
|
? { position: 'relative', zIndex: 10 }
|
|
119
120
|
: undefined
|
|
120
121
|
}
|
|
121
122
|
>
|
|
123
|
+
<PaneOverlay {...props} layer={i} />
|
|
122
124
|
{nodesToRender}
|
|
123
125
|
</div>
|
|
124
126
|
);
|
|
@@ -3,7 +3,9 @@ import { getCtx } from '@/stores/nodes';
|
|
|
3
3
|
import { viewportKeyStore } from '@/stores/storykeep';
|
|
4
4
|
import { RenderChildren } from './RenderChildren';
|
|
5
5
|
import { GhostInsertBlock } from './GhostInsertBlock';
|
|
6
|
+
import { PaneOverlay } from '@/components/compositor/tools/PaneOverlay';
|
|
6
7
|
import { processClassesForViewports } from '@/utils/compositor/reduceNodesClassNames';
|
|
8
|
+
import { isGridLayoutNode } from '@/utils/compositor/typeGuards';
|
|
7
9
|
import type { NodeProps } from '@/types/nodeProps';
|
|
8
10
|
import type {
|
|
9
11
|
MarkdownPaneFragmentNode,
|
|
@@ -35,6 +37,11 @@ export const Markdown = (props: NodeProps) => {
|
|
|
35
37
|
|
|
36
38
|
if (!node) return null;
|
|
37
39
|
|
|
40
|
+
// Context Check: Are we inside a Grid?
|
|
41
|
+
const allNodes = ctx.allNodes.get();
|
|
42
|
+
const parentNode = node.parentId ? allNodes.get(node.parentId) : null;
|
|
43
|
+
const isGridChild = parentNode ? isGridLayoutNode(parentNode) : false;
|
|
44
|
+
|
|
38
45
|
let isHidden = false;
|
|
39
46
|
switch (currentViewport) {
|
|
40
47
|
case 'mobile':
|
|
@@ -65,7 +72,6 @@ export const Markdown = (props: NodeProps) => {
|
|
|
65
72
|
);
|
|
66
73
|
}
|
|
67
74
|
|
|
68
|
-
const allNodes = ctx.allNodes.get();
|
|
69
75
|
const parentPaneId = ctx.getClosestNodeTypeFromId(id, 'Pane');
|
|
70
76
|
const bgNode = parentPaneId
|
|
71
77
|
? (() => {
|
|
@@ -185,7 +191,11 @@ export const Markdown = (props: NodeProps) => {
|
|
|
185
191
|
</>
|
|
186
192
|
);
|
|
187
193
|
|
|
194
|
+
// Conditional Layer Rendering:
|
|
195
|
+
// If we are NOT in a grid (standard Pane mode), we render the parentClasses loop.
|
|
196
|
+
// If we ARE in a grid, we skip this (the grid handles the outer layers, we handle the column style).
|
|
188
197
|
if (
|
|
198
|
+
!isGridChild &&
|
|
189
199
|
'parentClasses' in node &&
|
|
190
200
|
(node.parentClasses as ParentClassesPayload)?.length > 0
|
|
191
201
|
) {
|
|
@@ -210,13 +220,14 @@ export const Markdown = (props: NodeProps) => {
|
|
|
210
220
|
ctx.setClickedNodeId(props.nodeId, true);
|
|
211
221
|
e.stopPropagation();
|
|
212
222
|
}}
|
|
213
|
-
className={ctx.getNodeClasses(id, currentViewport, i - 1)}
|
|
223
|
+
className={`${ctx.getNodeClasses(id, currentViewport, i - 1)} group relative`}
|
|
214
224
|
style={
|
|
215
225
|
i === parentClassesLength
|
|
216
226
|
? { position: 'relative', zIndex: 10 }
|
|
217
227
|
: undefined
|
|
218
228
|
}
|
|
219
229
|
>
|
|
230
|
+
<PaneOverlay {...props} layer={i} />
|
|
220
231
|
{nodesToRender}
|
|
221
232
|
</div>
|
|
222
233
|
);
|
|
@@ -224,7 +235,11 @@ export const Markdown = (props: NodeProps) => {
|
|
|
224
235
|
}
|
|
225
236
|
|
|
226
237
|
return (
|
|
227
|
-
<div
|
|
238
|
+
<div
|
|
239
|
+
className={`${gridClassName} group relative`}
|
|
240
|
+
style={{ position: 'relative', zIndex: 10 }}
|
|
241
|
+
>
|
|
242
|
+
<PaneOverlay {...props} isColumn={isGridChild} />
|
|
228
243
|
{nodesToRender}
|
|
229
244
|
</div>
|
|
230
245
|
);
|
|
@@ -7,7 +7,12 @@ import ListContentSetup from '@/components/codehooks/ListContentSetup';
|
|
|
7
7
|
import BunnyVideoSetup from '@/components/codehooks/BunnyVideoSetup';
|
|
8
8
|
import { ProductCardSetup } from '@/components/codehooks/ProductCardSetup';
|
|
9
9
|
import { ProductGridSetup } from '@/components/codehooks/ProductGridSetup';
|
|
10
|
-
import
|
|
10
|
+
import { PaneOverlay } from '@/components/compositor/tools/PaneOverlay';
|
|
11
|
+
import type {
|
|
12
|
+
PaneNode,
|
|
13
|
+
BgImageNode,
|
|
14
|
+
ArtpackImageNode,
|
|
15
|
+
} from '@/types/compositorTypes';
|
|
11
16
|
import type { NodeProps } from '@/types/nodeProps';
|
|
12
17
|
|
|
13
18
|
const TARGETS = [
|
|
@@ -95,7 +100,7 @@ const Pane = memo(
|
|
|
95
100
|
return () => unsubscribeViewport();
|
|
96
101
|
}, []);
|
|
97
102
|
|
|
98
|
-
const wrapperClasses = `grid ${getCtx(props).getNodeClasses(props.nodeId, currentViewport)}`;
|
|
103
|
+
const wrapperClasses = `grid pt-6 ${getCtx(props).getNodeClasses(props.nodeId, currentViewport)}`;
|
|
99
104
|
|
|
100
105
|
const contentClasses = 'relative w-full h-auto justify-self-start';
|
|
101
106
|
const contentStyles: CSSProperties = {
|
|
@@ -119,6 +124,8 @@ const Pane = memo(
|
|
|
119
124
|
|
|
120
125
|
// Get background node if it exists
|
|
121
126
|
const allNodes = getCtx(props).allNodes.get();
|
|
127
|
+
const getPaneId = () => `pane-${props.nodeId}`;
|
|
128
|
+
|
|
122
129
|
const bgNode = children
|
|
123
130
|
.map((id) => allNodes.get(id))
|
|
124
131
|
.find(
|
|
@@ -143,8 +150,6 @@ const Pane = memo(
|
|
|
143
150
|
? 'flex-row-reverse'
|
|
144
151
|
: 'flex-row';
|
|
145
152
|
|
|
146
|
-
const getPaneId = () => `pane-${props.nodeId}`;
|
|
147
|
-
|
|
148
153
|
const handleNotification = () => {
|
|
149
154
|
const newChildren = [...getCtx(props).getChildNodeIDs(props.nodeId)];
|
|
150
155
|
setChildren(newChildren); // Fresh copy
|
|
@@ -160,7 +165,8 @@ const Pane = memo(
|
|
|
160
165
|
}, [props.nodeId]);
|
|
161
166
|
|
|
162
167
|
return (
|
|
163
|
-
<div id={getPaneId()} className="pane">
|
|
168
|
+
<div id={getPaneId()} className="pane group relative">
|
|
169
|
+
<PaneOverlay {...props} hasBackground={!!bgNode} />
|
|
164
170
|
<div
|
|
165
171
|
id={getCtx(props).getNodeSlug(props.nodeId)}
|
|
166
172
|
className={useFlexLayout ? '' : wrapperClasses}
|
|
@@ -195,7 +195,7 @@ export const NodeAnchorComponent = (props: NodeProps, tagName: string) => {
|
|
|
195
195
|
// Create appropriate element based on tagName
|
|
196
196
|
let baseClasses = ctx.getNodeClasses(nodeId, viewportKeyStore.get().value);
|
|
197
197
|
|
|
198
|
-
if (toolMode === '
|
|
198
|
+
if (toolMode === 'text' && settingsPanel?.nodeId != nodeId) {
|
|
199
199
|
baseClasses += ' outline outline-1 outline-dotted outline-black';
|
|
200
200
|
} else if (settingsPanel?.nodeId === nodeId) {
|
|
201
201
|
baseClasses +=
|
|
@@ -213,9 +213,9 @@ export const NodeAnchorComponent = (props: NodeProps, tagName: string) => {
|
|
|
213
213
|
onClick={handleClick}
|
|
214
214
|
onDoubleClick={handleDoubleClick}
|
|
215
215
|
data-editable-link="true"
|
|
216
|
-
|
|
216
|
+
data-node-id={nodeId}
|
|
217
|
+
data-tag="a"
|
|
217
218
|
style={{
|
|
218
|
-
display: 'inline',
|
|
219
219
|
cursor: isEditMode ? 'text' : 'pointer',
|
|
220
220
|
}}
|
|
221
221
|
>
|
|
@@ -237,9 +237,9 @@ export const NodeAnchorComponent = (props: NodeProps, tagName: string) => {
|
|
|
237
237
|
onClick={handleClick}
|
|
238
238
|
onDoubleClick={handleDoubleClick}
|
|
239
239
|
data-editable-button="true"
|
|
240
|
-
|
|
240
|
+
data-node-id={nodeId}
|
|
241
|
+
data-tag="button"
|
|
241
242
|
style={{
|
|
242
|
-
display: 'inline',
|
|
243
243
|
cursor: isEditMode ? 'text' : 'crosshair',
|
|
244
244
|
}}
|
|
245
245
|
>
|