@deneb-ui/cli 2.0.71 → 2.0.73
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/bin/index.js +34 -0
- package/package.json +2 -2
- package/src/arc/__tests__/arc.test.cjs +82 -0
- package/src/arc/ast.cjs +46 -0
- package/src/arc/field-paths.cjs +4 -0
- package/src/arc/manifest.cjs +39 -3
- package/src/arc/planner.cjs +10 -0
- package/src/arc/residual.cjs +42 -12
- package/src/arc/semantic.cjs +153 -13
- package/src/arc/transformer.cjs +301 -12
- package/src/tools/deneb-template-validator.cjs +1 -1
package/bin/index.js
CHANGED
|
@@ -467,15 +467,49 @@ function getDefaultSiteData(projectName, pages) {
|
|
|
467
467
|
secondaryColor: '#0a1931',
|
|
468
468
|
accentColor: '#00adb5',
|
|
469
469
|
backgroundColor: '#ffffff',
|
|
470
|
+
cardBackgroundColor: '#f8fafc',
|
|
470
471
|
textColor: '#0f172a',
|
|
472
|
+
borderColor: '#e2e8f0',
|
|
471
473
|
headingFont: 'Inter',
|
|
472
474
|
bodyFont: 'Inter',
|
|
473
475
|
baseSize: '16px',
|
|
474
476
|
heroMinHeight: '70vh',
|
|
475
477
|
sectionPadding: '4rem',
|
|
478
|
+
dark: {
|
|
479
|
+
primaryColor: '#00adb5',
|
|
480
|
+
secondaryColor: '#f8fafc',
|
|
481
|
+
accentColor: '#016a7e',
|
|
482
|
+
backgroundColor: '#0b0f19',
|
|
483
|
+
cardBackgroundColor: '#111827',
|
|
484
|
+
textColor: '#f9fafb',
|
|
485
|
+
borderColor: '#1f2937',
|
|
486
|
+
},
|
|
476
487
|
},
|
|
477
488
|
},
|
|
478
489
|
},
|
|
490
|
+
theme: {
|
|
491
|
+
primaryColor: '#016a7e',
|
|
492
|
+
secondaryColor: '#0a1931',
|
|
493
|
+
accentColor: '#00adb5',
|
|
494
|
+
backgroundColor: '#ffffff',
|
|
495
|
+
cardBackgroundColor: '#f8fafc',
|
|
496
|
+
textColor: '#0f172a',
|
|
497
|
+
borderColor: '#e2e8f0',
|
|
498
|
+
headingFont: 'Inter',
|
|
499
|
+
bodyFont: 'Inter',
|
|
500
|
+
baseSize: '16px',
|
|
501
|
+
heroMinHeight: '70vh',
|
|
502
|
+
sectionPadding: '4rem',
|
|
503
|
+
dark: {
|
|
504
|
+
primaryColor: '#00adb5',
|
|
505
|
+
secondaryColor: '#f8fafc',
|
|
506
|
+
accentColor: '#016a7e',
|
|
507
|
+
backgroundColor: '#0b0f19',
|
|
508
|
+
cardBackgroundColor: '#111827',
|
|
509
|
+
textColor: '#f9fafb',
|
|
510
|
+
borderColor: '#1f2937',
|
|
511
|
+
},
|
|
512
|
+
},
|
|
479
513
|
requirements: {
|
|
480
514
|
requiredPages: pages.filter((p) => p.required).map((p) => p.id),
|
|
481
515
|
requiredFeatures: [],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deneb-ui/cli",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.73",
|
|
4
4
|
"description": "Official DENEB CLI — scaffold, convert, validate, and package Fivora-ready storefront templates.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"deneb": "bin/index.js",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"license": "MIT",
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"@babel/parser": "^7.28.0",
|
|
52
|
-
"@deneb-ui/core": "^2.0.
|
|
52
|
+
"@deneb-ui/core": "^2.0.73",
|
|
53
53
|
"@octokit/rest": "^22.0.1",
|
|
54
54
|
"adm-zip": "^0.6.0",
|
|
55
55
|
"dotenv": "^17.4.2",
|
|
@@ -1627,6 +1627,88 @@ test('learning loadFingerprintBoost returns verified boost for baseline trained
|
|
|
1627
1627
|
assert.equal(boost3.boost, 0.08);
|
|
1628
1628
|
});
|
|
1629
1629
|
|
|
1630
|
+
test('instrumentLayoutSource injects ThemeStyles and ThemeToggle with dual mode support into root layout', () => {
|
|
1631
|
+
const { instrumentLayoutSource } = require('../transformer.cjs');
|
|
1632
|
+
const code = `
|
|
1633
|
+
import React from 'react';
|
|
1634
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
1635
|
+
return (
|
|
1636
|
+
<html lang="en">
|
|
1637
|
+
<body>
|
|
1638
|
+
<main>{children}</main>
|
|
1639
|
+
</body>
|
|
1640
|
+
</html>
|
|
1641
|
+
);
|
|
1642
|
+
}
|
|
1643
|
+
`;
|
|
1644
|
+
const result = instrumentLayoutSource(code, '@/data/site-data.json', '@deneb-ui/ui');
|
|
1645
|
+
assert.equal(result.updated, true);
|
|
1646
|
+
assert.ok(result.code.includes('ThemeStyles'));
|
|
1647
|
+
assert.ok(result.code.includes('ThemeToggle'));
|
|
1648
|
+
assert.ok(result.code.includes('SiteDataProvider'));
|
|
1649
|
+
assert.ok(result.code.includes('enableDualMode'));
|
|
1650
|
+
assert.ok(result.code.includes('fixed bottom-6 left-6 z-40'));
|
|
1651
|
+
});
|
|
1652
|
+
|
|
1653
|
+
test('semantic prop harvester detects user-facing copy props and transformer binds them to siteData', () => {
|
|
1654
|
+
const { analyzeFile } = require('../semantic.cjs');
|
|
1655
|
+
const { planTransformations } = require('../planner.cjs');
|
|
1656
|
+
const { applyFilePlan } = require('../transformer.cjs');
|
|
1657
|
+
const code = `
|
|
1658
|
+
export function Showcase() {
|
|
1659
|
+
return (
|
|
1660
|
+
<div className="features">
|
|
1661
|
+
<FeatureCard title="Lightning Fast" description="Instant response times" />
|
|
1662
|
+
</div>
|
|
1663
|
+
);
|
|
1664
|
+
}
|
|
1665
|
+
`;
|
|
1666
|
+
const profile = { root: '/tmp/test', hasSrc: true, jsxFiles: ['Showcase.tsx'], routes: [{ id: 'home', route: '/' }] };
|
|
1667
|
+
const analysis = analyzeFile({
|
|
1668
|
+
code,
|
|
1669
|
+
relativeFile: 'Showcase.tsx',
|
|
1670
|
+
profile,
|
|
1671
|
+
graph: { routesByFile: { 'Showcase.tsx': ['home'] } },
|
|
1672
|
+
ownerScope: 'home',
|
|
1673
|
+
});
|
|
1674
|
+
const propCandidates = analysis.candidates.filter((c) => c.operation === 'extract-prop');
|
|
1675
|
+
assert.ok(propCandidates.length >= 2);
|
|
1676
|
+
assert.ok(propCandidates.some((c) => c.value === 'Lightning Fast' && c.extra?.propName === 'title'));
|
|
1677
|
+
assert.ok(propCandidates.some((c) => c.value === 'Instant response times' && c.extra?.propName === 'description'));
|
|
1678
|
+
|
|
1679
|
+
const plan = planTransformations({
|
|
1680
|
+
profile,
|
|
1681
|
+
analyses: [{ ...analysis, relativeFile: 'Showcase.tsx', code }],
|
|
1682
|
+
});
|
|
1683
|
+
const filePlan = plan.files[0];
|
|
1684
|
+
const transformed = applyFilePlan(filePlan, profile);
|
|
1685
|
+
assert.equal(transformed.changed, true);
|
|
1686
|
+
assert.ok(transformed.code.includes('title={siteData?.content?.home'));
|
|
1687
|
+
assert.ok(transformed.code.includes('description={siteData?.content?.home'));
|
|
1688
|
+
});
|
|
1689
|
+
|
|
1690
|
+
test('manifest buildSiteDataAndManifest creates default dual-mode palette with dark mode overrides', () => {
|
|
1691
|
+
const { buildSiteDataAndManifest } = require('../manifest.cjs');
|
|
1692
|
+
const profile = {
|
|
1693
|
+
packageName: 'test-store',
|
|
1694
|
+
hasSrc: true,
|
|
1695
|
+
routes: [{ id: 'home', label: 'Home', route: '/', required: true }],
|
|
1696
|
+
};
|
|
1697
|
+
const plan = { files: [], usedPaths: [] };
|
|
1698
|
+
const bundle = buildSiteDataAndManifest({
|
|
1699
|
+
projectDir: '/tmp/test',
|
|
1700
|
+
projectName: 'test-store',
|
|
1701
|
+
profile,
|
|
1702
|
+
plan,
|
|
1703
|
+
});
|
|
1704
|
+
assert.ok(bundle.siteData.theme);
|
|
1705
|
+
assert.ok(bundle.siteData.theme.dark);
|
|
1706
|
+
assert.equal(bundle.siteData.theme.backgroundColor, '#ffffff');
|
|
1707
|
+
assert.equal(bundle.siteData.theme.dark.backgroundColor, '#0b0f19');
|
|
1708
|
+
assert.equal(bundle.siteData.theme.dark.textColor, '#f9fafb');
|
|
1709
|
+
});
|
|
1710
|
+
|
|
1711
|
+
|
|
1630
1712
|
|
|
1631
1713
|
|
|
1632
1714
|
|
package/src/arc/ast.cjs
CHANGED
|
@@ -240,6 +240,50 @@ function jsxTemplatePathAttr(attrName, prefix, indexName, suffix = '') {
|
|
|
240
240
|
);
|
|
241
241
|
}
|
|
242
242
|
|
|
243
|
+
function unwrapExpr(node) {
|
|
244
|
+
let curr = node;
|
|
245
|
+
while (
|
|
246
|
+
curr &&
|
|
247
|
+
(curr.type === 'TSAsExpression' ||
|
|
248
|
+
curr.type === 'TSTypeAssertion' ||
|
|
249
|
+
curr.type === 'TypeCastExpression' ||
|
|
250
|
+
curr.type === 'TSNonNullExpression' ||
|
|
251
|
+
curr.type === 'ParenthesizedExpression')
|
|
252
|
+
) {
|
|
253
|
+
curr = curr.expression;
|
|
254
|
+
}
|
|
255
|
+
return curr;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function extractItemMemberName(expr, binding) {
|
|
259
|
+
if (!expr) return null;
|
|
260
|
+
const unwrapped = unwrapExpr(expr);
|
|
261
|
+
if (!unwrapped) return null;
|
|
262
|
+
|
|
263
|
+
if (unwrapped.type === 'MemberExpression' && !unwrapped.computed) {
|
|
264
|
+
if (
|
|
265
|
+
(unwrapped.object?.type === 'Identifier' && unwrapped.object.name === binding) ||
|
|
266
|
+
(unwrapped.object?.type === 'JSXIdentifier' && unwrapped.object.name === binding)
|
|
267
|
+
) {
|
|
268
|
+
return unwrapped.property?.name || null;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (unwrapped.type === 'LogicalExpression' || unwrapped.type === 'BinaryExpression') {
|
|
273
|
+
return extractItemMemberName(unwrapped.left, binding) || extractItemMemberName(unwrapped.right, binding);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (unwrapped.type === 'ConditionalExpression') {
|
|
277
|
+
return (
|
|
278
|
+
extractItemMemberName(unwrapped.consequent, binding) ||
|
|
279
|
+
extractItemMemberName(unwrapped.alternate, binding) ||
|
|
280
|
+
extractItemMemberName(unwrapped.test, binding)
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
|
|
243
287
|
function wrapTextInEditableSpan(fieldPath, fallback, fieldType) {
|
|
244
288
|
return b.jsxElement(
|
|
245
289
|
b.jsxOpeningElement(
|
|
@@ -489,6 +533,8 @@ module.exports = {
|
|
|
489
533
|
jsxStaticAttr,
|
|
490
534
|
jsxTemplatePathAttr,
|
|
491
535
|
wrapTextInEditableSpan,
|
|
536
|
+
unwrapExpr,
|
|
537
|
+
extractItemMemberName,
|
|
492
538
|
hasDirective,
|
|
493
539
|
ensureImport,
|
|
494
540
|
ensureDefaultImport,
|
package/src/arc/field-paths.cjs
CHANGED
|
@@ -83,6 +83,10 @@ function inferFieldName(kind, tag, text, extra = {}) {
|
|
|
83
83
|
if (extra.cta) return extra.cta === 'primary' ? 'primaryCtaLabel' : `${extra.cta}Label`;
|
|
84
84
|
return toCamel([text || '', 'label']) || (extra.action ? `${extra.action}Label` : 'ctaLabel');
|
|
85
85
|
}
|
|
86
|
+
if (extra.isBrandLogo) {
|
|
87
|
+
if (kind === 'image') return 'logoUrl';
|
|
88
|
+
return 'logoText';
|
|
89
|
+
}
|
|
86
90
|
if (kind === 'image') return extra.alt ? toCamel([extra.alt, 'image']) || 'image' : 'image';
|
|
87
91
|
if (kind === 'alt') return extra.imageField ? extra.imageField.replace(/Image$/, 'ImageAlt').replace(/image$/, 'imageAlt') : 'imageAlt';
|
|
88
92
|
if (kind === 'placeholder') return toCamel([text || '', 'placeholder']) || 'placeholder';
|
package/src/arc/manifest.cjs
CHANGED
|
@@ -62,6 +62,37 @@ function setDeep(target, pathStr, value) {
|
|
|
62
62
|
if (curr[last] === undefined) curr[last] = value;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
function defaultDualModeTheme(existingTheme = {}) {
|
|
66
|
+
const base = {
|
|
67
|
+
primaryColor: '#0284c7',
|
|
68
|
+
secondaryColor: '#0f172a',
|
|
69
|
+
accentColor: '#38bdf8',
|
|
70
|
+
backgroundColor: '#ffffff',
|
|
71
|
+
cardBackgroundColor: '#f8fafc',
|
|
72
|
+
textColor: '#0f172a',
|
|
73
|
+
borderColor: '#e2e8f0',
|
|
74
|
+
headingFont: 'Outfit, sans-serif',
|
|
75
|
+
bodyFont: 'Inter, sans-serif',
|
|
76
|
+
borderRadius: '12px',
|
|
77
|
+
dark: {
|
|
78
|
+
primaryColor: '#38bdf8',
|
|
79
|
+
secondaryColor: '#f8fafc',
|
|
80
|
+
accentColor: '#0284c7',
|
|
81
|
+
backgroundColor: '#0b0f19',
|
|
82
|
+
cardBackgroundColor: '#111827',
|
|
83
|
+
textColor: '#f9fafb',
|
|
84
|
+
borderColor: '#1f2937',
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
if (isPlainObject(existingTheme)) {
|
|
88
|
+
Object.assign(base, existingTheme);
|
|
89
|
+
if (isPlainObject(existingTheme.dark)) {
|
|
90
|
+
base.dark = { ...base.dark, ...existingTheme.dark };
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return base;
|
|
94
|
+
}
|
|
95
|
+
|
|
65
96
|
function getDeep(target, pathStr) {
|
|
66
97
|
const parts = String(pathStr).split('.').filter(Boolean);
|
|
67
98
|
let curr = target;
|
|
@@ -149,11 +180,13 @@ function upsertSchemaList(sections, listPath, itemFields, items) {
|
|
|
149
180
|
const key = rest[rest.length - 1];
|
|
150
181
|
if (fields.some((f) => f.key === key)) return;
|
|
151
182
|
|
|
183
|
+
const isPrimitiveList = (itemFields || []).length === 0;
|
|
152
184
|
fields.push({
|
|
153
185
|
key,
|
|
154
186
|
type: 'list',
|
|
155
187
|
label: humanLabel(key),
|
|
156
188
|
itemLabel: humanLabel(key).replace(/s$/, '') || 'Item',
|
|
189
|
+
itemType: isPrimitiveList ? 'string' : 'object',
|
|
157
190
|
minItems: 0,
|
|
158
191
|
maxItems: Math.max((items || []).length, 12),
|
|
159
192
|
fields: (itemFields || []).map((field) => ({
|
|
@@ -384,7 +417,7 @@ function ensurePlatformSections(sections, content, boundListPaths = []) {
|
|
|
384
417
|
{ key: 'businessSummary', type: 'textarea', label: 'Business summary' },
|
|
385
418
|
{ key: 'additionalBusinessDetails', type: 'textarea', label: 'Additional business details' },
|
|
386
419
|
{ key: 'referenceWebsiteUrl', type: 'url', label: 'Reference website URL' },
|
|
387
|
-
{ key: 'guidanceNotes', type: 'object', label: 'AI Guidance Notes' },
|
|
420
|
+
{ key: 'guidanceNotes', type: 'object', label: 'AI Guidance Notes', fields: [] },
|
|
388
421
|
],
|
|
389
422
|
});
|
|
390
423
|
}
|
|
@@ -572,7 +605,10 @@ function buildSiteDataAndManifest({
|
|
|
572
605
|
id: `${projectName}-template`,
|
|
573
606
|
name: projectName,
|
|
574
607
|
engine: 'NEXT_STATIC_EXPORT',
|
|
575
|
-
structure: {
|
|
608
|
+
structure: {
|
|
609
|
+
pages: routes.map((p) => p.id),
|
|
610
|
+
theme: defaultDualModeTheme(existingSiteData?.template?.structure?.theme || existingSiteData?.theme),
|
|
611
|
+
},
|
|
576
612
|
},
|
|
577
613
|
requirements: existingSiteData?.requirements || {
|
|
578
614
|
requiredPages: routes.filter((p) => p.required).map((p) => p.id),
|
|
@@ -580,7 +616,7 @@ function buildSiteDataAndManifest({
|
|
|
580
616
|
},
|
|
581
617
|
content,
|
|
582
618
|
styles: collectStylesFromPlan(plan, existingSiteData?.styles),
|
|
583
|
-
|
|
619
|
+
theme: defaultDualModeTheme(existingSiteData?.theme || existingSiteData?.template?.structure?.theme),
|
|
584
620
|
};
|
|
585
621
|
|
|
586
622
|
applyFontTheme(siteData, collectFontIdsFromSiteData(siteData));
|
package/src/arc/planner.cjs
CHANGED
|
@@ -159,6 +159,16 @@ function planTransformations({ profile, analyses, recipe }) {
|
|
|
159
159
|
field: inferFieldName('placeholder', candidate.tag, candidate.value, extra),
|
|
160
160
|
used: usedPaths,
|
|
161
161
|
});
|
|
162
|
+
} else if (candidate.operation === 'extract-prop') {
|
|
163
|
+
const propName = extra.propName || 'text';
|
|
164
|
+
transform.field = buildFieldPath({
|
|
165
|
+
scope,
|
|
166
|
+
section,
|
|
167
|
+
field: inferFieldName(propName, candidate.tag, candidate.value, extra),
|
|
168
|
+
used: usedPaths,
|
|
169
|
+
});
|
|
170
|
+
transform.propName = propName;
|
|
171
|
+
transform.fieldType = classifyFieldType('text', candidate.value);
|
|
162
172
|
} else if (candidate.operation === 'collection-conversion') {
|
|
163
173
|
// A collection is named after the developer's own array variable so the
|
|
164
174
|
// merchant sees "products", not "items2".
|
package/src/arc/residual.cjs
CHANGED
|
@@ -95,11 +95,12 @@ function inMapCallback(pathNode) {
|
|
|
95
95
|
return false;
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
function isMeaningfulVisibleText(text) {
|
|
98
|
+
function isMeaningfulVisibleText(text, isLogoOrList = false) {
|
|
99
99
|
const value = String(text || '').replace(/\s+/g, ' ').trim();
|
|
100
|
-
if (!value
|
|
100
|
+
if (!value) return false;
|
|
101
|
+
if (value.length < 2 && !isLogoOrList) return false;
|
|
101
102
|
if (!/\p{L}/u.test(value)) return false;
|
|
102
|
-
if (isStaticSkipText(value)) return false;
|
|
103
|
+
if (isStaticSkipText(value, isLogoOrList)) return false;
|
|
103
104
|
if (CHROME_TEXT_RE.test(value)) return false;
|
|
104
105
|
return true;
|
|
105
106
|
}
|
|
@@ -126,8 +127,16 @@ function classifyResidual(node, text, tag) {
|
|
|
126
127
|
if (IMAGE_TAGS.has(tag) && attrLiteral(node, 'src')) {
|
|
127
128
|
return { bind: true, kind: 'image', value: attrLiteral(node, 'src') };
|
|
128
129
|
}
|
|
129
|
-
|
|
130
|
-
|
|
130
|
+
const isExplicitLogo = /\b(site-logo|brand-logo|nav-logo|header-logo|monogram)\b/i.test(className);
|
|
131
|
+
const ariaLabel = attrLiteral(node, 'aria-label');
|
|
132
|
+
const isLogoContext =
|
|
133
|
+
isExplicitLogo ||
|
|
134
|
+
((tag === 'a' || tag === 'Link' || tag === 'span') &&
|
|
135
|
+
(attrLiteral(node, 'href') === '/' || /\b(logo|home)\b/i.test(ariaLabel)));
|
|
136
|
+
const isListContext = tag === 'li' || /list-item|bullet/i.test(className);
|
|
137
|
+
|
|
138
|
+
if (isMeaningfulVisibleText(text, isLogoContext || isListContext)) {
|
|
139
|
+
return { bind: true, kind: 'text', value: text, isBrandLogo: isLogoContext };
|
|
131
140
|
}
|
|
132
141
|
if (text && text.trim()) {
|
|
133
142
|
return { bind: false, reason: 'decorative-copy' };
|
|
@@ -298,7 +307,8 @@ function applyResidualPass({ code, file, ownerScope, usedPaths, componentName, r
|
|
|
298
307
|
applied++;
|
|
299
308
|
}
|
|
300
309
|
|
|
301
|
-
if (decision && decision.bind && decision.kind === 'text'
|
|
310
|
+
if (decision && decision.bind && decision.kind === 'text') {
|
|
311
|
+
const isLogoText = Boolean(decision.isBrandLogo);
|
|
302
312
|
const section = inferSection({
|
|
303
313
|
componentName,
|
|
304
314
|
fileName: file,
|
|
@@ -307,9 +317,9 @@ function applyResidualPass({ code, file, ownerScope, usedPaths, componentName, r
|
|
|
307
317
|
role,
|
|
308
318
|
});
|
|
309
319
|
const field = buildFieldPath({
|
|
310
|
-
scope: ownerScope || 'home',
|
|
320
|
+
scope: (isLogoText && (role === 'navigation' || /header|footer|nav/i.test(file))) ? 'common' : (ownerScope || 'home'),
|
|
311
321
|
section,
|
|
312
|
-
field: inferFieldName('text', tag, text, { tag }),
|
|
322
|
+
field: inferFieldName('text', tag, text, { tag, isBrandLogo: isLogoText }),
|
|
313
323
|
used,
|
|
314
324
|
});
|
|
315
325
|
const fieldType = classifyFieldType('text', text);
|
|
@@ -372,10 +382,30 @@ function applyResidualPass({ code, file, ownerScope, usedPaths, componentName, r
|
|
|
372
382
|
fields.push({ path: field, type: fieldType, value: text });
|
|
373
383
|
applied++;
|
|
374
384
|
}
|
|
375
|
-
} else
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
385
|
+
} else {
|
|
386
|
+
const nextChildren = [];
|
|
387
|
+
let wrapped = false;
|
|
388
|
+
for (const child of node.children || []) {
|
|
389
|
+
if (!wrapped && child.type === 'JSXText' && child.value.replace(/\s+/g, '').length) {
|
|
390
|
+
const leading = child.value.match(/^\s*/)?.[0] || '';
|
|
391
|
+
const trailing = child.value.match(/\s*$/)?.[0] || '';
|
|
392
|
+
if (leading) nextChildren.push(b.jsxText(leading));
|
|
393
|
+
nextChildren.push(wrapTextInEditableSpan(field, text.trim(), fieldType));
|
|
394
|
+
if (trailing) nextChildren.push(b.jsxText(trailing));
|
|
395
|
+
wrapped = true;
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
nextChildren.push(child);
|
|
399
|
+
}
|
|
400
|
+
if (wrapped) {
|
|
401
|
+
node.children = nextChildren;
|
|
402
|
+
fields.push({ path: field, type: fieldType, value: text.trim() });
|
|
403
|
+
applied++;
|
|
404
|
+
} else if (!BROAD_CONTENT_CONTAINERS.has(lower)) {
|
|
405
|
+
if (ensureStaticOnLeaf(node, 'non-leaf-copy')) applied++;
|
|
406
|
+
} else if (wrapFirstLiteralAsStatic(node, 'container-copy')) {
|
|
407
|
+
applied++;
|
|
408
|
+
}
|
|
379
409
|
}
|
|
380
410
|
this.traverse(pathNode);
|
|
381
411
|
return;
|