@hale-bopp/valentino-engine 2.0.0 → 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.
Files changed (51) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +2 -2
  3. package/dist/bin/commands/llms.d.ts +2 -0
  4. package/dist/bin/commands/llms.d.ts.map +1 -0
  5. package/dist/bin/commands/llms.js +84 -0
  6. package/dist/bin/commands/llms.js.map +1 -0
  7. package/dist/bin/valentino.js +6 -1
  8. package/dist/bin/valentino.js.map +1 -1
  9. package/dist/core/animation.d.ts +18 -0
  10. package/dist/core/animation.d.ts.map +1 -0
  11. package/dist/core/animation.js +91 -0
  12. package/dist/core/animation.js.map +1 -0
  13. package/dist/core/editor.d.ts +80 -0
  14. package/dist/core/editor.d.ts.map +1 -0
  15. package/dist/core/editor.js +421 -0
  16. package/dist/core/editor.js.map +1 -0
  17. package/dist/core/encoding.d.ts +32 -0
  18. package/dist/core/encoding.d.ts.map +1 -0
  19. package/dist/core/encoding.js +112 -0
  20. package/dist/core/encoding.js.map +1 -0
  21. package/dist/core/gallery.d.ts +53 -0
  22. package/dist/core/gallery.d.ts.map +1 -0
  23. package/dist/core/gallery.js +123 -0
  24. package/dist/core/gallery.js.map +1 -0
  25. package/dist/core/gallery.test.d.ts +2 -0
  26. package/dist/core/gallery.test.d.ts.map +1 -0
  27. package/dist/core/gallery.test.js +127 -0
  28. package/dist/core/gallery.test.js.map +1 -0
  29. package/dist/core/guardrails-cms.d.ts +31 -1
  30. package/dist/core/guardrails-cms.d.ts.map +1 -1
  31. package/dist/core/guardrails-cms.js +160 -0
  32. package/dist/core/guardrails-cms.js.map +1 -1
  33. package/dist/core/llms-generator.d.ts +36 -0
  34. package/dist/core/llms-generator.d.ts.map +1 -0
  35. package/dist/core/llms-generator.js +95 -0
  36. package/dist/core/llms-generator.js.map +1 -0
  37. package/dist/core/page-generator.d.ts +85 -0
  38. package/dist/core/page-generator.d.ts.map +1 -0
  39. package/dist/core/page-generator.js +353 -0
  40. package/dist/core/page-generator.js.map +1 -0
  41. package/dist/core/page-generator.test.d.ts +2 -0
  42. package/dist/core/page-generator.test.d.ts.map +1 -0
  43. package/dist/core/page-generator.test.js +138 -0
  44. package/dist/core/page-generator.test.js.map +1 -0
  45. package/dist/core/types.d.ts +11 -0
  46. package/dist/core/types.d.ts.map +1 -1
  47. package/dist/index.d.ts +14 -1
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +14 -2
  50. package/dist/index.js.map +1 -1
  51. package/package.json +2 -2
