@syntrologie/adapt-content 0.0.0-semantically-released → 1.0.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/dist/cdn.d.ts +35 -0
- package/dist/cdn.d.ts.map +1 -0
- package/dist/cdn.js +39 -0
- package/dist/editor.d.ts +11 -6
- package/dist/editor.d.ts.map +1 -1
- package/dist/editor.js +508 -5
- package/dist/runtime.d.ts.map +1 -1
- package/dist/schema.d.ts +34 -4
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +6 -0
- package/dist/summarize.d.ts +15 -0
- package/dist/summarize.d.ts.map +1 -0
- package/dist/summarize.js +78 -0
- package/dist/types.d.ts +16 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +14 -6
package/dist/cdn.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CDN Entry Point for Adaptive Content
|
|
3
|
+
*
|
|
4
|
+
* This module is bundled for CDN delivery and self-registers with the global
|
|
5
|
+
* SynOS app registry when loaded dynamically via the AppLoader.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* App manifest for registry registration.
|
|
9
|
+
* Follows the AppManifest interface expected by AppLoader/AppRegistry.
|
|
10
|
+
*/
|
|
11
|
+
export declare const manifest: {
|
|
12
|
+
id: string;
|
|
13
|
+
version: string;
|
|
14
|
+
name: string;
|
|
15
|
+
description: string;
|
|
16
|
+
runtime: {
|
|
17
|
+
actions: {
|
|
18
|
+
kind: "content:insertHtml" | "content:setText" | "content:setAttr" | "content:addClass" | "content:removeClass" | "content:setStyle";
|
|
19
|
+
executor: import("./types").ActionExecutor<import("./types").InsertHtmlAction> | import("./types").ActionExecutor<import("./types").SetTextAction> | import("./types").ActionExecutor<import("./types").SetAttrAction> | import("./types").ActionExecutor<import("./types").AddClassAction> | import("./types").ActionExecutor<import("./types").RemoveClassAction> | import("./types").ActionExecutor<import("./types").SetStyleAction>;
|
|
20
|
+
}[];
|
|
21
|
+
};
|
|
22
|
+
editor: {
|
|
23
|
+
panel: {
|
|
24
|
+
title: string;
|
|
25
|
+
icon: string;
|
|
26
|
+
description: string;
|
|
27
|
+
};
|
|
28
|
+
component: typeof import("./editor").ContentEditor;
|
|
29
|
+
};
|
|
30
|
+
metadata: {
|
|
31
|
+
isBuiltIn: boolean;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
export default manifest;
|
|
35
|
+
//# sourceMappingURL=cdn.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cdn.d.ts","sourceRoot":"","sources":["../src/cdn.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH;;;GAGG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;CAepB,CAAC;AAaF,eAAe,QAAQ,CAAC"}
|
package/dist/cdn.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CDN Entry Point for Adaptive Content
|
|
3
|
+
*
|
|
4
|
+
* This module is bundled for CDN delivery and self-registers with the global
|
|
5
|
+
* SynOS app registry when loaded dynamically via the AppLoader.
|
|
6
|
+
*/
|
|
7
|
+
import { editor } from './editor';
|
|
8
|
+
import { executors, runtime } from './runtime';
|
|
9
|
+
/**
|
|
10
|
+
* App manifest for registry registration.
|
|
11
|
+
* Follows the AppManifest interface expected by AppLoader/AppRegistry.
|
|
12
|
+
*/
|
|
13
|
+
export const manifest = {
|
|
14
|
+
id: runtime.id,
|
|
15
|
+
version: runtime.version,
|
|
16
|
+
name: runtime.name,
|
|
17
|
+
description: runtime.description,
|
|
18
|
+
runtime: {
|
|
19
|
+
actions: executors.map(({ kind, executor }) => ({
|
|
20
|
+
kind,
|
|
21
|
+
executor,
|
|
22
|
+
})),
|
|
23
|
+
},
|
|
24
|
+
editor,
|
|
25
|
+
metadata: {
|
|
26
|
+
isBuiltIn: true,
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Self-register with global registry if available.
|
|
31
|
+
* This happens when loaded via script tag (UMD).
|
|
32
|
+
*/
|
|
33
|
+
if (typeof window !== 'undefined') {
|
|
34
|
+
const registry = window.SynOS?.appRegistry;
|
|
35
|
+
if (registry && typeof registry.register === 'function') {
|
|
36
|
+
registry.register(manifest);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export default manifest;
|
package/dist/editor.d.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Adaptive Content - Editor
|
|
2
|
+
* Adaptive Content - Editor Component
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Review & tweak editor for AI-generated content modifications.
|
|
5
|
+
* Displays a scannable list of one-liner change summaries.
|
|
6
|
+
* Clicking a card navigates to the element and shows a floating edit panel.
|
|
5
7
|
*/
|
|
6
8
|
import type { EditorPanelProps } from './types';
|
|
7
|
-
|
|
8
|
-
* Content editor panel component.
|
|
9
|
-
*/
|
|
10
|
-
export declare function ContentEditor({ config: _config, onChange: _onChange, editor: _editor, }: EditorPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export declare function ContentEditor({ config, onChange, editor }: EditorPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
11
10
|
/**
|
|
12
11
|
* Editor module configuration.
|
|
13
12
|
*/
|
|
@@ -19,4 +18,10 @@ export declare const editor: {
|
|
|
19
18
|
};
|
|
20
19
|
component: typeof ContentEditor;
|
|
21
20
|
};
|
|
21
|
+
export declare const editorPanel: {
|
|
22
|
+
title: string;
|
|
23
|
+
icon: string;
|
|
24
|
+
description: string;
|
|
25
|
+
};
|
|
26
|
+
export default ContentEditor;
|
|
22
27
|
//# sourceMappingURL=editor.d.ts.map
|
package/dist/editor.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../src/editor.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../src/editor.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAqXhD,wBAAgB,aAAa,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,gBAAgB,2CAsa3E;AAED;;GAEG;AACH,eAAO,MAAM,MAAM;;;;;;;CAOlB,CAAC;AAEF,eAAO,MAAM,WAAW;;;;CAAe,CAAC;AAExC,eAAe,aAAa,CAAC"}
|
package/dist/editor.js
CHANGED
|
@@ -1,9 +1,510 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
|
-
* Content
|
|
3
|
+
* Adaptive Content - Editor Component
|
|
4
|
+
*
|
|
5
|
+
* Review & tweak editor for AI-generated content modifications.
|
|
6
|
+
* Displays a scannable list of one-liner change summaries.
|
|
7
|
+
* Clicking a card navigates to the element and shows a floating edit panel.
|
|
4
8
|
*/
|
|
5
|
-
|
|
6
|
-
|
|
9
|
+
import { useState, useCallback, useEffect, useRef } from 'react';
|
|
10
|
+
import { summarizeContentChange } from './summarize';
|
|
11
|
+
function itemKey(section, index) {
|
|
12
|
+
return `${section}:${index}`;
|
|
13
|
+
}
|
|
14
|
+
function parseItemKey(key) {
|
|
15
|
+
const [section, indexStr] = key.split(':');
|
|
16
|
+
return { section: section, index: Number(indexStr) };
|
|
17
|
+
}
|
|
18
|
+
// ============================================================================
|
|
19
|
+
// Section Config
|
|
20
|
+
// ============================================================================
|
|
21
|
+
const SECTION_ICONS = {
|
|
22
|
+
textReplacements: '\u{1f4dd}',
|
|
23
|
+
attributeChanges: '\u{1f3f7}\u{fe0f}',
|
|
24
|
+
styleChanges: '\u{1f3a8}',
|
|
25
|
+
htmlInsertions: '\u{1f4c4}',
|
|
26
|
+
classAdditions: '\u{2795}',
|
|
27
|
+
classRemovals: '\u{2796}',
|
|
28
|
+
};
|
|
29
|
+
// ============================================================================
|
|
30
|
+
// Styles
|
|
31
|
+
// ============================================================================
|
|
32
|
+
const styles = {
|
|
33
|
+
container: {
|
|
34
|
+
display: 'flex',
|
|
35
|
+
flexDirection: 'column',
|
|
36
|
+
height: '100%',
|
|
37
|
+
fontFamily: 'system-ui, -apple-system, sans-serif',
|
|
38
|
+
},
|
|
39
|
+
header: {
|
|
40
|
+
padding: '16px',
|
|
41
|
+
borderBottom: '1px solid #334155',
|
|
42
|
+
display: 'flex',
|
|
43
|
+
alignItems: 'center',
|
|
44
|
+
gap: '12px',
|
|
45
|
+
},
|
|
46
|
+
backButton: {
|
|
47
|
+
padding: '6px 12px',
|
|
48
|
+
borderRadius: '6px',
|
|
49
|
+
border: 'none',
|
|
50
|
+
backgroundColor: 'rgba(255,255,255,0.05)',
|
|
51
|
+
color: '#94a3b8',
|
|
52
|
+
fontSize: '13px',
|
|
53
|
+
cursor: 'pointer',
|
|
54
|
+
},
|
|
55
|
+
title: {
|
|
56
|
+
margin: 0,
|
|
57
|
+
fontSize: '15px',
|
|
58
|
+
fontWeight: 600,
|
|
59
|
+
color: '#f8fafc',
|
|
60
|
+
},
|
|
61
|
+
subtitle: {
|
|
62
|
+
margin: '2px 0 0 0',
|
|
63
|
+
fontSize: '11px',
|
|
64
|
+
color: '#64748b',
|
|
65
|
+
},
|
|
66
|
+
body: {
|
|
67
|
+
flex: 1,
|
|
68
|
+
overflow: 'auto',
|
|
69
|
+
padding: '16px',
|
|
70
|
+
},
|
|
71
|
+
groupHeader: {
|
|
72
|
+
fontSize: '11px',
|
|
73
|
+
fontWeight: 700,
|
|
74
|
+
color: '#64748b',
|
|
75
|
+
textTransform: 'uppercase',
|
|
76
|
+
letterSpacing: '0.06em',
|
|
77
|
+
padding: '4px 0 8px 0',
|
|
78
|
+
display: 'flex',
|
|
79
|
+
alignItems: 'center',
|
|
80
|
+
justifyContent: 'space-between',
|
|
81
|
+
},
|
|
82
|
+
groupCount: {
|
|
83
|
+
fontSize: '10px',
|
|
84
|
+
color: '#475569',
|
|
85
|
+
backgroundColor: 'rgba(255,255,255,0.06)',
|
|
86
|
+
padding: '2px 6px',
|
|
87
|
+
borderRadius: '8px',
|
|
88
|
+
},
|
|
89
|
+
card: {
|
|
90
|
+
display: 'flex',
|
|
91
|
+
alignItems: 'center',
|
|
92
|
+
gap: '8px',
|
|
93
|
+
padding: '8px 10px',
|
|
94
|
+
borderRadius: '6px',
|
|
95
|
+
border: '1px solid rgba(255,255,255,0.06)',
|
|
96
|
+
background: 'rgba(255,255,255,0.02)',
|
|
97
|
+
marginBottom: '4px',
|
|
98
|
+
cursor: 'pointer',
|
|
99
|
+
fontSize: '13px',
|
|
100
|
+
color: '#e2e8f0',
|
|
101
|
+
},
|
|
102
|
+
cardIcon: {
|
|
103
|
+
fontSize: '13px',
|
|
104
|
+
flexShrink: 0,
|
|
105
|
+
},
|
|
106
|
+
cardText: {
|
|
107
|
+
flex: 1,
|
|
108
|
+
overflow: 'hidden',
|
|
109
|
+
textOverflow: 'ellipsis',
|
|
110
|
+
whiteSpace: 'nowrap',
|
|
111
|
+
},
|
|
112
|
+
dismissButton: {
|
|
113
|
+
padding: '2px 6px',
|
|
114
|
+
borderRadius: '4px',
|
|
115
|
+
border: 'none',
|
|
116
|
+
background: 'transparent',
|
|
117
|
+
color: '#64748b',
|
|
118
|
+
fontSize: '14px',
|
|
119
|
+
cursor: 'pointer',
|
|
120
|
+
flexShrink: 0,
|
|
121
|
+
lineHeight: 1,
|
|
122
|
+
},
|
|
123
|
+
dismissedSection: {
|
|
124
|
+
marginTop: '16px',
|
|
125
|
+
cursor: 'pointer',
|
|
126
|
+
userSelect: 'none',
|
|
127
|
+
},
|
|
128
|
+
dismissedHeader: {
|
|
129
|
+
fontSize: '11px',
|
|
130
|
+
fontWeight: 600,
|
|
131
|
+
color: '#475569',
|
|
132
|
+
display: 'flex',
|
|
133
|
+
alignItems: 'center',
|
|
134
|
+
gap: '6px',
|
|
135
|
+
},
|
|
136
|
+
dismissedCard: {
|
|
137
|
+
display: 'flex',
|
|
138
|
+
alignItems: 'center',
|
|
139
|
+
gap: '8px',
|
|
140
|
+
padding: '6px 10px',
|
|
141
|
+
borderRadius: '6px',
|
|
142
|
+
border: '1px solid rgba(255,255,255,0.03)',
|
|
143
|
+
background: 'transparent',
|
|
144
|
+
marginBottom: '3px',
|
|
145
|
+
cursor: 'pointer',
|
|
146
|
+
fontSize: '12px',
|
|
147
|
+
color: '#475569',
|
|
148
|
+
opacity: 0.6,
|
|
149
|
+
},
|
|
150
|
+
emptyState: {
|
|
151
|
+
textAlign: 'center',
|
|
152
|
+
padding: '32px 16px',
|
|
153
|
+
color: '#64748b',
|
|
154
|
+
fontSize: '13px',
|
|
155
|
+
},
|
|
156
|
+
footer: {
|
|
157
|
+
padding: '12px 16px',
|
|
158
|
+
borderTop: '1px solid #334155',
|
|
159
|
+
display: 'flex',
|
|
160
|
+
gap: '8px',
|
|
161
|
+
},
|
|
162
|
+
saveButton: {
|
|
163
|
+
flex: 1,
|
|
164
|
+
padding: '10px',
|
|
165
|
+
borderRadius: '8px',
|
|
166
|
+
border: 'none',
|
|
167
|
+
background: 'rgba(59, 130, 246, 0.15)',
|
|
168
|
+
color: '#3b82f6',
|
|
169
|
+
fontSize: '13px',
|
|
170
|
+
fontWeight: 600,
|
|
171
|
+
cursor: 'pointer',
|
|
172
|
+
},
|
|
173
|
+
publishButton: {
|
|
174
|
+
flex: 1,
|
|
175
|
+
padding: '10px',
|
|
176
|
+
borderRadius: '8px',
|
|
177
|
+
border: 'none',
|
|
178
|
+
background: '#22c55e',
|
|
179
|
+
color: 'white',
|
|
180
|
+
fontSize: '13px',
|
|
181
|
+
fontWeight: 600,
|
|
182
|
+
cursor: 'pointer',
|
|
183
|
+
},
|
|
184
|
+
// Edit form styles
|
|
185
|
+
editForm: {
|
|
186
|
+
padding: '4px 0',
|
|
187
|
+
},
|
|
188
|
+
editHeader: {
|
|
189
|
+
display: 'flex',
|
|
190
|
+
alignItems: 'center',
|
|
191
|
+
gap: '8px',
|
|
192
|
+
marginBottom: '12px',
|
|
193
|
+
fontSize: '13px',
|
|
194
|
+
fontWeight: 600,
|
|
195
|
+
color: '#e2e8f0',
|
|
196
|
+
},
|
|
197
|
+
editTarget: {
|
|
198
|
+
fontSize: '11px',
|
|
199
|
+
fontFamily: 'monospace',
|
|
200
|
+
color: '#94a3b8',
|
|
201
|
+
padding: '4px 8px',
|
|
202
|
+
background: 'rgba(255,255,255,0.04)',
|
|
203
|
+
borderRadius: '4px',
|
|
204
|
+
marginBottom: '12px',
|
|
205
|
+
},
|
|
206
|
+
editLabel: {
|
|
207
|
+
fontSize: '11px',
|
|
208
|
+
fontWeight: 600,
|
|
209
|
+
color: '#64748b',
|
|
210
|
+
marginBottom: '4px',
|
|
211
|
+
display: 'block',
|
|
212
|
+
},
|
|
213
|
+
editInput: {
|
|
214
|
+
width: '100%',
|
|
215
|
+
padding: '6px 8px',
|
|
216
|
+
borderRadius: '4px',
|
|
217
|
+
border: '1px solid rgba(255,255,255,0.1)',
|
|
218
|
+
background: 'rgba(255,255,255,0.04)',
|
|
219
|
+
color: '#e2e8f0',
|
|
220
|
+
fontSize: '12px',
|
|
221
|
+
fontFamily: 'inherit',
|
|
222
|
+
marginBottom: '8px',
|
|
223
|
+
boxSizing: 'border-box',
|
|
224
|
+
},
|
|
225
|
+
editTextarea: {
|
|
226
|
+
width: '100%',
|
|
227
|
+
padding: '6px 8px',
|
|
228
|
+
borderRadius: '4px',
|
|
229
|
+
border: '1px solid rgba(255,255,255,0.1)',
|
|
230
|
+
background: 'rgba(255,255,255,0.04)',
|
|
231
|
+
color: '#e2e8f0',
|
|
232
|
+
fontSize: '12px',
|
|
233
|
+
fontFamily: 'inherit',
|
|
234
|
+
marginBottom: '8px',
|
|
235
|
+
resize: 'vertical',
|
|
236
|
+
minHeight: '60px',
|
|
237
|
+
boxSizing: 'border-box',
|
|
238
|
+
},
|
|
239
|
+
editSelect: {
|
|
240
|
+
width: '100%',
|
|
241
|
+
padding: '6px 8px',
|
|
242
|
+
borderRadius: '4px',
|
|
243
|
+
border: '1px solid rgba(255,255,255,0.1)',
|
|
244
|
+
background: 'rgba(255,255,255,0.04)',
|
|
245
|
+
color: '#e2e8f0',
|
|
246
|
+
fontSize: '12px',
|
|
247
|
+
marginBottom: '8px',
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
// ============================================================================
|
|
251
|
+
// Helpers
|
|
252
|
+
// ============================================================================
|
|
253
|
+
/** Build a flat list of all items across all section types. */
|
|
254
|
+
function flattenItems(config) {
|
|
255
|
+
const items = [];
|
|
256
|
+
const sections = Object.keys(SECTION_ICONS);
|
|
257
|
+
for (const section of sections) {
|
|
258
|
+
const arr = config[section] || [];
|
|
259
|
+
arr.forEach((item, i) => {
|
|
260
|
+
const rec = item;
|
|
261
|
+
items.push({
|
|
262
|
+
key: itemKey(section, i),
|
|
263
|
+
section,
|
|
264
|
+
index: i,
|
|
265
|
+
icon: SECTION_ICONS[section],
|
|
266
|
+
summary: summarizeContentChange(section, rec),
|
|
267
|
+
anchorId: rec.anchorId || '',
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
return items;
|
|
272
|
+
}
|
|
273
|
+
/** Remove items by key set from a config, returning a new config. */
|
|
274
|
+
function filterConfig(config, dismissedKeys) {
|
|
275
|
+
const result = { ...config };
|
|
276
|
+
const sections = Object.keys(SECTION_ICONS);
|
|
277
|
+
for (const section of sections) {
|
|
278
|
+
const arr = config[section] || [];
|
|
279
|
+
const filtered = arr.filter((_, i) => !dismissedKeys.has(itemKey(section, i)));
|
|
280
|
+
if (filtered.length > 0 || config[section] !== undefined) {
|
|
281
|
+
result[section] = filtered;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return result;
|
|
285
|
+
}
|
|
286
|
+
function useAnchorDetection(items) {
|
|
287
|
+
const [detectionMap, setDetectionMap] = useState(new Map());
|
|
288
|
+
const itemsRef = useRef(items);
|
|
289
|
+
itemsRef.current = items;
|
|
290
|
+
useEffect(() => {
|
|
291
|
+
const runDetection = () => {
|
|
292
|
+
const map = new Map();
|
|
293
|
+
for (const item of itemsRef.current) {
|
|
294
|
+
if (!item.anchorId) {
|
|
295
|
+
map.set(item.key, { found: false, element: null });
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
try {
|
|
299
|
+
const el = document.querySelector(item.anchorId);
|
|
300
|
+
map.set(item.key, { found: el !== null, element: el });
|
|
301
|
+
}
|
|
302
|
+
catch {
|
|
303
|
+
map.set(item.key, { found: false, element: null });
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
setDetectionMap(map);
|
|
307
|
+
};
|
|
308
|
+
runDetection();
|
|
309
|
+
const interval = setInterval(runDetection, 2000);
|
|
310
|
+
return () => clearInterval(interval);
|
|
311
|
+
}, []);
|
|
312
|
+
return detectionMap;
|
|
313
|
+
}
|
|
314
|
+
function DetectionBadge({ found }) {
|
|
315
|
+
return (_jsx("span", { style: {
|
|
316
|
+
width: '8px',
|
|
317
|
+
height: '8px',
|
|
318
|
+
borderRadius: '50%',
|
|
319
|
+
backgroundColor: found ? '#22c55e' : '#475569',
|
|
320
|
+
flexShrink: 0,
|
|
321
|
+
display: 'inline-block',
|
|
322
|
+
}, title: found ? 'Found on this page' : 'Not found on this page' }));
|
|
323
|
+
}
|
|
324
|
+
// ============================================================================
|
|
325
|
+
// ContentEditor Component
|
|
326
|
+
// ============================================================================
|
|
327
|
+
export function ContentEditor({ config, onChange, editor }) {
|
|
328
|
+
const typedConfig = config;
|
|
329
|
+
const [dismissedKeys, setDismissedKeys] = useState(new Set());
|
|
330
|
+
const [dismissedOpen, setDismissedOpen] = useState(false);
|
|
331
|
+
const [editingKey, setEditingKey] = useState(null);
|
|
332
|
+
const [previewMode, setPreviewMode] = useState('after');
|
|
333
|
+
// Consume initialEditKey from accordion navigation on mount
|
|
334
|
+
const initialConsumed = useRef(false);
|
|
335
|
+
useEffect(() => {
|
|
336
|
+
if (editor.initialEditKey != null && !initialConsumed.current) {
|
|
337
|
+
initialConsumed.current = true;
|
|
338
|
+
// Map flat action index to internal section:index key.
|
|
339
|
+
// The flat index refers to the position within this app's actions only
|
|
340
|
+
// (after actionsToAppConfig transformation), so we walk sections in order.
|
|
341
|
+
const allFlat = flattenItems(typedConfig);
|
|
342
|
+
const targetIdx = Number(editor.initialEditKey);
|
|
343
|
+
if (targetIdx >= 0 && targetIdx < allFlat.length) {
|
|
344
|
+
const target = allFlat[targetIdx];
|
|
345
|
+
setEditingKey(target.key);
|
|
346
|
+
if (target.anchorId) {
|
|
347
|
+
editor.highlightElement(target.anchorId);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
editor.clearInitialState?.();
|
|
351
|
+
}
|
|
352
|
+
else if (editor.initialCreate && !initialConsumed.current) {
|
|
353
|
+
initialConsumed.current = true;
|
|
354
|
+
editor.clearInitialState?.();
|
|
355
|
+
}
|
|
356
|
+
}, [editor, typedConfig]);
|
|
357
|
+
const allItems = flattenItems(typedConfig);
|
|
358
|
+
const activeItems = allItems.filter((item) => !dismissedKeys.has(item.key));
|
|
359
|
+
const dismissedItems = allItems.filter((item) => dismissedKeys.has(item.key));
|
|
360
|
+
const totalChanges = activeItems.length;
|
|
361
|
+
const [_hoveredKey, setHoveredKey] = useState(null);
|
|
362
|
+
const detectionMap = useAnchorDetection(allItems);
|
|
363
|
+
const foundCount = activeItems.filter((item) => detectionMap.get(item.key)?.found).length;
|
|
364
|
+
const handleDismiss = useCallback((key) => {
|
|
365
|
+
setDismissedKeys((prev) => {
|
|
366
|
+
const next = new Set(prev);
|
|
367
|
+
next.add(key);
|
|
368
|
+
return next;
|
|
369
|
+
});
|
|
370
|
+
if (editingKey === key)
|
|
371
|
+
setEditingKey(null);
|
|
372
|
+
}, [editingKey]);
|
|
373
|
+
const handleRestore = useCallback((key) => {
|
|
374
|
+
setDismissedKeys((prev) => {
|
|
375
|
+
const next = new Set(prev);
|
|
376
|
+
next.delete(key);
|
|
377
|
+
return next;
|
|
378
|
+
});
|
|
379
|
+
}, []);
|
|
380
|
+
const handleCardClick = useCallback((item) => {
|
|
381
|
+
if (item.anchorId) {
|
|
382
|
+
editor.highlightElement(item.anchorId);
|
|
383
|
+
}
|
|
384
|
+
setEditingKey(item.key);
|
|
385
|
+
}, [editor]);
|
|
386
|
+
const handleBackToList = useCallback(() => {
|
|
387
|
+
setEditingKey(null);
|
|
388
|
+
setPreviewMode('after');
|
|
389
|
+
editor.previewConfig(config);
|
|
390
|
+
editor.clearHighlight();
|
|
391
|
+
}, [editor, config]);
|
|
392
|
+
const handleBeforeAfter = useCallback((mode) => {
|
|
393
|
+
setPreviewMode(mode);
|
|
394
|
+
if (mode === 'before') {
|
|
395
|
+
const filtered = filterConfig(typedConfig, new Set([editingKey]));
|
|
396
|
+
editor.previewConfig(filtered);
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
editor.previewConfig(config);
|
|
400
|
+
}
|
|
401
|
+
}, [typedConfig, editingKey, editor, config]);
|
|
402
|
+
const handleFieldChange = useCallback((section, index, field, value) => {
|
|
403
|
+
const arr = (typedConfig[section] || []).slice();
|
|
404
|
+
const item = { ...arr[index] };
|
|
405
|
+
item[field] = value;
|
|
406
|
+
arr[index] = item;
|
|
407
|
+
const updated = { ...typedConfig, [section]: arr };
|
|
408
|
+
onChange(updated);
|
|
409
|
+
editor.setDirty(true);
|
|
410
|
+
}, [typedConfig, onChange, editor]);
|
|
411
|
+
const handlePublish = useCallback(() => {
|
|
412
|
+
// Filter dismissed items before publishing
|
|
413
|
+
if (dismissedKeys.size > 0) {
|
|
414
|
+
const filtered = filterConfig(typedConfig, dismissedKeys);
|
|
415
|
+
onChange(filtered);
|
|
416
|
+
}
|
|
417
|
+
editor.publish();
|
|
418
|
+
}, [dismissedKeys, typedConfig, onChange, editor]);
|
|
419
|
+
const handleCardHover = useCallback((item) => {
|
|
420
|
+
setHoveredKey(item.key);
|
|
421
|
+
if (item.anchorId) {
|
|
422
|
+
editor.highlightElement(item.anchorId);
|
|
423
|
+
}
|
|
424
|
+
}, [editor]);
|
|
425
|
+
const handleCardLeave = useCallback(() => {
|
|
426
|
+
setHoveredKey(null);
|
|
427
|
+
editor.clearHighlight();
|
|
428
|
+
}, [editor]);
|
|
429
|
+
// ---- Edit form renderers per section type ----
|
|
430
|
+
const renderEditFields = (section, index) => {
|
|
431
|
+
const arr = typedConfig[section] || [];
|
|
432
|
+
const item = arr[index];
|
|
433
|
+
if (!item)
|
|
434
|
+
return null;
|
|
435
|
+
const anchorId = item.anchorId || '';
|
|
436
|
+
switch (section) {
|
|
437
|
+
case 'textReplacements':
|
|
438
|
+
return (_jsxs("div", { style: styles.editForm, children: [_jsx("div", { style: styles.editTarget, children: anchorId }), _jsx("label", { style: styles.editLabel, children: "Text" }), _jsx("textarea", { style: styles.editTextarea, value: item.text || '', onChange: (e) => handleFieldChange(section, index, 'text', e.target.value) })] }));
|
|
439
|
+
case 'attributeChanges':
|
|
440
|
+
return (_jsxs("div", { style: styles.editForm, children: [_jsx("div", { style: styles.editTarget, children: anchorId }), _jsx("label", { style: styles.editLabel, children: "Attribute" }), _jsx("input", { style: styles.editInput, value: item.attr || '', onChange: (e) => handleFieldChange(section, index, 'attr', e.target.value) }), _jsx("label", { style: styles.editLabel, children: "Value" }), _jsx("input", { style: styles.editInput, value: item.value || '', onChange: (e) => handleFieldChange(section, index, 'value', e.target.value) })] }));
|
|
441
|
+
case 'styleChanges': {
|
|
442
|
+
const styleObj = item.styles || {};
|
|
443
|
+
return (_jsxs("div", { style: styles.editForm, children: [_jsx("div", { style: styles.editTarget, children: anchorId }), _jsx("label", { style: styles.editLabel, children: "Styles" }), Object.entries(styleObj).map(([prop, val]) => (_jsxs("div", { style: { display: 'flex', gap: '4px', marginBottom: '4px' }, children: [_jsx("input", { style: { ...styles.editInput, flex: 1, marginBottom: 0 }, value: prop, readOnly: true }), _jsx("input", { style: { ...styles.editInput, flex: 1, marginBottom: 0 }, value: val, onChange: (e) => {
|
|
444
|
+
const newStyles = { ...styleObj, [prop]: e.target.value };
|
|
445
|
+
handleFieldChange(section, index, 'styles', newStyles);
|
|
446
|
+
} })] }, prop)))] }));
|
|
447
|
+
}
|
|
448
|
+
case 'htmlInsertions':
|
|
449
|
+
return (_jsxs("div", { style: styles.editForm, children: [_jsx("div", { style: styles.editTarget, children: anchorId }), _jsx("label", { style: styles.editLabel, children: "Position" }), _jsxs("select", { style: styles.editSelect, value: item.position || 'after', onChange: (e) => handleFieldChange(section, index, 'position', e.target.value), children: [_jsx("option", { value: "before", children: "Before" }), _jsx("option", { value: "after", children: "After" }), _jsx("option", { value: "prepend", children: "Prepend" }), _jsx("option", { value: "append", children: "Append" }), _jsx("option", { value: "replace", children: "Replace" })] }), _jsx("label", { style: styles.editLabel, children: "HTML" }), _jsx("textarea", { style: { ...styles.editTextarea, fontFamily: 'monospace' }, value: item.html || '', onChange: (e) => handleFieldChange(section, index, 'html', e.target.value) })] }));
|
|
450
|
+
case 'classAdditions':
|
|
451
|
+
case 'classRemovals':
|
|
452
|
+
return (_jsxs("div", { style: styles.editForm, children: [_jsx("div", { style: styles.editTarget, children: anchorId }), _jsx("label", { style: styles.editLabel, children: "Class Name" }), _jsx("input", { style: styles.editInput, value: item.className || '', onChange: (e) => handleFieldChange(section, index, 'className', e.target.value) })] }));
|
|
453
|
+
default:
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
};
|
|
457
|
+
return (_jsxs("div", { style: styles.container, children: [_jsxs("div", { style: styles.header, children: [_jsx("button", { onClick: () => (editingKey !== null ? handleBackToList() : editor.navigateHome()), style: styles.backButton, children: "\u2190 Back" }), _jsxs("div", { children: [_jsx("h2", { style: styles.title, children: "Review Changes" }), _jsxs("p", { style: styles.subtitle, children: [totalChanges, " change", totalChanges !== 1 ? 's' : '', totalChanges > 0 && ` (${foundCount} found on this page)`] })] })] }), _jsx("div", { style: styles.body, children: editingKey !== null ? (
|
|
458
|
+
/* ---- Edit mode ---- */
|
|
459
|
+
(() => {
|
|
460
|
+
const ref = parseItemKey(editingKey);
|
|
461
|
+
const editItem = allItems.find((it) => it.key === editingKey);
|
|
462
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { style: styles.editHeader, children: [_jsx("span", { children: editItem?.icon }), _jsx("span", { children: editItem?.summary })] }), _jsxs("div", { style: {
|
|
463
|
+
display: 'flex',
|
|
464
|
+
gap: '0',
|
|
465
|
+
marginBottom: '12px',
|
|
466
|
+
borderRadius: '6px',
|
|
467
|
+
overflow: 'hidden',
|
|
468
|
+
border: '1px solid rgba(255,255,255,0.1)',
|
|
469
|
+
}, children: [_jsx("button", { onClick: () => handleBeforeAfter('before'), style: {
|
|
470
|
+
flex: 1,
|
|
471
|
+
padding: '6px 12px',
|
|
472
|
+
border: 'none',
|
|
473
|
+
fontSize: '12px',
|
|
474
|
+
fontWeight: 600,
|
|
475
|
+
cursor: 'pointer',
|
|
476
|
+
background: previewMode === 'before' ? 'rgba(59,130,246,0.2)' : 'transparent',
|
|
477
|
+
color: previewMode === 'before' ? '#3b82f6' : '#64748b',
|
|
478
|
+
}, children: "Before" }), _jsx("button", { onClick: () => handleBeforeAfter('after'), style: {
|
|
479
|
+
flex: 1,
|
|
480
|
+
padding: '6px 12px',
|
|
481
|
+
border: 'none',
|
|
482
|
+
borderLeft: '1px solid rgba(255,255,255,0.1)',
|
|
483
|
+
fontSize: '12px',
|
|
484
|
+
fontWeight: 600,
|
|
485
|
+
cursor: 'pointer',
|
|
486
|
+
background: previewMode === 'after' ? 'rgba(59,130,246,0.2)' : 'transparent',
|
|
487
|
+
color: previewMode === 'after' ? '#3b82f6' : '#64748b',
|
|
488
|
+
}, children: "After" })] }), renderEditFields(ref.section, ref.index)] }));
|
|
489
|
+
})()) : (
|
|
490
|
+
/* ---- List mode ---- */
|
|
491
|
+
_jsxs(_Fragment, { children: [allItems.length === 0 && (_jsx("div", { style: styles.emptyState, children: "No content changes configured." })), activeItems.length > 0 && (_jsxs(_Fragment, { children: [_jsxs("div", { style: styles.groupHeader, children: [_jsx("span", { children: "CONTENT" }), _jsx("span", { style: styles.groupCount, children: activeItems.length })] }), activeItems.map((item) => {
|
|
492
|
+
const detection = detectionMap.get(item.key);
|
|
493
|
+
return (_jsxs("div", { style: styles.card, "data-item-key": item.key, onMouseEnter: () => handleCardHover(item), onMouseLeave: handleCardLeave, children: [_jsx(DetectionBadge, { found: detection?.found ?? false }), _jsx("span", { style: styles.cardIcon, onClick: (e) => {
|
|
494
|
+
e.stopPropagation();
|
|
495
|
+
handleCardClick(item);
|
|
496
|
+
}, children: item.icon }), _jsx("span", { style: styles.cardText, onClick: () => handleCardClick(item), children: item.summary }), _jsx("button", { style: styles.dismissButton, onClick: (e) => {
|
|
497
|
+
e.stopPropagation();
|
|
498
|
+
handleDismiss(item.key);
|
|
499
|
+
}, title: "Dismiss this change", children: "\u00D7" })] }, item.key));
|
|
500
|
+
})] })), dismissedItems.length > 0 && (_jsxs("div", { style: styles.dismissedSection, children: [_jsxs("div", { style: { ...styles.dismissedHeader, cursor: 'pointer' }, onClick: () => setDismissedOpen(!dismissedOpen), children: [_jsx("span", { children: dismissedOpen ? '\u25be' : '\u25b8' }), _jsxs("span", { children: ["Dismissed (", dismissedItems.length, ")"] })] }), dismissedOpen && (_jsx("div", { style: { marginTop: '6px' }, children: dismissedItems.map((item) => (_jsxs("div", { style: styles.dismissedCard, children: [_jsx("span", { style: styles.cardIcon, children: item.icon }), _jsx("span", { style: { ...styles.cardText, textDecoration: 'line-through' }, children: item.summary }), _jsx("button", { style: {
|
|
501
|
+
...styles.dismissButton,
|
|
502
|
+
color: '#3b82f6',
|
|
503
|
+
fontSize: '11px',
|
|
504
|
+
}, onClick: (e) => {
|
|
505
|
+
e.stopPropagation();
|
|
506
|
+
handleRestore(item.key);
|
|
507
|
+
}, children: "Restore" })] }, item.key))) }))] }))] })) }), _jsxs("div", { style: styles.footer, children: [_jsx("button", { onClick: () => editor.save(), style: styles.saveButton, children: "Save Draft" }), _jsx("button", { onClick: handlePublish, style: styles.publishButton, children: "Publish" })] })] }));
|
|
7
508
|
}
|
|
8
509
|
/**
|
|
9
510
|
* Editor module configuration.
|
|
@@ -11,8 +512,10 @@ export function ContentEditor({ config: _config, onChange: _onChange, editor: _e
|
|
|
11
512
|
export const editor = {
|
|
12
513
|
panel: {
|
|
13
514
|
title: 'Content',
|
|
14
|
-
icon: '
|
|
515
|
+
icon: '\u{1f4dd}',
|
|
15
516
|
description: 'Text and attribute modifications',
|
|
16
517
|
},
|
|
17
518
|
component: ContentEditor,
|
|
18
519
|
};
|
|
520
|
+
export const editorPanel = editor.panel;
|
|
521
|
+
export default ContentEditor;
|
package/dist/runtime.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EACV,gBAAgB,EAChB,aAAa,EACb,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,cAAc,EAEd,cAAc,EACf,MAAM,SAAS,CAAC;AAMjB;;GAEG;AACH,eAAO,MAAM,iBAAiB,EAAE,cAAc,CAAC,gBAAgB,CAmE9D,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,cAAc,CAAC,aAAa,CA+BxD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,cAAc,CAAC,aAAa,CA2CxD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,cAAc,CAAC,cAAc,CA8B1D,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kBAAkB,EAAE,cAAc,CAAC,iBAAiB,CA8BhE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,cAAc,CAAC,cAAc,CA+C1D,CAAC;AAMF;;;GAGG;AACH,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;EAOZ,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;CAMnB,CAAC"}
|
package/dist/schema.d.ts
CHANGED
|
@@ -12,127 +12,157 @@ export declare const configSchema: z.ZodObject<{
|
|
|
12
12
|
/** Text replacements */
|
|
13
13
|
textReplacements: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
14
14
|
anchorId: z.ZodString;
|
|
15
|
+
summary: z.ZodString;
|
|
15
16
|
text: z.ZodString;
|
|
16
17
|
}, "strip", z.ZodTypeAny, {
|
|
17
18
|
anchorId: string;
|
|
19
|
+
summary: string;
|
|
18
20
|
text: string;
|
|
19
21
|
}, {
|
|
20
22
|
anchorId: string;
|
|
23
|
+
summary: string;
|
|
21
24
|
text: string;
|
|
22
25
|
}>, "many">>;
|
|
23
26
|
/** Attribute modifications */
|
|
24
27
|
attributeChanges: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
25
28
|
anchorId: z.ZodString;
|
|
29
|
+
summary: z.ZodString;
|
|
26
30
|
attr: z.ZodString;
|
|
27
31
|
value: z.ZodString;
|
|
28
32
|
}, "strip", z.ZodTypeAny, {
|
|
33
|
+
value: string;
|
|
29
34
|
anchorId: string;
|
|
35
|
+
summary: string;
|
|
30
36
|
attr: string;
|
|
31
|
-
value: string;
|
|
32
37
|
}, {
|
|
38
|
+
value: string;
|
|
33
39
|
anchorId: string;
|
|
40
|
+
summary: string;
|
|
34
41
|
attr: string;
|
|
35
|
-
value: string;
|
|
36
42
|
}>, "many">>;
|
|
37
43
|
/** Style modifications */
|
|
38
44
|
styleChanges: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
39
45
|
anchorId: z.ZodString;
|
|
46
|
+
summary: z.ZodString;
|
|
40
47
|
styles: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
41
48
|
}, "strip", z.ZodTypeAny, {
|
|
42
49
|
anchorId: string;
|
|
50
|
+
summary: string;
|
|
43
51
|
styles: Record<string, string>;
|
|
44
52
|
}, {
|
|
45
53
|
anchorId: string;
|
|
54
|
+
summary: string;
|
|
46
55
|
styles: Record<string, string>;
|
|
47
56
|
}>, "many">>;
|
|
48
57
|
/** HTML insertions */
|
|
49
58
|
htmlInsertions: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
50
59
|
anchorId: z.ZodString;
|
|
60
|
+
summary: z.ZodString;
|
|
51
61
|
html: z.ZodString;
|
|
52
62
|
position: z.ZodEnum<["before", "after", "prepend", "append", "replace"]>;
|
|
53
63
|
}, "strip", z.ZodTypeAny, {
|
|
54
64
|
anchorId: string;
|
|
65
|
+
summary: string;
|
|
55
66
|
html: string;
|
|
56
67
|
position: "before" | "after" | "prepend" | "append" | "replace";
|
|
57
68
|
}, {
|
|
58
69
|
anchorId: string;
|
|
70
|
+
summary: string;
|
|
59
71
|
html: string;
|
|
60
72
|
position: "before" | "after" | "prepend" | "append" | "replace";
|
|
61
73
|
}>, "many">>;
|
|
62
74
|
/** Class additions */
|
|
63
75
|
classAdditions: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
64
76
|
anchorId: z.ZodString;
|
|
77
|
+
summary: z.ZodString;
|
|
65
78
|
className: z.ZodString;
|
|
66
79
|
}, "strip", z.ZodTypeAny, {
|
|
67
80
|
anchorId: string;
|
|
81
|
+
summary: string;
|
|
68
82
|
className: string;
|
|
69
83
|
}, {
|
|
70
84
|
anchorId: string;
|
|
85
|
+
summary: string;
|
|
71
86
|
className: string;
|
|
72
87
|
}>, "many">>;
|
|
73
88
|
/** Class removals */
|
|
74
89
|
classRemovals: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
75
90
|
anchorId: z.ZodString;
|
|
91
|
+
summary: z.ZodString;
|
|
76
92
|
className: z.ZodString;
|
|
77
93
|
}, "strip", z.ZodTypeAny, {
|
|
78
94
|
anchorId: string;
|
|
95
|
+
summary: string;
|
|
79
96
|
className: string;
|
|
80
97
|
}, {
|
|
81
98
|
anchorId: string;
|
|
99
|
+
summary: string;
|
|
82
100
|
className: string;
|
|
83
101
|
}>, "many">>;
|
|
84
102
|
}, "strip", z.ZodTypeAny, {
|
|
85
103
|
textReplacements?: {
|
|
86
104
|
anchorId: string;
|
|
105
|
+
summary: string;
|
|
87
106
|
text: string;
|
|
88
107
|
}[] | undefined;
|
|
89
108
|
attributeChanges?: {
|
|
109
|
+
value: string;
|
|
90
110
|
anchorId: string;
|
|
111
|
+
summary: string;
|
|
91
112
|
attr: string;
|
|
92
|
-
value: string;
|
|
93
113
|
}[] | undefined;
|
|
94
114
|
styleChanges?: {
|
|
95
115
|
anchorId: string;
|
|
116
|
+
summary: string;
|
|
96
117
|
styles: Record<string, string>;
|
|
97
118
|
}[] | undefined;
|
|
98
119
|
htmlInsertions?: {
|
|
99
120
|
anchorId: string;
|
|
121
|
+
summary: string;
|
|
100
122
|
html: string;
|
|
101
123
|
position: "before" | "after" | "prepend" | "append" | "replace";
|
|
102
124
|
}[] | undefined;
|
|
103
125
|
classAdditions?: {
|
|
104
126
|
anchorId: string;
|
|
127
|
+
summary: string;
|
|
105
128
|
className: string;
|
|
106
129
|
}[] | undefined;
|
|
107
130
|
classRemovals?: {
|
|
108
131
|
anchorId: string;
|
|
132
|
+
summary: string;
|
|
109
133
|
className: string;
|
|
110
134
|
}[] | undefined;
|
|
111
135
|
}, {
|
|
112
136
|
textReplacements?: {
|
|
113
137
|
anchorId: string;
|
|
138
|
+
summary: string;
|
|
114
139
|
text: string;
|
|
115
140
|
}[] | undefined;
|
|
116
141
|
attributeChanges?: {
|
|
142
|
+
value: string;
|
|
117
143
|
anchorId: string;
|
|
144
|
+
summary: string;
|
|
118
145
|
attr: string;
|
|
119
|
-
value: string;
|
|
120
146
|
}[] | undefined;
|
|
121
147
|
styleChanges?: {
|
|
122
148
|
anchorId: string;
|
|
149
|
+
summary: string;
|
|
123
150
|
styles: Record<string, string>;
|
|
124
151
|
}[] | undefined;
|
|
125
152
|
htmlInsertions?: {
|
|
126
153
|
anchorId: string;
|
|
154
|
+
summary: string;
|
|
127
155
|
html: string;
|
|
128
156
|
position: "before" | "after" | "prepend" | "append" | "replace";
|
|
129
157
|
}[] | undefined;
|
|
130
158
|
classAdditions?: {
|
|
131
159
|
anchorId: string;
|
|
160
|
+
summary: string;
|
|
132
161
|
className: string;
|
|
133
162
|
}[] | undefined;
|
|
134
163
|
classRemovals?: {
|
|
135
164
|
anchorId: string;
|
|
165
|
+
summary: string;
|
|
136
166
|
className: string;
|
|
137
167
|
}[] | undefined;
|
|
138
168
|
}>;
|
package/dist/schema.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;GAGG;AACH,eAAO,MAAM,YAAY;IACvB,wBAAwB
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;GAGG;AACH,eAAO,MAAM,YAAY;IACvB,wBAAwB;;;;;;;;;;;;;;IAWxB,8BAA8B;;;;;;;;;;;;;;;;;IAY9B,0BAA0B;;;;;;;;;;;;;;IAW1B,sBAAsB;;;;;;;;;;;;;;;;;IAYtB,sBAAsB;;;;;;;;;;;;;;IAWtB,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAUrB,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC"}
|
package/dist/schema.js
CHANGED
|
@@ -13,6 +13,7 @@ export const configSchema = z.object({
|
|
|
13
13
|
textReplacements: z
|
|
14
14
|
.array(z.object({
|
|
15
15
|
anchorId: z.string(),
|
|
16
|
+
summary: z.string(),
|
|
16
17
|
text: z.string(),
|
|
17
18
|
}))
|
|
18
19
|
.optional(),
|
|
@@ -20,6 +21,7 @@ export const configSchema = z.object({
|
|
|
20
21
|
attributeChanges: z
|
|
21
22
|
.array(z.object({
|
|
22
23
|
anchorId: z.string(),
|
|
24
|
+
summary: z.string(),
|
|
23
25
|
attr: z.string(),
|
|
24
26
|
value: z.string(),
|
|
25
27
|
}))
|
|
@@ -28,6 +30,7 @@ export const configSchema = z.object({
|
|
|
28
30
|
styleChanges: z
|
|
29
31
|
.array(z.object({
|
|
30
32
|
anchorId: z.string(),
|
|
33
|
+
summary: z.string(),
|
|
31
34
|
styles: z.record(z.string()),
|
|
32
35
|
}))
|
|
33
36
|
.optional(),
|
|
@@ -35,6 +38,7 @@ export const configSchema = z.object({
|
|
|
35
38
|
htmlInsertions: z
|
|
36
39
|
.array(z.object({
|
|
37
40
|
anchorId: z.string(),
|
|
41
|
+
summary: z.string(),
|
|
38
42
|
html: z.string(),
|
|
39
43
|
position: z.enum(['before', 'after', 'prepend', 'append', 'replace']),
|
|
40
44
|
}))
|
|
@@ -43,6 +47,7 @@ export const configSchema = z.object({
|
|
|
43
47
|
classAdditions: z
|
|
44
48
|
.array(z.object({
|
|
45
49
|
anchorId: z.string(),
|
|
50
|
+
summary: z.string(),
|
|
46
51
|
className: z.string(),
|
|
47
52
|
}))
|
|
48
53
|
.optional(),
|
|
@@ -50,6 +55,7 @@ export const configSchema = z.object({
|
|
|
50
55
|
classRemovals: z
|
|
51
56
|
.array(z.object({
|
|
52
57
|
anchorId: z.string(),
|
|
58
|
+
summary: z.string(),
|
|
53
59
|
className: z.string(),
|
|
54
60
|
}))
|
|
55
61
|
.optional(),
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Human-readable summary generation for content config changes.
|
|
3
|
+
*
|
|
4
|
+
* Pure functions — no DOM access, just string formatting from config data.
|
|
5
|
+
*/
|
|
6
|
+
import type { ContentConfig } from './schema';
|
|
7
|
+
/**
|
|
8
|
+
* Convert a CSS selector into a human-friendly element description.
|
|
9
|
+
*/
|
|
10
|
+
export declare function describeSelector(selector: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* Generate a human-readable one-liner for a content config change.
|
|
13
|
+
*/
|
|
14
|
+
export declare function summarizeContentChange(type: keyof ContentConfig, item: Record<string, unknown>): string;
|
|
15
|
+
//# sourceMappingURL=summarize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summarize.d.ts","sourceRoot":"","sources":["../src/summarize.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAI9C;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAwBzD;AAYD;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,aAAa,EACzB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,MAAM,CAiCR"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Human-readable summary generation for content config changes.
|
|
3
|
+
*
|
|
4
|
+
* Pure functions — no DOM access, just string formatting from config data.
|
|
5
|
+
*/
|
|
6
|
+
const MAX_TEXT_LEN = 40;
|
|
7
|
+
/**
|
|
8
|
+
* Convert a CSS selector into a human-friendly element description.
|
|
9
|
+
*/
|
|
10
|
+
export function describeSelector(selector) {
|
|
11
|
+
if (!selector)
|
|
12
|
+
return '(no target)';
|
|
13
|
+
// ID selector: strip # and common prefixes
|
|
14
|
+
if (selector.startsWith('#')) {
|
|
15
|
+
let name = selector.slice(1);
|
|
16
|
+
const prefixes = ['hero-', 'main-', 'page-', 'app-', 'section-'];
|
|
17
|
+
for (const prefix of prefixes) {
|
|
18
|
+
if (name.startsWith(prefix)) {
|
|
19
|
+
name = name.slice(prefix.length);
|
|
20
|
+
break;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return truncate(name, 50);
|
|
24
|
+
}
|
|
25
|
+
// data-testid selector
|
|
26
|
+
const testIdMatch = selector.match(/\[data-testid="([^"]+)"\]/);
|
|
27
|
+
if (testIdMatch) {
|
|
28
|
+
return `${testIdMatch[1]} element`;
|
|
29
|
+
}
|
|
30
|
+
// Class selector or complex selector: keep as-is
|
|
31
|
+
return truncate(selector, 50);
|
|
32
|
+
}
|
|
33
|
+
function truncate(text, max) {
|
|
34
|
+
if (text.length <= max)
|
|
35
|
+
return text;
|
|
36
|
+
return `${text.slice(0, max)}...`;
|
|
37
|
+
}
|
|
38
|
+
function truncateQuoted(text, max) {
|
|
39
|
+
if (text.length <= max)
|
|
40
|
+
return `"${text}"`;
|
|
41
|
+
return `"${text.slice(0, max)}..."`;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Generate a human-readable one-liner for a content config change.
|
|
45
|
+
*/
|
|
46
|
+
export function summarizeContentChange(type, item) {
|
|
47
|
+
const desc = describeSelector(item.anchorId || '');
|
|
48
|
+
switch (type) {
|
|
49
|
+
case 'textReplacements': {
|
|
50
|
+
const text = item.text || '';
|
|
51
|
+
return `Change ${desc} to ${truncateQuoted(text, MAX_TEXT_LEN)}`;
|
|
52
|
+
}
|
|
53
|
+
case 'attributeChanges': {
|
|
54
|
+
const attr = item.attr || '';
|
|
55
|
+
const value = item.value || '';
|
|
56
|
+
return `Set ${desc} ${attr} to ${truncateQuoted(value, MAX_TEXT_LEN)}`;
|
|
57
|
+
}
|
|
58
|
+
case 'styleChanges': {
|
|
59
|
+
const styles = item.styles || {};
|
|
60
|
+
const count = Object.keys(styles).length;
|
|
61
|
+
return `Restyle ${desc} (${count} ${count === 1 ? 'property' : 'properties'})`;
|
|
62
|
+
}
|
|
63
|
+
case 'htmlInsertions': {
|
|
64
|
+
const position = item.position || 'append';
|
|
65
|
+
return `Insert HTML ${position} ${desc}`;
|
|
66
|
+
}
|
|
67
|
+
case 'classAdditions': {
|
|
68
|
+
const className = item.className || '';
|
|
69
|
+
return `Add class "${className}" to ${desc}`;
|
|
70
|
+
}
|
|
71
|
+
case 'classRemovals': {
|
|
72
|
+
const className = item.className || '';
|
|
73
|
+
return `Remove class "${className}" from ${desc}`;
|
|
74
|
+
}
|
|
75
|
+
default:
|
|
76
|
+
return `Unknown change on ${desc}`;
|
|
77
|
+
}
|
|
78
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -12,6 +12,22 @@ export interface EditorPanelProps {
|
|
|
12
12
|
navigateHome: () => Promise<boolean>;
|
|
13
13
|
save: () => Promise<void>;
|
|
14
14
|
publish: (captureScreenshot?: boolean) => Promise<void>;
|
|
15
|
+
/** Navigate the target page to a different route (preserves editor params). */
|
|
16
|
+
navigateTo: (route: string) => Promise<void>;
|
|
17
|
+
/** Show a persistent blue highlight on the element matching this selector. */
|
|
18
|
+
highlightElement: (selector: string) => void;
|
|
19
|
+
/** Remove the persistent element highlight. */
|
|
20
|
+
clearHighlight: () => void;
|
|
21
|
+
/** Get the current page route (pathname). */
|
|
22
|
+
getCurrentRoute: () => string;
|
|
23
|
+
/** Push a temporary config to the live page preview without saving to state. */
|
|
24
|
+
previewConfig: (config: Record<string, unknown>) => void;
|
|
25
|
+
/** Flat action index to open in edit mode (from accordion navigation). */
|
|
26
|
+
initialEditKey?: string;
|
|
27
|
+
/** Open the editor in create mode. */
|
|
28
|
+
initialCreate?: boolean;
|
|
29
|
+
/** Clear the initial navigation state (call after consuming). */
|
|
30
|
+
clearInitialState?: () => void;
|
|
15
31
|
};
|
|
16
32
|
platformClient?: unknown;
|
|
17
33
|
}
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IACpD,MAAM,EAAE;QACN,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;QACnC,YAAY,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;QACrC,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;QAC1B,OAAO,EAAE,CAAC,iBAAiB,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@syntrologie/adapt-content",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Adaptive Content app - DOM manipulation actions for text, attributes, and styles",
|
|
5
5
|
"license": "Proprietary",
|
|
6
6
|
"private": false,
|
|
@@ -33,7 +33,9 @@
|
|
|
33
33
|
],
|
|
34
34
|
"scripts": {
|
|
35
35
|
"build": "tsc",
|
|
36
|
-
"clean": "rm -rf dist"
|
|
36
|
+
"clean": "rm -rf dist",
|
|
37
|
+
"test": "vitest run",
|
|
38
|
+
"test:watch": "vitest"
|
|
37
39
|
},
|
|
38
40
|
"peerDependencies": {
|
|
39
41
|
"@syntrologie/runtime-sdk": "^2.0.0",
|
|
@@ -42,9 +44,15 @@
|
|
|
42
44
|
"zod": "^3.0.0"
|
|
43
45
|
},
|
|
44
46
|
"devDependencies": {
|
|
45
|
-
"@floating-ui/dom": "^1.
|
|
46
|
-
"@
|
|
47
|
-
"
|
|
48
|
-
"
|
|
47
|
+
"@floating-ui/dom": "^1.7.5",
|
|
48
|
+
"@testing-library/react": "^16.3.2",
|
|
49
|
+
"@types/react": "^19.1.0",
|
|
50
|
+
"@types/react-dom": "^19.2.3",
|
|
51
|
+
"jsdom": "^25.0.1",
|
|
52
|
+
"react": "^19.2.4",
|
|
53
|
+
"react-dom": "^19.2.4",
|
|
54
|
+
"typescript": "^5.7.3",
|
|
55
|
+
"vitest": "^2.1.9",
|
|
56
|
+
"zod": "^3.25.76"
|
|
49
57
|
}
|
|
50
58
|
}
|