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
|
@@ -15,12 +15,15 @@ import {
|
|
|
15
15
|
} from './TemplateNodes';
|
|
16
16
|
import { getCtx, NodesContext } from '@/stores/nodes';
|
|
17
17
|
import { settingsPanelStore } from '@/stores/storykeep';
|
|
18
|
+
import { regexpHook } from '@/constants';
|
|
18
19
|
import { PatchOp } from '@/stores/nodesHistory';
|
|
19
20
|
import { cloneDeep } from '@/utils/helpers';
|
|
20
|
-
import { isPaneNode } from './typeGuards';
|
|
21
|
+
import { isPaneNode, isGridLayoutNode } from './typeGuards';
|
|
22
|
+
import { processClassesForViewports } from '@/utils/compositor/reduceNodesClassNames';
|
|
21
23
|
import type {
|
|
22
24
|
BaseNode,
|
|
23
25
|
FlatNode,
|
|
26
|
+
ViewportKey,
|
|
24
27
|
StoryFragmentNode,
|
|
25
28
|
TemplateNode,
|
|
26
29
|
ToolAddMode,
|
|
@@ -28,7 +31,6 @@ import type {
|
|
|
28
31
|
GridLayoutNode,
|
|
29
32
|
PaneNode,
|
|
30
33
|
} from '@/types/compositorTypes';
|
|
31
|
-
import type { NodeTagProps } from '@/types/nodeProps';
|
|
32
34
|
|
|
33
35
|
export const getTemplateNode = (value: ToolAddMode): TemplateNode => {
|
|
34
36
|
let templateNode: TemplateNode;
|
|
@@ -74,19 +76,48 @@ export const getTemplateNode = (value: ToolAddMode): TemplateNode => {
|
|
|
74
76
|
return templateNode;
|
|
75
77
|
};
|
|
76
78
|
|
|
77
|
-
const
|
|
79
|
+
export const canEditText = (
|
|
80
|
+
node: BaseNode | FlatNode,
|
|
81
|
+
ctx: NodesContext
|
|
82
|
+
): boolean => {
|
|
83
|
+
if (!node || node.nodeType !== 'TagElement') return false;
|
|
84
|
+
|
|
85
|
+
const flatNode = node as FlatNode;
|
|
86
|
+
const tagName = flatNode.tagName;
|
|
87
|
+
|
|
88
|
+
// 1. Explicitly forbid structural containers
|
|
89
|
+
if (tagName === 'ul' || tagName === 'ol') {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
78
92
|
|
|
79
|
-
|
|
80
|
-
|
|
93
|
+
// 2. Surgical LI check:
|
|
94
|
+
// Only allow text editing if it is a standard text list item.
|
|
95
|
+
if (tagName === 'li') {
|
|
96
|
+
const childIds = ctx.getChildNodeIDs(node.id);
|
|
81
97
|
|
|
82
|
-
|
|
83
|
-
|
|
98
|
+
// If it contains an image or code widget, it is NOT a text editor.
|
|
99
|
+
const hasComplexContent = childIds.some((id) => {
|
|
100
|
+
const child = ctx.allNodes.get().get(id) as FlatNode;
|
|
101
|
+
return child && ['img', 'code'].includes(child.tagName);
|
|
102
|
+
});
|
|
84
103
|
|
|
85
|
-
|
|
86
|
-
|
|
104
|
+
return !hasComplexContent;
|
|
105
|
+
}
|
|
87
106
|
|
|
88
|
-
|
|
89
|
-
|
|
107
|
+
// 3. Allowed text-editing tags
|
|
108
|
+
const editableTags = [
|
|
109
|
+
'p',
|
|
110
|
+
'h2',
|
|
111
|
+
'h3',
|
|
112
|
+
'h4',
|
|
113
|
+
'h5',
|
|
114
|
+
'h6',
|
|
115
|
+
'a',
|
|
116
|
+
'button',
|
|
117
|
+
'span',
|
|
118
|
+
];
|
|
119
|
+
|
|
120
|
+
return editableTags.includes(tagName);
|
|
90
121
|
};
|
|
91
122
|
|
|
92
123
|
export function parseMarkdownToNodes(
|
|
@@ -133,113 +164,58 @@ export function parseMarkdownToNodes(
|
|
|
133
164
|
|
|
134
165
|
function extractNodesFromDOM(
|
|
135
166
|
element: HTMLElement,
|
|
136
|
-
parentId: string
|
|
167
|
+
parentId: string,
|
|
168
|
+
onInsertSignal?: (tagName: string, nodeId: string) => void
|
|
137
169
|
): FlatNode[] {
|
|
138
|
-
const
|
|
170
|
+
const results: FlatNode[] = [];
|
|
139
171
|
|
|
140
|
-
// Process each child node
|
|
141
172
|
Array.from(element.childNodes).forEach((child) => {
|
|
142
173
|
if (child.nodeType === Node.TEXT_NODE) {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if (text !== null && text !== undefined) {
|
|
148
|
-
// Remove zero-width spaces
|
|
149
|
-
text = text.replace(/\u200B/g, '');
|
|
150
|
-
if (text.trim() === '') {
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
result.push({
|
|
174
|
+
const text = child.textContent || '';
|
|
175
|
+
const sanitizedText = text.replace(/\u200B/g, '');
|
|
176
|
+
if (sanitizedText) {
|
|
177
|
+
results.push({
|
|
154
178
|
id: ulid(),
|
|
155
|
-
parentId,
|
|
156
179
|
nodeType: 'TagElement',
|
|
157
180
|
tagName: 'text',
|
|
158
|
-
|
|
181
|
+
parentId,
|
|
182
|
+
copy: sanitizedText,
|
|
159
183
|
} as FlatNode);
|
|
160
184
|
}
|
|
161
185
|
} else if (child.nodeType === Node.ELEMENT_NODE) {
|
|
162
186
|
const elem = child as HTMLElement;
|
|
163
|
-
const tagName =
|
|
187
|
+
const tagName =
|
|
188
|
+
elem.getAttribute('data-tag') || elem.tagName.toLowerCase();
|
|
164
189
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
tagName === 'span' &&
|
|
168
|
-
(elem.classList.contains('space-marker') ||
|
|
169
|
-
elem.getAttribute('style')?.includes('font-size: 0px'))
|
|
170
|
-
) {
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
190
|
+
const originalId = elem.getAttribute('data-node-id');
|
|
191
|
+
const nodeId = originalId || ulid();
|
|
173
192
|
|
|
174
|
-
// Create node for this element
|
|
175
|
-
const nodeId = ulid();
|
|
176
193
|
const node: FlatNode = {
|
|
177
194
|
id: nodeId,
|
|
178
|
-
parentId,
|
|
179
195
|
nodeType: 'TagElement',
|
|
180
196
|
tagName,
|
|
181
|
-
|
|
197
|
+
parentId,
|
|
198
|
+
} as FlatNode;
|
|
182
199
|
|
|
183
|
-
// Handle special attributes for different tags
|
|
184
200
|
if (tagName === 'a') {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
(elem as HTMLAnchorElement).getAttribute('href') || undefined;
|
|
188
|
-
|
|
189
|
-
// Save classes for the link
|
|
190
|
-
const className = elem.getAttribute('class');
|
|
191
|
-
if (className) {
|
|
192
|
-
node.elementCss = className;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// Update attribute name for editable links
|
|
196
|
-
if (
|
|
197
|
-
elem.hasAttribute('data-space-protected') ||
|
|
198
|
-
elem.hasAttribute('data-editable-link')
|
|
199
|
-
) {
|
|
200
|
-
(node as any)['data-editable-link'] = 'true';
|
|
201
|
-
}
|
|
202
|
-
} else if (tagName === 'button') {
|
|
203
|
-
// Process button tags - preserve all attributes
|
|
204
|
-
const className = elem.getAttribute('class');
|
|
205
|
-
if (className) {
|
|
206
|
-
node.elementCss = className;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// Update attribute name for editable buttons
|
|
210
|
-
if (
|
|
211
|
-
elem.hasAttribute('data-space-protected') ||
|
|
212
|
-
elem.hasAttribute('data-editable-button')
|
|
213
|
-
) {
|
|
214
|
-
(node as any)['data-editable-button'] = 'true';
|
|
215
|
-
}
|
|
201
|
+
node.href = elem.getAttribute('href') || '#';
|
|
202
|
+
}
|
|
216
203
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
attr.name.startsWith('data-') &&
|
|
221
|
-
attr.name !== 'data-space-protected' &&
|
|
222
|
-
attr.name !== 'data-editable-button'
|
|
223
|
-
) {
|
|
224
|
-
(node as any)[attr.name] = attr.value;
|
|
225
|
-
}
|
|
226
|
-
});
|
|
227
|
-
} else if (tagName === 'img') {
|
|
228
|
-
// Process image tags
|
|
229
|
-
node.src = (elem as HTMLImageElement).getAttribute('src') || undefined;
|
|
230
|
-
node.alt = (elem as HTMLImageElement).getAttribute('alt') || undefined;
|
|
204
|
+
if (tagName === 'img') {
|
|
205
|
+
node.src = elem.getAttribute('src') || '';
|
|
206
|
+
node.alt = elem.getAttribute('alt') || '';
|
|
231
207
|
}
|
|
232
208
|
|
|
233
|
-
|
|
234
|
-
result.push(node);
|
|
209
|
+
results.push(node);
|
|
235
210
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
211
|
+
const childNodes = extractNodesFromDOM(elem, nodeId, onInsertSignal);
|
|
212
|
+
if (childNodes.length > 0) {
|
|
213
|
+
results.push(...childNodes);
|
|
214
|
+
}
|
|
239
215
|
}
|
|
240
216
|
});
|
|
241
217
|
|
|
242
|
-
return
|
|
218
|
+
return results;
|
|
243
219
|
}
|
|
244
220
|
|
|
245
221
|
export function findLinkDestinationInHtml(html: string): string | null {
|
|
@@ -359,25 +335,29 @@ export function processRichTextToNodes(
|
|
|
359
335
|
|
|
360
336
|
if (parsedNodes.length === 0) return [];
|
|
361
337
|
|
|
362
|
-
//
|
|
338
|
+
// Create a map for O(1) identity lookup
|
|
339
|
+
const originalNodesMap = new Map(originalNodes.map((n) => [n.id, n]));
|
|
340
|
+
|
|
363
341
|
parsedNodes.forEach((node) => {
|
|
364
|
-
if (['a', 'button', 'span'].includes(node.tagName)) {
|
|
365
|
-
const matchingOriginalNode =
|
|
342
|
+
if (['a', 'button', 'span', 'img'].includes(node.tagName)) {
|
|
343
|
+
const matchingOriginalNode = originalNodesMap.get(node.id);
|
|
344
|
+
|
|
366
345
|
if (matchingOriginalNode) {
|
|
346
|
+
// Re-hydrate the node with its payloads
|
|
367
347
|
if (['a', 'button'].includes(node.tagName)) {
|
|
368
|
-
if (matchingOriginalNode.href)
|
|
369
|
-
node.href = matchingOriginalNode.href;
|
|
370
|
-
}
|
|
348
|
+
if (matchingOriginalNode.href) node.href = matchingOriginalNode.href;
|
|
371
349
|
node.buttonPayload = matchingOriginalNode.buttonPayload;
|
|
372
350
|
} else if (node.tagName === 'span') {
|
|
373
351
|
node.elementCss = matchingOriginalNode.elementCss;
|
|
374
352
|
node.overrideClasses = matchingOriginalNode.overrideClasses;
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
353
|
+
node.wordCarouselPayload = matchingOriginalNode.wordCarouselPayload;
|
|
354
|
+
} else if (node.tagName === 'img') {
|
|
355
|
+
// Keep original src/alt if the DOM didn't change them
|
|
356
|
+
if (matchingOriginalNode.src) node.src = matchingOriginalNode.src;
|
|
357
|
+
if (matchingOriginalNode.alt) node.alt = matchingOriginalNode.alt;
|
|
378
358
|
}
|
|
379
359
|
} else if (onInsertSignal) {
|
|
380
|
-
//
|
|
360
|
+
// Truly a new node (e.g. from a paste)
|
|
381
361
|
onInsertSignal(node.tagName, node.id);
|
|
382
362
|
}
|
|
383
363
|
}
|
|
@@ -756,3 +736,202 @@ export function addColumn(gridLayoutId: string) {
|
|
|
756
736
|
|
|
757
737
|
redoLogic();
|
|
758
738
|
}
|
|
739
|
+
|
|
740
|
+
export function parseCodeHook(node: BaseNode | FlatNode) {
|
|
741
|
+
if ('codeHookParams' in node && Array.isArray(node.codeHookParams)) {
|
|
742
|
+
const hookMatch = node.copy?.match(regexpHook);
|
|
743
|
+
|
|
744
|
+
if (!hookMatch) return null;
|
|
745
|
+
|
|
746
|
+
return {
|
|
747
|
+
hook: hookMatch[1],
|
|
748
|
+
value1: node.codeHookParams[0] || null,
|
|
749
|
+
value2: node.codeHookParams[1] || null,
|
|
750
|
+
value3: node.codeHookParams[2] || '',
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
// Legacy/Children check fallback
|
|
755
|
+
if ('children' in node && Array.isArray((node as any).children)) {
|
|
756
|
+
const firstChild = (node as any).children[0];
|
|
757
|
+
if (!firstChild?.value) return null;
|
|
758
|
+
|
|
759
|
+
const regexpValues = /((?:[^\\|]+|\\\|?)+)/g;
|
|
760
|
+
const hookMatch = firstChild.value.match(regexpHook);
|
|
761
|
+
|
|
762
|
+
if (!hookMatch) return null;
|
|
763
|
+
|
|
764
|
+
const hook = hookMatch[1];
|
|
765
|
+
const hookValuesRaw = hookMatch[2].match(regexpValues);
|
|
766
|
+
|
|
767
|
+
return {
|
|
768
|
+
hook,
|
|
769
|
+
value1: hookValuesRaw?.[0] || null,
|
|
770
|
+
value2: hookValuesRaw?.[1] || null,
|
|
771
|
+
value3: hookValuesRaw?.[2] || '',
|
|
772
|
+
};
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
return null;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
export const isAddressableNode = (
|
|
779
|
+
node: BaseNode | FlatNode,
|
|
780
|
+
ctx?: NodesContext
|
|
781
|
+
): boolean => {
|
|
782
|
+
if (!node || node.nodeType !== 'TagElement') {
|
|
783
|
+
return false;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
const flatNode = node as FlatNode;
|
|
787
|
+
const tagName = flatNode.tagName;
|
|
788
|
+
|
|
789
|
+
if (tagName === 'ul' || tagName === 'ol') {
|
|
790
|
+
return false;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
// Handle LI: Only addressable if it is a text-item.
|
|
794
|
+
// If it contains an img or code widget, it is a transparent container.
|
|
795
|
+
if (tagName === 'li') {
|
|
796
|
+
if (!ctx) return false;
|
|
797
|
+
|
|
798
|
+
const childIds = ctx.getChildNodeIDs(node.id);
|
|
799
|
+
const hasComplexContent = childIds.some((id) => {
|
|
800
|
+
const child = ctx.allNodes.get().get(id) as FlatNode;
|
|
801
|
+
return child && ['img', 'code'].includes(child.tagName);
|
|
802
|
+
});
|
|
803
|
+
|
|
804
|
+
return !hasComplexContent;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
const addressableTags = [
|
|
808
|
+
'p',
|
|
809
|
+
'h2',
|
|
810
|
+
'h3',
|
|
811
|
+
'h4',
|
|
812
|
+
'h5',
|
|
813
|
+
'h6',
|
|
814
|
+
'img',
|
|
815
|
+
'a',
|
|
816
|
+
'button',
|
|
817
|
+
'code',
|
|
818
|
+
];
|
|
819
|
+
|
|
820
|
+
return addressableTags.includes(tagName);
|
|
821
|
+
};
|
|
822
|
+
|
|
823
|
+
export const isTopLevelBlockNode = (
|
|
824
|
+
node: BaseNode | FlatNode,
|
|
825
|
+
ctx: NodesContext
|
|
826
|
+
): boolean => {
|
|
827
|
+
if (!node.parentId) return false;
|
|
828
|
+
const parent = ctx.allNodes.get().get(node.parentId);
|
|
829
|
+
if (!parent) return false;
|
|
830
|
+
|
|
831
|
+
return parent.nodeType === 'Markdown' || isGridLayoutNode(parent);
|
|
832
|
+
};
|
|
833
|
+
|
|
834
|
+
export const getNodeDisplayMode = (
|
|
835
|
+
node: BaseNode | FlatNode,
|
|
836
|
+
viewport: ViewportKey,
|
|
837
|
+
ctx: NodesContext
|
|
838
|
+
): boolean => {
|
|
839
|
+
const flatNode = node as FlatNode;
|
|
840
|
+
const tagName = flatNode.tagName || '';
|
|
841
|
+
|
|
842
|
+
// 1. Check Overrides (Highest Priority)
|
|
843
|
+
// If the user manually added a class, we respect it immediately.
|
|
844
|
+
if (flatNode.overrideClasses) {
|
|
845
|
+
const [_, mobile, tablet, desktop] = processClassesForViewports(
|
|
846
|
+
{ mobile: {}, tablet: {}, desktop: {} },
|
|
847
|
+
flatNode.overrideClasses,
|
|
848
|
+
1
|
|
849
|
+
);
|
|
850
|
+
const active =
|
|
851
|
+
viewport === 'mobile'
|
|
852
|
+
? mobile[0]
|
|
853
|
+
: viewport === 'tablet'
|
|
854
|
+
? tablet[0]
|
|
855
|
+
: desktop[0];
|
|
856
|
+
|
|
857
|
+
// If explicit inline, force inline
|
|
858
|
+
if (active.includes('inline-block') || active.includes('inline-'))
|
|
859
|
+
return true;
|
|
860
|
+
|
|
861
|
+
// If explicit layout (flex, grid, block), force NOT inline (let the class work)
|
|
862
|
+
if (
|
|
863
|
+
active.includes('block') ||
|
|
864
|
+
active.includes('flex') ||
|
|
865
|
+
active.includes('grid')
|
|
866
|
+
)
|
|
867
|
+
return false;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
// 2. PARENT CONTEXT CHECK
|
|
871
|
+
// If the parent is a layout container (Flex/Grid), this node is an Item.
|
|
872
|
+
// It should NOT be forced to inline-block. It should default to block (div).
|
|
873
|
+
if (node.parentId) {
|
|
874
|
+
const parent = ctx.allNodes.get().get(node.parentId) as any;
|
|
875
|
+
if (parent) {
|
|
876
|
+
// Check Parent's Extracted Classes (defaults + overrides)
|
|
877
|
+
const extracted = extractClassesFromNodes([parent]).join(' ');
|
|
878
|
+
|
|
879
|
+
// Also check Parent's Explicit Overrides directly for current viewport
|
|
880
|
+
let parentActiveOverrides = '';
|
|
881
|
+
if (parent.overrideClasses) {
|
|
882
|
+
const [_, pMob, pTab, pDesk] = processClassesForViewports(
|
|
883
|
+
{ mobile: {}, tablet: {}, desktop: {} },
|
|
884
|
+
parent.overrideClasses,
|
|
885
|
+
1
|
|
886
|
+
);
|
|
887
|
+
parentActiveOverrides =
|
|
888
|
+
viewport === 'mobile'
|
|
889
|
+
? pMob[0]
|
|
890
|
+
: viewport === 'tablet'
|
|
891
|
+
? pTab[0]
|
|
892
|
+
: pDesk[0];
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
const combinedParentClasses = `${extracted} ${parentActiveOverrides}`;
|
|
896
|
+
|
|
897
|
+
// If parent is Flex or Grid, we are an Item. Return FALSE to avoid inline-block.
|
|
898
|
+
if (
|
|
899
|
+
combinedParentClasses.includes('flex') ||
|
|
900
|
+
combinedParentClasses.includes('grid') ||
|
|
901
|
+
combinedParentClasses.includes('gap-')
|
|
902
|
+
) {
|
|
903
|
+
return false;
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
// 3. Check Default Classes (Theme Defaults)
|
|
909
|
+
const markdownParentId = ctx.getClosestNodeTypeFromId(node.id, 'Markdown');
|
|
910
|
+
if (markdownParentId) {
|
|
911
|
+
const styleSourceNode = ctx.allNodes.get().get(markdownParentId) as any;
|
|
912
|
+
const styles = styleSourceNode?.defaultClasses?.[tagName];
|
|
913
|
+
if (styles) {
|
|
914
|
+
const defaultClassStr = Object.values(styles.mobile || {})
|
|
915
|
+
.flat()
|
|
916
|
+
.join(' ');
|
|
917
|
+
|
|
918
|
+
if (defaultClassStr.includes('inline')) {
|
|
919
|
+
return true;
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
// 4. Tag Default (Lowest Priority)
|
|
925
|
+
// Standard HTML behavior
|
|
926
|
+
const inlineTags = ['a', 'span', 'img', 'button', 'strong', 'em'];
|
|
927
|
+
if (inlineTags.includes(tagName)) {
|
|
928
|
+
// Exception: Top level blocks (direct children of Markdown roots) usually stack
|
|
929
|
+
if (isTopLevelBlockNode(node, ctx)) {
|
|
930
|
+
return false;
|
|
931
|
+
}
|
|
932
|
+
return true;
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
// Default to Block (False)
|
|
936
|
+
return false;
|
|
937
|
+
};
|