@@ -0,0 +1,421 @@
1
+ /**
2
+ * Editor utilities — Pure functions for visual page spec editing.
3
+ * PBI #602 — Editor visuale non-tecnico.
4
+ *
5
+ * Provides:
6
+ * - generateEditorSchema(sectionType) — JSON-like schema for form generation
7
+ * - applySectionPatch(spec, sectionIndex, patch) — immutable update + validation
8
+ * - getEditableSectionTypes() — list of section types supported by editor
9
+ */
10
+ import { validatePageSpec } from './page-spec.js';
11
+ // ---------------------------------------------------------------------------
12
+ // Shared field definitions (reusable across section types)
13
+ // ---------------------------------------------------------------------------
14
+ const presentationFields = [
15
+ { key: 'presentation.surface', label: 'Surface', type: 'enum', enumValues: ['default', 'muted', 'accent', 'dark', 'shell-dark', 'reading-light', 'ops-light'], group: 'presentation' },
16
+ { key: 'presentation.surfaceScreen', label: 'Surface Screen', type: 'enum', enumValues: ['none', 'soft', 'immersive', 'inherit'], group: 'presentation' },
17
+ { key: 'presentation.height', label: 'Height', type: 'enum', enumValues: ['content', 'screen-sm', 'screen-md', 'screen-full'], group: 'presentation' },
18
+ { key: 'presentation.tone', label: 'Tone', type: 'enum', enumValues: ['default', 'elevated', 'immersive'], group: 'presentation' },
19
+ { key: 'presentation.rhythmProfile', label: 'Rhythm Profile', type: 'enum', enumValues: ['hero', 'transition', 'feature', 'reading', 'proof', 'metrics', 'ops'], group: 'presentation' },
20
+ { key: 'presentation.rhythmGroup', label: 'Rhythm Group', type: 'string', group: 'presentation' },
21
+ { key: 'presentation.seamProfile', label: 'Seam Profile', type: 'enum', enumValues: ['none', 'hero-intro-merge', 'hero-to-light'], group: 'presentation' },
22
+ { key: 'presentation.surfaceEntry', label: 'Surface Entry', type: 'enum', enumValues: ['slab', 'bleed', 'floating'], group: 'presentation' },
23
+ { key: 'presentation.contentLift', label: 'Content Lift', type: 'enum', enumValues: ['none', 'sm', 'md', 'lg'], group: 'presentation' },
24
+ { key: 'presentation.visualStage', label: 'Visual Stage', type: 'string', group: 'presentation' },
25
+ { key: 'presentation.presetId', label: 'Preset ID', type: 'string', group: 'presentation' },
26
+ ];
27
+ const animationFields = [
28
+ { key: 'animation.entrance', label: 'Entrance Animation', type: 'enum', enumValues: ['fade-up', 'fade-in', 'slide-left', 'slide-right', 'scale-in', 'none'], group: 'animation' },
29
+ { key: 'animation.delay', label: 'Delay', type: 'enum', enumValues: ['none', 'stagger'], group: 'animation' },
30
+ { key: 'animation.trigger', label: 'Trigger', type: 'enum', enumValues: ['viewport', 'immediate'], group: 'animation' },
31
+ { key: 'animation.duration', label: 'Duration (ms)', type: 'number', defaultValue: 400, group: 'animation' },
32
+ ];
33
+ const ctaFields = (prefix, label) => [
34
+ { key: `${prefix}.labelKey`, label: `${label} Label Key`, type: 'string', group: 'content' },
35
+ { key: `${prefix}.action.type`, label: `${label} Action Type`, type: 'enum', enumValues: ['link', 'noop'], group: 'content' },
36
+ { key: `${prefix}.action.href`, label: `${label} Href`, type: 'string', group: 'content' },
37
+ ];
38
+ // ---------------------------------------------------------------------------
39
+ // Per-section-type schema definitions
40
+ // ---------------------------------------------------------------------------
41
+ const SECTION_SCHEMAS = {
42
+ hero: () => ({
43
+ sectionType: 'hero',
44
+ label: 'Hero',
45
+ fields: [
46
+ { key: 'titleKey', label: 'Title Key', type: 'string', required: true, group: 'content' },
47
+ { key: 'eyebrowKey', label: 'Eyebrow Key', type: 'string', group: 'content' },
48
+ { key: 'taglineKey', label: 'Tagline Key', type: 'string', group: 'content' },
49
+ { key: 'supportKey', label: 'Support Key', type: 'string', group: 'content' },
50
+ { key: 'mottoKey', label: 'Motto Key', type: 'string', group: 'content' },
51
+ { key: 'poeticAsideKey', label: 'Poetic Aside Key', type: 'string', group: 'content' },
52
+ { key: 'layout', label: 'Layout', type: 'enum', enumValues: ['default', 'split'], group: 'layout' },
53
+ { key: 'layoutMode', label: 'Layout Mode', type: 'enum', enumValues: ['stack', 'split', 'stage-right'], group: 'layout' },
54
+ { key: 'primaryFocus', label: 'Primary Focus', type: 'enum', enumValues: ['copy', 'action-box', 'balanced'], group: 'layout' },
55
+ { key: 'heroPanel', label: 'Hero Panel', type: 'enum', enumValues: ['none', 'inline', 'right-box'], group: 'layout' },
56
+ { key: 'foldVisibility', label: 'Fold Visibility', type: 'enum', enumValues: ['immediate', 'scroll'], group: 'layout' },
57
+ ...ctaFields('cta', 'CTA'),
58
+ ...ctaFields('ctaSecondary', 'CTA Secondary'),
59
+ { key: 'visualAssetPath', label: 'Visual Asset Path', type: 'string', group: 'content' },
60
+ ...presentationFields,
61
+ ...animationFields,
62
+ ],
63
+ }),
64
+ cards: () => ({
65
+ sectionType: 'cards',
66
+ label: 'Cards',
67
+ fields: [
68
+ { key: 'titleKey', label: 'Title Key', type: 'string', group: 'content' },
69
+ { key: 'descKey', label: 'Description Key', type: 'string', group: 'content' },
70
+ { key: 'variant', label: 'Variant', type: 'enum', enumValues: ['catalog'], required: true, group: 'layout' },
71
+ { key: 'density', label: 'Density', type: 'enum', enumValues: ['default', 'compact'], group: 'layout' },
72
+ {
73
+ key: 'items', label: 'Cards', type: 'array', required: true, group: 'content',
74
+ itemFields: [
75
+ { key: 'titleKey', label: 'Title Key', type: 'string', required: true },
76
+ { key: 'descKey', label: 'Description Key', type: 'string' },
77
+ { key: 'iconText', label: 'Icon Text', type: 'string' },
78
+ { key: 'badgeKey', label: 'Badge Key', type: 'string' },
79
+ ],
80
+ },
81
+ ...presentationFields,
82
+ ...animationFields,
83
+ ],
84
+ }),
85
+ comparison: () => ({
86
+ sectionType: 'comparison',
87
+ label: 'Comparison',
88
+ fields: [
89
+ { key: 'titleKey', label: 'Title Key', type: 'string', required: true, group: 'content' },
90
+ { key: 'subtitleKey', label: 'Subtitle Key', type: 'string', group: 'content' },
91
+ { key: 'left.titleKey', label: 'Left Title Key', type: 'string', required: true, group: 'content' },
92
+ { key: 'left.itemsKeys', label: 'Left Items Keys', type: 'json', required: true, group: 'content', description: 'JSON array of i18n keys' },
93
+ { key: 'right.titleKey', label: 'Right Title Key', type: 'string', required: true, group: 'content' },
94
+ { key: 'right.itemsKeys', label: 'Right Items Keys', type: 'json', required: true, group: 'content', description: 'JSON array of i18n keys' },
95
+ ...presentationFields,
96
+ ...animationFields,
97
+ ],
98
+ }),
99
+ cta: () => ({
100
+ sectionType: 'cta',
101
+ label: 'Call to Action',
102
+ fields: [
103
+ { key: 'titleKey', label: 'Title Key', type: 'string', required: true, group: 'content' },
104
+ { key: 'bodyKey', label: 'Body Key', type: 'string', group: 'content' },
105
+ ...ctaFields('primary', 'Primary CTA'),
106
+ ...ctaFields('secondary', 'Secondary CTA'),
107
+ ...presentationFields,
108
+ ...animationFields,
109
+ ],
110
+ }),
111
+ form: () => ({
112
+ sectionType: 'form',
113
+ label: 'Form',
114
+ fields: [
115
+ { key: 'titleKey', label: 'Title Key', type: 'string', required: true, group: 'content' },
116
+ { key: 'leadKey', label: 'Lead Key', type: 'string', group: 'content' },
117
+ { key: 'variant', label: 'Variant', type: 'enum', enumValues: ['demo'], group: 'layout' },
118
+ { key: 'submitKey', label: 'Submit Label Key', type: 'string', required: true, group: 'content' },
119
+ { key: 'consentKey', label: 'Consent Key', type: 'string', group: 'content' },
120
+ { key: 'legalKey', label: 'Legal Key', type: 'string', group: 'content' },
121
+ {
122
+ key: 'fields', label: 'Form Fields', type: 'array', required: true, group: 'content',
123
+ itemFields: [
124
+ { key: 'name', label: 'Field Name', type: 'string', required: true },
125
+ { key: 'type', label: 'Field Type', type: 'enum', enumValues: ['text', 'email', 'select', 'textarea', 'checkbox'], required: true },
126
+ { key: 'labelKey', label: 'Label Key', type: 'string', required: true },
127
+ { key: 'placeholderKey', label: 'Placeholder Key', type: 'string' },
128
+ { key: 'required', label: 'Required', type: 'boolean' },
129
+ { key: 'width', label: 'Width', type: 'enum', enumValues: ['half', 'full'] },
130
+ ],
131
+ },
132
+ ...presentationFields,
133
+ ...animationFields,
134
+ ],
135
+ }),
136
+ manifesto: () => ({
137
+ sectionType: 'manifesto',
138
+ label: 'Manifesto',
139
+ fields: [
140
+ { key: 'contentPrefix', label: 'Content Prefix', type: 'string', group: 'content', description: 'i18n key prefix for manifesto paragraphs' },
141
+ { key: 'ctaLabelKey', label: 'CTA Label Key', type: 'string', group: 'content' },
142
+ { key: 'ctaHref', label: 'CTA Href', type: 'string', group: 'content' },
143
+ ...presentationFields,
144
+ ...animationFields,
145
+ ],
146
+ }),
147
+ spacer: () => ({
148
+ sectionType: 'spacer',
149
+ label: 'Spacer',
150
+ fields: [
151
+ { key: 'size', label: 'Size', type: 'enum', enumValues: ['sm', 'md', 'lg'], group: 'layout' },
152
+ ...presentationFields,
153
+ ],
154
+ }),
155
+ stats: () => ({
156
+ sectionType: 'stats',
157
+ label: 'Stats',
158
+ fields: [
159
+ { key: 'titleKey', label: 'Title Key', type: 'string', group: 'content' },
160
+ {
161
+ key: 'items', label: 'Stat Items', type: 'array', required: true, group: 'content',
162
+ itemFields: [
163
+ { key: 'valueKey', label: 'Value Key', type: 'string', required: true },
164
+ { key: 'labelKey', label: 'Label Key', type: 'string', required: true },
165
+ ],
166
+ },
167
+ ...presentationFields,
168
+ ...animationFields,
169
+ ],
170
+ }),
171
+ 'how-it-works': () => ({
172
+ sectionType: 'how-it-works',
173
+ label: 'How It Works',
174
+ fields: [
175
+ { key: 'titleKey', label: 'Title Key', type: 'string', group: 'content' },
176
+ { key: 'subtitleKey', label: 'Subtitle Key', type: 'string', group: 'content' },
177
+ {
178
+ key: 'steps', label: 'Steps', type: 'array', required: true, group: 'content',
179
+ itemFields: [
180
+ { key: 'numKey', label: 'Number Key', type: 'string', required: true },
181
+ { key: 'titleKey', label: 'Title Key', type: 'string', required: true },
182
+ { key: 'descKey', label: 'Description Key', type: 'string', required: true },
183
+ ],
184
+ },
185
+ ...presentationFields,
186
+ ...animationFields,
187
+ ],
188
+ }),
189
+ advisor: () => ({
190
+ sectionType: 'advisor',
191
+ label: 'Advisor',
192
+ fields: [
193
+ { key: 'titleKey', label: 'Title Key', type: 'string', required: true, group: 'content' },
194
+ { key: 'leadKey', label: 'Lead Key', type: 'string', group: 'content' },
195
+ { key: 'promptPlaceholderKey', label: 'Prompt Placeholder Key', type: 'string', group: 'content' },
196
+ { key: 'submitKey', label: 'Submit Key', type: 'string', required: true, group: 'content' },
197
+ { key: 'submitUrl', label: 'Submit URL', type: 'string', group: 'content' },
198
+ { key: 'loadingKey', label: 'Loading Key', type: 'string', group: 'content' },
199
+ { key: 'fallbackTitleKey', label: 'Fallback Title Key', type: 'string', required: true, group: 'content' },
200
+ { key: 'fallbackBodyKey', label: 'Fallback Body Key', type: 'string', required: true, group: 'content' },
201
+ {
202
+ key: 'prompts', label: 'Quick Prompts', type: 'array', required: true, group: 'content',
203
+ itemFields: [
204
+ { key: 'id', label: 'ID', type: 'string', required: true },
205
+ { key: 'labelKey', label: 'Label Key', type: 'string', required: true },
206
+ { key: 'answerTitleKey', label: 'Answer Title Key', type: 'string', required: true },
207
+ { key: 'answerBodyKey', label: 'Answer Body Key', type: 'string', required: true },
208
+ ],
209
+ },
210
+ ...presentationFields,
211
+ ...animationFields,
212
+ ],
213
+ }),
214
+ 'mermaid-diagram': () => ({
215
+ sectionType: 'mermaid-diagram',
216
+ label: 'Mermaid Diagram',
217
+ fields: [
218
+ { key: 'titleKey', label: 'Title Key', type: 'string', group: 'content' },
219
+ { key: 'descKey', label: 'Description Key', type: 'string', group: 'content' },
220
+ { key: 'mermaidCode', label: 'Mermaid Code', type: 'text', required: true, group: 'content' },
221
+ { key: 'height', label: 'Height', type: 'enum', enumValues: ['auto', 'sm', 'md', 'lg'], group: 'layout' },
222
+ ...presentationFields,
223
+ ...animationFields,
224
+ ],
225
+ }),
226
+ 'data-list': () => ({
227
+ sectionType: 'data-list',
228
+ label: 'Data List',
229
+ fields: [
230
+ { key: 'titleKey', label: 'Title Key', type: 'string', group: 'content' },
231
+ { key: 'dataUrl', label: 'Data URL', type: 'string', required: true, group: 'content' },
232
+ {
233
+ key: 'columns', label: 'Columns', type: 'array', required: true, group: 'content',
234
+ itemFields: [
235
+ { key: 'key', label: 'Key', type: 'string', required: true },
236
+ { key: 'labelKey', label: 'Label Key', type: 'string', required: true },
237
+ { key: 'format', label: 'Format', type: 'enum', enumValues: ['datetime', 'date', 'currency'] },
238
+ ],
239
+ },
240
+ ...presentationFields,
241
+ ...animationFields,
242
+ ],
243
+ }),
244
+ 'action-form': () => ({
245
+ sectionType: 'action-form',
246
+ label: 'Action Form',
247
+ fields: [
248
+ { key: 'titleKey', label: 'Title Key', type: 'string', required: true, group: 'content' },
249
+ { key: 'submitUrl', label: 'Submit URL', type: 'string', required: true, group: 'content' },
250
+ { key: 'submitKey', label: 'Submit Label Key', type: 'string', required: true, group: 'content' },
251
+ { key: 'successKey', label: 'Success Message Key', type: 'string', required: true, group: 'content' },
252
+ {
253
+ key: 'fields', label: 'Fields', type: 'array', required: true, group: 'content',
254
+ itemFields: [
255
+ { key: 'name', label: 'Field Name', type: 'string', required: true },
256
+ { key: 'type', label: 'Field Type', type: 'enum', enumValues: ['text', 'email', 'number', 'date', 'datetime-local', 'textarea'], required: true },
257
+ { key: 'labelKey', label: 'Label Key', type: 'string', required: true },
258
+ { key: 'placeholderKey', label: 'Placeholder Key', type: 'string' },
259
+ { key: 'required', label: 'Required', type: 'boolean' },
260
+ ],
261
+ },
262
+ ...presentationFields,
263
+ ...animationFields,
264
+ ],
265
+ }),
266
+ };
267
+ // ---------------------------------------------------------------------------
268
+ // Public API
269
+ // ---------------------------------------------------------------------------
270
+ /**
271
+ * Returns the list of section types that the editor supports.
272
+ */
273
+ export function getEditableSectionTypes() {
274
+ return Object.keys(SECTION_SCHEMAS);
275
+ }
276
+ /**
277
+ * Generates the editor schema for a given section type.
278
+ * Returns null if the section type is not supported.
279
+ */
280
+ export function generateEditorSchema(sectionType) {
281
+ const factory = SECTION_SCHEMAS[sectionType];
282
+ return factory ? factory() : null;
283
+ }
284
+ /**
285
+ * Returns editor schemas for all supported section types.
286
+ */
287
+ export function generateAllEditorSchemas() {
288
+ return Object.values(SECTION_SCHEMAS).map((factory) => factory());
289
+ }
290
+ /**
291
+ * Deep-get a value from an object using a dot-separated path.
292
+ */
293
+ function deepGet(obj, path) {
294
+ const parts = path.split('.');
295
+ let current = obj;
296
+ for (const part of parts) {
297
+ if (current == null || typeof current !== 'object')
298
+ return undefined;
299
+ current = current[part];
300
+ }
301
+ return current;
302
+ }
303
+ /**
304
+ * Deep-set a value in an object using a dot-separated path (immutable — returns new object).
305
+ */
306
+ function deepSet(obj, path, value) {
307
+ const parts = path.split('.');
308
+ if (parts.length === 1) {
309
+ return { ...obj, [parts[0]]: value };
310
+ }
311
+ const [head, ...rest] = parts;
312
+ const child = (obj[head] != null && typeof obj[head] === 'object')
313
+ ? { ...obj[head] }
314
+ : {};
315
+ return { ...obj, [head]: deepSet(child, rest.join('.'), value) };
316
+ }
317
+ /**
318
+ * Apply a patch (key-value map) to a section within a PageSpec.
319
+ * Returns a new PageSpec (immutable) + validation warnings.
320
+ *
321
+ * Keys use dot notation for nested fields (e.g. "presentation.surface").
322
+ * If value is null/undefined, the field is removed.
323
+ */
324
+ export function applySectionPatch(spec, sectionIndex, patch) {
325
+ const warnings = [];
326
+ if (sectionIndex < 0 || sectionIndex >= spec.sections.length) {
327
+ return {
328
+ spec,
329
+ warnings: [{ field: '_index', message: `Section index ${sectionIndex} out of bounds (0..${spec.sections.length - 1})` }],
330
+ };
331
+ }
332
+ // Clone sections array
333
+ const newSections = [...spec.sections];
334
+ let section = { ...newSections[sectionIndex] };
335
+ // Apply each patch field
336
+ for (const [key, value] of Object.entries(patch)) {
337
+ if (value === null || value === undefined) {
338
+ // Remove field — rebuild without it
339
+ const parts = key.split('.');
340
+ if (parts.length === 1) {
341
+ const { [key]: _removed, ...rest } = section;
342
+ section = rest;
343
+ }
344
+ else {
345
+ // For nested, set to undefined and let JSON.parse(JSON.stringify) clean it
346
+ section = deepSet(section, key, undefined);
347
+ }
348
+ }
349
+ else {
350
+ section = deepSet(section, key, value);
351
+ }
352
+ }
353
+ // Validate: section must still have a type
354
+ if (!section.type || typeof section.type !== 'string') {
355
+ warnings.push({ field: 'type', message: 'Section type is missing or invalid after patch' });
356
+ }
357
+ // Validate: check required fields per schema
358
+ const schema = section.type ? generateEditorSchema(section.type) : null;
359
+ if (schema) {
360
+ for (const field of schema.fields) {
361
+ if (field.required && deepGet(section, field.key) == null) {
362
+ warnings.push({ field: field.key, message: `Required field "${field.label}" is missing` });
363
+ }
364
+ }
365
+ }
366
+ newSections[sectionIndex] = section;
367
+ const newSpec = {
368
+ ...spec,
369
+ sections: newSections,
370
+ };
371
+ // Final validation
372
+ if (!validatePageSpec(newSpec)) {
373
+ warnings.push({ field: '_spec', message: 'Patched spec failed PageSpecV1 validation' });
374
+ }
375
+ return { spec: newSpec, warnings };
376
+ }
377
+ /**
378
+ * Add a new section to a PageSpec at a given index.
379
+ * Returns the updated spec.
380
+ */
381
+ export function addSection(spec, section, atIndex) {
382
+ const newSections = [...spec.sections];
383
+ const idx = atIndex ?? newSections.length;
384
+ newSections.splice(idx, 0, section);
385
+ return { ...spec, sections: newSections };
386
+ }
387
+ /**
388
+ * Remove a section from a PageSpec by index.
389
+ * Returns the updated spec.
390
+ */
391
+ export function removeSection(spec, sectionIndex) {
392
+ if (sectionIndex < 0 || sectionIndex >= spec.sections.length)
393
+ return spec;
394
+ const newSections = [...spec.sections];
395
+ newSections.splice(sectionIndex, 1);
396
+ return { ...spec, sections: newSections };
397
+ }
398
+ /**
399
+ * Move a section within a PageSpec (reorder).
400
+ * Returns the updated spec.
401
+ */
402
+ export function moveSection(spec, fromIndex, toIndex) {
403
+ if (fromIndex < 0 || fromIndex >= spec.sections.length)
404
+ return spec;
405
+ if (toIndex < 0 || toIndex >= spec.sections.length)
406
+ return spec;
407
+ if (fromIndex === toIndex)
408
+ return spec;
409
+ const newSections = [...spec.sections];
410
+ const [moved] = newSections.splice(fromIndex, 1);
411
+ newSections.splice(toIndex, 0, moved);
412
+ return { ...spec, sections: newSections };
413
+ }
414
+ /**
415
+ * Update page-level fields (id, titleKey, profile, themeId, chrome, seo).
416
+ * Returns the updated spec.
417
+ */
418
+ export function applyPagePatch(spec, patch) {
419
+ return { ...spec, ...patch, version: '1', sections: spec.sections };
420
+ }
421
+ //# sourceMappingURL=editor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editor.js","sourceRoot":"","sources":["../../src/core/editor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Encoding Guardrail — Mojibake detection and typographic validation.
3
+ * Pure functions: takes content strings, returns warnings.
4
+ * No I/O — consumer loads the content files.
5
+ * PBI #629.
6
+ */
7
+ export type EncodingWarning = {
8
+ type: 'mojibake' | 'typography';
9
+ severity: 'error' | 'warning';
10
+ file: string;
11
+ key: string;
12
+ message: string;
13
+ match: string;
14
+ };
15
+ /**
16
+ * Detect mojibake in content strings.
17
+ * Returns errors for each mojibake pattern found.
18
+ */
19
+ export declare function checkMojibake(contentByFile: Map<string, Map<string, string>>): EncodingWarning[];
20
+ /**
21
+ * Check typographic issues in content strings.
22
+ * Filters rules by language ('*' matches all).
23
+ */
24
+ export declare function checkTypography(contentByFile: Map<string, Map<string, string>>, lang?: string): EncodingWarning[];
25
+ /**
26
+ * Run all encoding checks (mojibake + typography).
27
+ */
28
+ export declare function checkEncoding(contentByFile: Map<string, Map<string, string>>, lang?: string): EncodingWarning[];
29
+ /** Exported constants for consumer use. */
30
+ export declare const MOJIBAKE_PATTERN_COUNT: number;
31
+ export declare const TYPO_RULE_COUNT: number;
32
+ //# sourceMappingURL=encoding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encoding.d.ts","sourceRoot":"","sources":["../../src/core/encoding.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,MAAM,eAAe,GAAG;IAC1B,IAAI,EAAE,UAAU,GAAG,YAAY,CAAC;IAChC,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACjB,CAAC;AAmDF;;;GAGG;AACH,wBAAgB,aAAa,CACzB,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAChD,eAAe,EAAE,CAwBnB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC3B,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAC/C,IAAI,CAAC,EAAE,MAAM,GACd,eAAe,EAAE,CA0BnB;AAED;;GAEG;AACH,wBAAgB,aAAa,CACzB,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAC/C,IAAI,CAAC,EAAE,MAAM,GACd,eAAe,EAAE,CAKnB;AAED,2CAA2C;AAC3C,eAAO,MAAM,sBAAsB,QAA2B,CAAC;AAC/D,eAAO,MAAM,eAAe,QAAoB,CAAC"}
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Encoding Guardrail — Mojibake detection and typographic validation.
3
+ * Pure functions: takes content strings, returns warnings.
4
+ * No I/O — consumer loads the content files.
5
+ * PBI #629.
6
+ */
7
+ const MOJIBAKE_PATTERNS = [
8
+ { pattern: /è/g, label: 'è (Latin-1→UTF-8)', fix: 'è' },
9
+ { pattern: /é/g, label: 'é (Latin-1→UTF-8)', fix: 'é' },
10
+ { pattern: /Ã /g, label: 'à (Latin-1→UTF-8)', fix: 'à' },
11
+ { pattern: /ù/g, label: 'ù (Latin-1→UTF-8)', fix: 'ù' },
12
+ { pattern: /ò/g, label: 'ò (Latin-1→UTF-8)', fix: 'ò' },
13
+ { pattern: /ì/g, label: 'ì (Latin-1→UTF-8)', fix: 'ì' },
14
+ { pattern: /ö/g, label: 'ö (Latin-1→UTF-8)', fix: 'ö' },
15
+ { pattern: /ü/g, label: 'ü (Latin-1→UTF-8)', fix: 'ü' },
16
+ { pattern: /ä/g, label: 'ä (Latin-1→UTF-8)', fix: 'ä' },
17
+ { pattern: /ñ/g, label: 'ñ (Latin-1→UTF-8)', fix: 'ñ' },
18
+ { pattern: /ç/g, label: 'ç (Latin-1→UTF-8)', fix: 'ç' },
19
+ { pattern: /’/g, label: 'right single quote (Win-1252)', fix: '\u2019' },
20
+ { pattern: /“/g, label: 'left double quote (Win-1252)', fix: '\u201C' },
21
+ { pattern: /â€\u009D/g, label: 'right double quote (Win-1252)', fix: '\u201D' },
22
+ ];
23
+ const TYPO_RULES = [
24
+ // Italian
25
+ { pattern: /\bE'\s/g, lang: 'it', label: "E' instead of È", suggestion: 'Use È (capital E with accent)' },
26
+ { pattern: /\bperche'\b/gi, lang: 'it', label: "perche' instead of perché", suggestion: 'Use perché' },
27
+ { pattern: /\bperche\b(?!')/gi, lang: 'it', label: "perche without accent", suggestion: 'Use perché' },
28
+ { pattern: /\bpiu'\b/gi, lang: 'it', label: "piu' instead of più", suggestion: 'Use più' },
29
+ { pattern: /\bcioe'\b/gi, lang: 'it', label: "cioe' instead of cioè", suggestion: 'Use cioè' },
30
+ { pattern: /\bpuo'\b/gi, lang: 'it', label: "puo' instead of può", suggestion: 'Use può' },
31
+ { pattern: /\bgiacche'\b/gi, lang: 'it', label: "giacche' instead of giacché", suggestion: 'Use giacché' },
32
+ // General typography
33
+ { pattern: /\.\.\./g, lang: '*', label: 'ASCII ellipsis', suggestion: 'Use … (U+2026)' },
34
+ { pattern: /--/g, lang: '*', label: 'double hyphen', suggestion: 'Use — (em dash) or – (en dash)' },
35
+ { pattern: /\s'/g, lang: '*', label: "space before apostrophe", suggestion: 'Remove space before apostrophe' },
36
+ { pattern: /"([^"]*)"/g, lang: '*', label: 'straight double quotes', suggestion: 'Use \u201C\u201D (curly quotes)' },
37
+ // French
38
+ { pattern: /\b(\w+)\s*\?/g, lang: 'fr', label: 'missing space before ?', suggestion: 'French requires a space before ?' },
39
+ { pattern: /\b(\w+)\s*!/g, lang: 'fr', label: 'missing space before !', suggestion: 'French requires a space before !' },
40
+ // German
41
+ { pattern: /\bfuer\b/gi, lang: 'de', label: 'fuer instead of für', suggestion: 'Use für' },
42
+ { pattern: /\bueber\b/gi, lang: 'de', label: 'ueber instead of über', suggestion: 'Use über' },
43
+ // Spanish
44
+ { pattern: /\banio\b/gi, lang: 'es', label: 'anio instead of año', suggestion: 'Use año' },
45
+ ];
46
+ /**
47
+ * Detect mojibake in content strings.
48
+ * Returns errors for each mojibake pattern found.
49
+ */
50
+ export function checkMojibake(contentByFile) {
51
+ const warnings = [];
52
+ for (const [file, entries] of contentByFile) {
53
+ for (const [key, value] of entries) {
54
+ for (const { pattern, label } of MOJIBAKE_PATTERNS) {
55
+ // Reset lastIndex for global patterns
56
+ pattern.lastIndex = 0;
57
+ const match = pattern.exec(value);
58
+ if (match) {
59
+ warnings.push({
60
+ type: 'mojibake',
61
+ severity: 'error',
62
+ file,
63
+ key,
64
+ message: `Mojibake detected: ${label}`,
65
+ match: match[0],
66
+ });
67
+ }
68
+ }
69
+ }
70
+ }
71
+ return warnings;
72
+ }
73
+ /**
74
+ * Check typographic issues in content strings.
75
+ * Filters rules by language ('*' matches all).
76
+ */
77
+ export function checkTypography(contentByFile, lang) {
78
+ const warnings = [];
79
+ const applicableRules = TYPO_RULES.filter((r) => r.lang === '*' || (lang && r.lang === lang));
80
+ for (const [file, entries] of contentByFile) {
81
+ for (const [key, value] of entries) {
82
+ for (const rule of applicableRules) {
83
+ rule.pattern.lastIndex = 0;
84
+ const match = rule.pattern.exec(value);
85
+ if (match) {
86
+ warnings.push({
87
+ type: 'typography',
88
+ severity: 'warning',
89
+ file,
90
+ key,
91
+ message: `${rule.label} — ${rule.suggestion}`,
92
+ match: match[0],
93
+ });
94
+ }
95
+ }
96
+ }
97
+ }
98
+ return warnings;
99
+ }
100
+ /**
101
+ * Run all encoding checks (mojibake + typography).
102
+ */
103
+ export function checkEncoding(contentByFile, lang) {
104
+ return [
105
+ ...checkMojibake(contentByFile),
106
+ ...checkTypography(contentByFile, lang),
107
+ ];
108
+ }
109
+ /** Exported constants for consumer use. */
110
+ export const MOJIBAKE_PATTERN_COUNT = MOJIBAKE_PATTERNS.length;
111
+ export const TYPO_RULE_COUNT = TYPO_RULES.length;
112
+ //# sourceMappingURL=encoding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encoding.js","sourceRoot":"","sources":["../../src/core/encoding.ts"],"names":[],"mappings":"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"}