dynim-core 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/README.md +290 -0
- package/dist/builder/ai-prompt-popover.d.ts +26 -0
- package/dist/builder/ai-prompt-popover.d.ts.map +1 -0
- package/dist/builder/ai-prompt-popover.js +180 -0
- package/dist/builder/builder-client.d.ts +48 -0
- package/dist/builder/builder-client.d.ts.map +1 -0
- package/dist/builder/builder-client.js +157 -0
- package/dist/builder/builder.d.ts +41 -0
- package/dist/builder/builder.d.ts.map +1 -0
- package/dist/builder/builder.js +537 -0
- package/dist/builder/bundle-manager.d.ts +60 -0
- package/dist/builder/bundle-manager.d.ts.map +1 -0
- package/dist/builder/bundle-manager.js +357 -0
- package/dist/builder/classifier/classname-analyzer.d.ts +6 -0
- package/dist/builder/classifier/classname-analyzer.d.ts.map +1 -0
- package/dist/builder/classifier/classname-analyzer.js +107 -0
- package/dist/builder/classifier/index.d.ts +20 -0
- package/dist/builder/classifier/index.d.ts.map +1 -0
- package/dist/builder/classifier/index.js +181 -0
- package/dist/builder/classifier/semantic-analyzer.d.ts +24 -0
- package/dist/builder/classifier/semantic-analyzer.d.ts.map +1 -0
- package/dist/builder/classifier/semantic-analyzer.js +94 -0
- package/dist/builder/classifier/size-analyzer.d.ts +7 -0
- package/dist/builder/classifier/size-analyzer.d.ts.map +1 -0
- package/dist/builder/classifier/size-analyzer.js +120 -0
- package/dist/builder/classifier/visual-analyzer.d.ts +6 -0
- package/dist/builder/classifier/visual-analyzer.d.ts.map +1 -0
- package/dist/builder/classifier/visual-analyzer.js +158 -0
- package/dist/builder/client.d.ts +22 -0
- package/dist/builder/client.d.ts.map +1 -0
- package/dist/builder/client.js +54 -0
- package/dist/builder/code-client.d.ts +101 -0
- package/dist/builder/code-client.d.ts.map +1 -0
- package/dist/builder/code-client.js +418 -0
- package/dist/builder/diff-state.d.ts +24 -0
- package/dist/builder/diff-state.d.ts.map +1 -0
- package/dist/builder/diff-state.js +134 -0
- package/dist/builder/dom-scanner.d.ts +20 -0
- package/dist/builder/dom-scanner.d.ts.map +1 -0
- package/dist/builder/dom-scanner.js +102 -0
- package/dist/builder/drag-engine.d.ts +41 -0
- package/dist/builder/drag-engine.d.ts.map +1 -0
- package/dist/builder/drag-engine.js +686 -0
- package/dist/builder/editor-overlays.d.ts +31 -0
- package/dist/builder/editor-overlays.d.ts.map +1 -0
- package/dist/builder/editor-overlays.js +202 -0
- package/dist/builder/editor-state.d.ts +50 -0
- package/dist/builder/editor-state.d.ts.map +1 -0
- package/dist/builder/editor-state.js +132 -0
- package/dist/builder/element-utils.d.ts +43 -0
- package/dist/builder/element-utils.d.ts.map +1 -0
- package/dist/builder/element-utils.js +227 -0
- package/dist/builder/fiber-capture.d.ts +28 -0
- package/dist/builder/fiber-capture.d.ts.map +1 -0
- package/dist/builder/fiber-capture.js +264 -0
- package/dist/builder/freeze-overlay.d.ts +26 -0
- package/dist/builder/freeze-overlay.d.ts.map +1 -0
- package/dist/builder/freeze-overlay.js +213 -0
- package/dist/builder/history-state.d.ts +41 -0
- package/dist/builder/history-state.d.ts.map +1 -0
- package/dist/builder/history-state.js +76 -0
- package/dist/builder/index.d.ts +62 -0
- package/dist/builder/index.d.ts.map +1 -0
- package/dist/builder/index.js +92 -0
- package/dist/builder/state.d.ts +27 -0
- package/dist/builder/state.d.ts.map +1 -0
- package/dist/builder/state.js +50 -0
- package/dist/builder/style-applier.d.ts +61 -0
- package/dist/builder/style-applier.d.ts.map +1 -0
- package/dist/builder/style-applier.js +311 -0
- package/dist/builder/tree-state.d.ts +71 -0
- package/dist/builder/tree-state.d.ts.map +1 -0
- package/dist/builder/tree-state.js +168 -0
- package/dist/builder/widget.d.ts +29 -0
- package/dist/builder/widget.d.ts.map +1 -0
- package/dist/builder/widget.js +181 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/package.json +25 -0
- package/src/styles/base.css +378 -0
- package/src/styles/builder.css +422 -0
- package/src/styles/editor.css +131 -0
- package/src/styles/themes/dark.css +24 -0
- package/src/styles/themes/light.css +21 -0
- package/src/styles/variables.css +63 -0
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Style Applier - Parses code edits and applies styling changes directly to DOM
|
|
3
|
+
*
|
|
4
|
+
* Extracts styling-related changes from React/JSX code diffs:
|
|
5
|
+
* - className prop changes
|
|
6
|
+
* - style prop changes
|
|
7
|
+
* - Tailwind class additions/removals
|
|
8
|
+
*
|
|
9
|
+
* Then applies them to the corresponding DOM elements without requiring a bundle reload.
|
|
10
|
+
*/
|
|
11
|
+
import { getFiberFromDOM } from './fiber-capture';
|
|
12
|
+
/**
|
|
13
|
+
* Extract component name from file path
|
|
14
|
+
* e.g., "components/Button.tsx" -> "Button"
|
|
15
|
+
*/
|
|
16
|
+
function extractComponentName(filePath) {
|
|
17
|
+
const match = filePath.match(/([A-Z][a-zA-Z0-9]*)\.(tsx?|jsx?)$/);
|
|
18
|
+
return match ? match[1] : null;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Parse className from JSX
|
|
22
|
+
* Handles: className="foo", className={'foo'}, className={`foo`}
|
|
23
|
+
*/
|
|
24
|
+
function parseClassName(code) {
|
|
25
|
+
// Match className="..." or className={'...'} or className={`...`}
|
|
26
|
+
const patterns = [
|
|
27
|
+
/className\s*=\s*"([^"]*)"/,
|
|
28
|
+
/className\s*=\s*'([^']*)'/,
|
|
29
|
+
/className\s*=\s*\{['"`]([^'"`]*?)['"`]\}/,
|
|
30
|
+
/className\s*=\s*\{`([^`]*?)`\}/,
|
|
31
|
+
];
|
|
32
|
+
for (const pattern of patterns) {
|
|
33
|
+
const match = code.match(pattern);
|
|
34
|
+
if (match) {
|
|
35
|
+
return match[1];
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Parse inline style from JSX
|
|
42
|
+
* Handles: style={{ color: 'red' }}
|
|
43
|
+
*/
|
|
44
|
+
function parseStyleProp(code) {
|
|
45
|
+
// Match style={{ ... }}
|
|
46
|
+
const match = code.match(/style\s*=\s*\{\{([^}]*)\}\}/);
|
|
47
|
+
if (!match)
|
|
48
|
+
return null;
|
|
49
|
+
const styleStr = match[1];
|
|
50
|
+
const styles = {};
|
|
51
|
+
// Parse key: value pairs (handles both 'value' and "value")
|
|
52
|
+
const propPattern = /(\w+)\s*:\s*['"]([^'"]+)['"]/g;
|
|
53
|
+
let propMatch;
|
|
54
|
+
while ((propMatch = propPattern.exec(styleStr)) !== null) {
|
|
55
|
+
// Convert camelCase to kebab-case for CSS
|
|
56
|
+
const key = propMatch[1].replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
57
|
+
styles[key] = propMatch[2];
|
|
58
|
+
}
|
|
59
|
+
return Object.keys(styles).length > 0 ? styles : null;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Find the differences between two class strings
|
|
63
|
+
*/
|
|
64
|
+
function diffClasses(oldClasses, newClasses) {
|
|
65
|
+
const oldSet = new Set(oldClasses.split(/\s+/).filter(Boolean));
|
|
66
|
+
const newSet = new Set(newClasses.split(/\s+/).filter(Boolean));
|
|
67
|
+
const add = [];
|
|
68
|
+
const remove = [];
|
|
69
|
+
for (const cls of newSet) {
|
|
70
|
+
if (!oldSet.has(cls))
|
|
71
|
+
add.push(cls);
|
|
72
|
+
}
|
|
73
|
+
for (const cls of oldSet) {
|
|
74
|
+
if (!newSet.has(cls))
|
|
75
|
+
remove.push(cls);
|
|
76
|
+
}
|
|
77
|
+
return { add, remove };
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Find style property differences
|
|
81
|
+
*/
|
|
82
|
+
function diffStyles(oldStyles, newStyles) {
|
|
83
|
+
const changes = [];
|
|
84
|
+
const allKeys = new Set([
|
|
85
|
+
...Object.keys(oldStyles || {}),
|
|
86
|
+
...Object.keys(newStyles || {}),
|
|
87
|
+
]);
|
|
88
|
+
for (const key of allKeys) {
|
|
89
|
+
const oldVal = oldStyles?.[key];
|
|
90
|
+
const newVal = newStyles?.[key];
|
|
91
|
+
if (oldVal !== newVal) {
|
|
92
|
+
changes.push({ property: key, oldValue: oldVal, newValue: newVal });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return changes;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Try to find elements by component name using React fiber
|
|
99
|
+
*/
|
|
100
|
+
function findElementsByFiber(componentName, root = document.body) {
|
|
101
|
+
const elements = [];
|
|
102
|
+
// Walk the DOM looking for elements rendered by this component
|
|
103
|
+
const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
|
|
104
|
+
let node = walker.nextNode();
|
|
105
|
+
while (node) {
|
|
106
|
+
const fiber = getFiberFromDOM(node);
|
|
107
|
+
if (fiber) {
|
|
108
|
+
// Check if this fiber's component matches
|
|
109
|
+
const type = fiber.type;
|
|
110
|
+
let name = null;
|
|
111
|
+
if (typeof type === 'function') {
|
|
112
|
+
name = type.displayName ||
|
|
113
|
+
type.name || null;
|
|
114
|
+
}
|
|
115
|
+
if (name === componentName) {
|
|
116
|
+
elements.push(node);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
node = walker.nextNode();
|
|
120
|
+
}
|
|
121
|
+
return elements;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Generate a selector to find elements
|
|
125
|
+
*/
|
|
126
|
+
function generateSelector(componentName) {
|
|
127
|
+
// Try common patterns:
|
|
128
|
+
// - data-component attribute
|
|
129
|
+
// - data-testid attribute
|
|
130
|
+
// - class name based on component
|
|
131
|
+
return [
|
|
132
|
+
`[data-component="${componentName}"]`,
|
|
133
|
+
`[data-testid="${componentName}"]`,
|
|
134
|
+
`[data-testid="${componentName.toLowerCase()}"]`,
|
|
135
|
+
`.${componentName}`,
|
|
136
|
+
`.${componentName.toLowerCase()}`,
|
|
137
|
+
].join(', ');
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Create a style applier instance
|
|
141
|
+
*/
|
|
142
|
+
export function createStyleApplier(config = {}) {
|
|
143
|
+
const { queryElements = (sel) => document.querySelectorAll(sel), mapComponentToElements, onApply, onError, } = config;
|
|
144
|
+
/**
|
|
145
|
+
* Parse a code edit and extract style changes
|
|
146
|
+
*/
|
|
147
|
+
function parseEdit(edit) {
|
|
148
|
+
const changes = [];
|
|
149
|
+
if (!edit.old_content && !edit.new_content) {
|
|
150
|
+
return changes;
|
|
151
|
+
}
|
|
152
|
+
const componentName = extractComponentName(edit.file_path);
|
|
153
|
+
// Parse classNames
|
|
154
|
+
const oldClassName = edit.old_content ? parseClassName(edit.old_content) : null;
|
|
155
|
+
const newClassName = edit.new_content ? parseClassName(edit.new_content) : null;
|
|
156
|
+
if (oldClassName !== newClassName && (oldClassName || newClassName)) {
|
|
157
|
+
// Determine if this is a full className change or Tailwind add/remove
|
|
158
|
+
if (oldClassName && newClassName) {
|
|
159
|
+
const { add, remove } = diffClasses(oldClassName, newClassName);
|
|
160
|
+
if (add.length > 0 || remove.length > 0) {
|
|
161
|
+
// Tailwind-style class manipulation
|
|
162
|
+
changes.push({
|
|
163
|
+
type: 'tailwind',
|
|
164
|
+
filePath: edit.file_path,
|
|
165
|
+
componentName: componentName || undefined,
|
|
166
|
+
oldClassName,
|
|
167
|
+
newClassName,
|
|
168
|
+
addClasses: add,
|
|
169
|
+
removeClasses: remove,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
// Full className replacement
|
|
175
|
+
changes.push({
|
|
176
|
+
type: 'className',
|
|
177
|
+
filePath: edit.file_path,
|
|
178
|
+
componentName: componentName || undefined,
|
|
179
|
+
oldClassName: oldClassName || undefined,
|
|
180
|
+
newClassName: newClassName || undefined,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// Parse style props
|
|
185
|
+
const oldStyles = edit.old_content ? parseStyleProp(edit.old_content) : null;
|
|
186
|
+
const newStyles = edit.new_content ? parseStyleProp(edit.new_content) : null;
|
|
187
|
+
const styleDiffs = diffStyles(oldStyles, newStyles);
|
|
188
|
+
for (const diff of styleDiffs) {
|
|
189
|
+
changes.push({
|
|
190
|
+
type: 'style',
|
|
191
|
+
filePath: edit.file_path,
|
|
192
|
+
componentName: componentName || undefined,
|
|
193
|
+
styleProperty: diff.property,
|
|
194
|
+
oldValue: diff.oldValue,
|
|
195
|
+
newValue: diff.newValue,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
return changes;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Find DOM elements for a component
|
|
202
|
+
*/
|
|
203
|
+
function findElements(componentName, filePath) {
|
|
204
|
+
// Try custom mapping first
|
|
205
|
+
if (mapComponentToElements) {
|
|
206
|
+
const elements = mapComponentToElements(componentName, filePath);
|
|
207
|
+
if (elements.length > 0)
|
|
208
|
+
return elements;
|
|
209
|
+
}
|
|
210
|
+
// Try fiber-based lookup
|
|
211
|
+
try {
|
|
212
|
+
const fiberElements = findElementsByFiber(componentName);
|
|
213
|
+
if (fiberElements.length > 0)
|
|
214
|
+
return fiberElements;
|
|
215
|
+
}
|
|
216
|
+
catch (e) {
|
|
217
|
+
// Fiber lookup failed, continue to selector fallback
|
|
218
|
+
}
|
|
219
|
+
// Fall back to selector-based lookup
|
|
220
|
+
const selector = generateSelector(componentName);
|
|
221
|
+
return Array.from(queryElements(selector));
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Apply a style change to the DOM
|
|
225
|
+
*/
|
|
226
|
+
function applyChange(change) {
|
|
227
|
+
const elements = [];
|
|
228
|
+
try {
|
|
229
|
+
// Find target elements
|
|
230
|
+
if (change.componentName) {
|
|
231
|
+
elements.push(...findElements(change.componentName, change.filePath));
|
|
232
|
+
}
|
|
233
|
+
else if (change.selector) {
|
|
234
|
+
elements.push(...Array.from(queryElements(change.selector)));
|
|
235
|
+
}
|
|
236
|
+
if (elements.length === 0) {
|
|
237
|
+
console.warn(`[StyleApplier] No elements found for:`, change);
|
|
238
|
+
return elements;
|
|
239
|
+
}
|
|
240
|
+
// Apply the change
|
|
241
|
+
for (const el of elements) {
|
|
242
|
+
const htmlEl = el;
|
|
243
|
+
switch (change.type) {
|
|
244
|
+
case 'className':
|
|
245
|
+
if (change.newClassName !== undefined) {
|
|
246
|
+
htmlEl.className = change.newClassName;
|
|
247
|
+
}
|
|
248
|
+
break;
|
|
249
|
+
case 'tailwind':
|
|
250
|
+
if (change.removeClasses) {
|
|
251
|
+
change.removeClasses.forEach(cls => {
|
|
252
|
+
if (cls)
|
|
253
|
+
htmlEl.classList.remove(cls);
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
if (change.addClasses) {
|
|
257
|
+
change.addClasses.forEach(cls => {
|
|
258
|
+
if (cls)
|
|
259
|
+
htmlEl.classList.add(cls);
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
break;
|
|
263
|
+
case 'style':
|
|
264
|
+
if (change.styleProperty) {
|
|
265
|
+
if (change.newValue !== undefined) {
|
|
266
|
+
htmlEl.style.setProperty(change.styleProperty, change.newValue);
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
htmlEl.style.removeProperty(change.styleProperty);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
break;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
onApply?.(change, elements);
|
|
276
|
+
}
|
|
277
|
+
catch (error) {
|
|
278
|
+
console.error('[StyleApplier] Failed to apply change:', error);
|
|
279
|
+
onError?.(change, error);
|
|
280
|
+
}
|
|
281
|
+
return elements;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Parse and apply all style changes from an edit
|
|
285
|
+
*/
|
|
286
|
+
function processEdit(edit) {
|
|
287
|
+
const changes = parseEdit(edit);
|
|
288
|
+
let applied = 0;
|
|
289
|
+
for (const change of changes) {
|
|
290
|
+
const elements = applyChange(change);
|
|
291
|
+
if (elements.length > 0) {
|
|
292
|
+
applied++;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return { changes, applied };
|
|
296
|
+
}
|
|
297
|
+
return {
|
|
298
|
+
parseEdit,
|
|
299
|
+
applyChange,
|
|
300
|
+
processEdit,
|
|
301
|
+
findElements,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Utility: Check if an edit likely contains style changes
|
|
306
|
+
* Quick check before doing full parsing
|
|
307
|
+
*/
|
|
308
|
+
export function hasStyleChanges(edit) {
|
|
309
|
+
const content = (edit.old_content || '') + (edit.new_content || '');
|
|
310
|
+
return /className|style\s*=|class\s*=/.test(content);
|
|
311
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component tree state management
|
|
3
|
+
* Stores the serialized DOM tree for navigation and classification
|
|
4
|
+
*/
|
|
5
|
+
import { type Classification } from './classifier/index';
|
|
6
|
+
export interface TreeNode {
|
|
7
|
+
id: string;
|
|
8
|
+
tagName: string;
|
|
9
|
+
className: string;
|
|
10
|
+
selector: string;
|
|
11
|
+
classification: Classification;
|
|
12
|
+
parentId: string | null;
|
|
13
|
+
childIds: string[];
|
|
14
|
+
depth: number;
|
|
15
|
+
domPath: number[];
|
|
16
|
+
stableId: string | null;
|
|
17
|
+
boundingRect: SerializedRect | null;
|
|
18
|
+
computedStyles: Record<string, string> | null;
|
|
19
|
+
isExpanded: boolean;
|
|
20
|
+
isVisible: boolean;
|
|
21
|
+
attributes: Record<string, string> | null;
|
|
22
|
+
textContent: string | null;
|
|
23
|
+
childCount: number;
|
|
24
|
+
}
|
|
25
|
+
export interface SerializedRect {
|
|
26
|
+
x: number;
|
|
27
|
+
y: number;
|
|
28
|
+
width: number;
|
|
29
|
+
height: number;
|
|
30
|
+
top: number;
|
|
31
|
+
right: number;
|
|
32
|
+
bottom: number;
|
|
33
|
+
left: number;
|
|
34
|
+
}
|
|
35
|
+
export interface SerializedDOMNode {
|
|
36
|
+
domPath: number[];
|
|
37
|
+
tagName: string;
|
|
38
|
+
id: string | null;
|
|
39
|
+
className: string;
|
|
40
|
+
rect: SerializedRect;
|
|
41
|
+
computedStyles: Record<string, string>;
|
|
42
|
+
computedDisplay: string;
|
|
43
|
+
hasChildren: boolean;
|
|
44
|
+
textContent: string | null;
|
|
45
|
+
attributes: Record<string, string>;
|
|
46
|
+
childCount: number;
|
|
47
|
+
children: SerializedDOMNode[];
|
|
48
|
+
depth: number;
|
|
49
|
+
}
|
|
50
|
+
export interface TreeStateData {
|
|
51
|
+
nodesById: Record<string, TreeNode>;
|
|
52
|
+
rootIds: string[];
|
|
53
|
+
nodesByDomPath: Record<string, string>;
|
|
54
|
+
nodesByStableId: Record<string, string>;
|
|
55
|
+
expandedIds: Set<string>;
|
|
56
|
+
}
|
|
57
|
+
export type TreeStateListener = (state: TreeStateData) => void;
|
|
58
|
+
export interface TreeState {
|
|
59
|
+
getState: () => TreeStateData;
|
|
60
|
+
setState: (updates: Partial<TreeStateData>) => void;
|
|
61
|
+
subscribe: (listener: TreeStateListener) => () => void;
|
|
62
|
+
setTree: (root: SerializedDOMNode) => void;
|
|
63
|
+
expandToNode: (id: string) => void;
|
|
64
|
+
getNodeByDomPath: (path: number[]) => TreeNode | null;
|
|
65
|
+
getNodeByStableId: (stableId: string) => TreeNode | null;
|
|
66
|
+
getNodeById: (id: string) => TreeNode | null;
|
|
67
|
+
toggleExpanded: (id: string) => void;
|
|
68
|
+
clear: () => void;
|
|
69
|
+
}
|
|
70
|
+
export declare function createTreeState(initialState?: Partial<TreeStateData>): TreeState;
|
|
71
|
+
//# sourceMappingURL=tree-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree-state.d.ts","sourceRoot":"","sources":["../../src/builder/tree-state.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAoB,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAE3E,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,cAAc,CAAC;IAC/B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,YAAY,EAAE,cAAc,GAAG,IAAI,CAAC;IACpC,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IAC9C,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IAC1C,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,cAAc,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACpC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC1B;AAED,MAAM,MAAM,iBAAiB,GAAG,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;AAE/D,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,aAAa,CAAC;IAC9B,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,aAAa,CAAC,KAAK,IAAI,CAAC;IACpD,SAAS,EAAE,CAAC,QAAQ,EAAE,iBAAiB,KAAK,MAAM,IAAI,CAAC;IACvD,OAAO,EAAE,CAAC,IAAI,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAC3C,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,QAAQ,GAAG,IAAI,CAAC;IACtD,iBAAiB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,QAAQ,GAAG,IAAI,CAAC;IACzD,WAAW,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,QAAQ,GAAG,IAAI,CAAC;IAC7C,cAAc,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED,wBAAgB,eAAe,CAAC,YAAY,GAAE,OAAO,CAAC,aAAa,CAAM,GAAG,SAAS,CA+LpF"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component tree state management
|
|
3
|
+
* Stores the serialized DOM tree for navigation and classification
|
|
4
|
+
*/
|
|
5
|
+
import { createClassifier } from './classifier/index';
|
|
6
|
+
export function createTreeState(initialState = {}) {
|
|
7
|
+
let state = {
|
|
8
|
+
nodesById: {},
|
|
9
|
+
rootIds: [],
|
|
10
|
+
nodesByDomPath: {},
|
|
11
|
+
nodesByStableId: {},
|
|
12
|
+
expandedIds: new Set(),
|
|
13
|
+
...initialState
|
|
14
|
+
};
|
|
15
|
+
const listeners = new Set();
|
|
16
|
+
const classifier = createClassifier();
|
|
17
|
+
function getState() {
|
|
18
|
+
return {
|
|
19
|
+
...state,
|
|
20
|
+
expandedIds: new Set(state.expandedIds)
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function setState(updates) {
|
|
24
|
+
state = { ...state, ...updates };
|
|
25
|
+
listeners.forEach(listener => listener(state));
|
|
26
|
+
}
|
|
27
|
+
function subscribe(listener) {
|
|
28
|
+
listeners.add(listener);
|
|
29
|
+
return () => listeners.delete(listener);
|
|
30
|
+
}
|
|
31
|
+
function generateId() {
|
|
32
|
+
return Math.random().toString(36).substr(2, 10);
|
|
33
|
+
}
|
|
34
|
+
function generateSelector(node) {
|
|
35
|
+
const tag = node.tagName.toLowerCase();
|
|
36
|
+
const id = node.id ? `#${node.id}` : '';
|
|
37
|
+
const classes = (node.className || '')
|
|
38
|
+
.split(/\s+/)
|
|
39
|
+
.filter(c => {
|
|
40
|
+
if (!c)
|
|
41
|
+
return false;
|
|
42
|
+
if (/^(flex|grid|p-|m-|w-|h-|bg-|text-|border-|rounded-|shadow-|overflow-|relative|absolute|fixed|hidden|block|inline|items-|justify-|gap-|space-)/.test(c)) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
return c.length > 1;
|
|
46
|
+
})
|
|
47
|
+
.slice(0, 3);
|
|
48
|
+
const classSelector = classes.length > 0 ? '.' + classes.join('.') : '';
|
|
49
|
+
return `${tag}${id}${classSelector}`;
|
|
50
|
+
}
|
|
51
|
+
function setTree(root) {
|
|
52
|
+
const newNodesById = {};
|
|
53
|
+
const newRootIds = [];
|
|
54
|
+
const newNodesByDomPath = {};
|
|
55
|
+
const newNodesByStableId = {};
|
|
56
|
+
const newExpandedIds = new Set();
|
|
57
|
+
function processNode(node, parentId) {
|
|
58
|
+
const id = generateId();
|
|
59
|
+
const classification = classifier.classify({
|
|
60
|
+
tagName: node.tagName,
|
|
61
|
+
id: node.id,
|
|
62
|
+
className: node.className,
|
|
63
|
+
computedStyles: node.computedStyles,
|
|
64
|
+
rect: node.rect,
|
|
65
|
+
parentTagName: parentId ? newNodesById[parentId]?.tagName ?? null : null,
|
|
66
|
+
childCount: node.childCount,
|
|
67
|
+
hasTextContent: !!node.textContent,
|
|
68
|
+
attributes: node.attributes
|
|
69
|
+
});
|
|
70
|
+
const selector = generateSelector(node);
|
|
71
|
+
const childIds = [];
|
|
72
|
+
for (const child of node.children || []) {
|
|
73
|
+
childIds.push(processNode(child, id));
|
|
74
|
+
}
|
|
75
|
+
const stableId = node.attributes?.['data-sdesign-id'] || null;
|
|
76
|
+
const treeNode = {
|
|
77
|
+
id,
|
|
78
|
+
tagName: node.tagName,
|
|
79
|
+
className: node.className,
|
|
80
|
+
selector,
|
|
81
|
+
classification,
|
|
82
|
+
parentId,
|
|
83
|
+
childIds,
|
|
84
|
+
depth: node.depth,
|
|
85
|
+
domPath: node.domPath,
|
|
86
|
+
stableId,
|
|
87
|
+
boundingRect: node.rect,
|
|
88
|
+
computedStyles: node.computedStyles,
|
|
89
|
+
isExpanded: node.depth < 2,
|
|
90
|
+
isVisible: true,
|
|
91
|
+
attributes: node.attributes,
|
|
92
|
+
textContent: node.textContent,
|
|
93
|
+
childCount: node.childCount
|
|
94
|
+
};
|
|
95
|
+
newNodesById[id] = treeNode;
|
|
96
|
+
newNodesByDomPath[node.domPath.join(',')] = id;
|
|
97
|
+
if (stableId) {
|
|
98
|
+
newNodesByStableId[stableId] = id;
|
|
99
|
+
}
|
|
100
|
+
if (node.depth < 2 && childIds.length > 0) {
|
|
101
|
+
newExpandedIds.add(id);
|
|
102
|
+
}
|
|
103
|
+
if (parentId === null) {
|
|
104
|
+
newRootIds.push(id);
|
|
105
|
+
}
|
|
106
|
+
return id;
|
|
107
|
+
}
|
|
108
|
+
processNode(root, null);
|
|
109
|
+
setState({
|
|
110
|
+
nodesById: newNodesById,
|
|
111
|
+
rootIds: newRootIds,
|
|
112
|
+
nodesByDomPath: newNodesByDomPath,
|
|
113
|
+
nodesByStableId: newNodesByStableId,
|
|
114
|
+
expandedIds: newExpandedIds
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
function expandToNode(id) {
|
|
118
|
+
const newExpandedIds = new Set(state.expandedIds);
|
|
119
|
+
let current = state.nodesById[id];
|
|
120
|
+
while (current?.parentId) {
|
|
121
|
+
newExpandedIds.add(current.parentId);
|
|
122
|
+
current = state.nodesById[current.parentId];
|
|
123
|
+
}
|
|
124
|
+
setState({ expandedIds: newExpandedIds });
|
|
125
|
+
}
|
|
126
|
+
function getNodeByDomPath(path) {
|
|
127
|
+
const id = state.nodesByDomPath[path.join(',')];
|
|
128
|
+
return id ? state.nodesById[id] ?? null : null;
|
|
129
|
+
}
|
|
130
|
+
function getNodeByStableId(stableId) {
|
|
131
|
+
const id = state.nodesByStableId[stableId];
|
|
132
|
+
return id ? state.nodesById[id] ?? null : null;
|
|
133
|
+
}
|
|
134
|
+
function getNodeById(id) {
|
|
135
|
+
return state.nodesById[id] ?? null;
|
|
136
|
+
}
|
|
137
|
+
function toggleExpanded(id) {
|
|
138
|
+
const newExpandedIds = new Set(state.expandedIds);
|
|
139
|
+
if (newExpandedIds.has(id)) {
|
|
140
|
+
newExpandedIds.delete(id);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
newExpandedIds.add(id);
|
|
144
|
+
}
|
|
145
|
+
setState({ expandedIds: newExpandedIds });
|
|
146
|
+
}
|
|
147
|
+
function clear() {
|
|
148
|
+
setState({
|
|
149
|
+
nodesById: {},
|
|
150
|
+
rootIds: [],
|
|
151
|
+
nodesByDomPath: {},
|
|
152
|
+
nodesByStableId: {},
|
|
153
|
+
expandedIds: new Set()
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
getState,
|
|
158
|
+
setState,
|
|
159
|
+
subscribe,
|
|
160
|
+
setTree,
|
|
161
|
+
expandToNode,
|
|
162
|
+
getNodeByDomPath,
|
|
163
|
+
getNodeByStableId,
|
|
164
|
+
getNodeById,
|
|
165
|
+
toggleExpanded,
|
|
166
|
+
clear
|
|
167
|
+
};
|
|
168
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default DOM widget for vanilla JS users
|
|
3
|
+
* Renders a complete chat interface - floating or inline
|
|
4
|
+
*/
|
|
5
|
+
import type { StateStore } from './state';
|
|
6
|
+
import type { StreamClient } from './client';
|
|
7
|
+
export type WidgetMode = 'floating' | 'inline';
|
|
8
|
+
export type WidgetPosition = 'bottom-right' | 'bottom-left';
|
|
9
|
+
export type WidgetTheme = 'light' | 'dark';
|
|
10
|
+
export interface WidgetConfig {
|
|
11
|
+
mode?: WidgetMode;
|
|
12
|
+
position?: WidgetPosition;
|
|
13
|
+
startOpen?: boolean;
|
|
14
|
+
showTimestamps?: boolean;
|
|
15
|
+
showAvatars?: boolean;
|
|
16
|
+
theme?: WidgetTheme;
|
|
17
|
+
placeholder?: string;
|
|
18
|
+
title?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface Widget {
|
|
21
|
+
root: HTMLElement;
|
|
22
|
+
open: () => void;
|
|
23
|
+
close: () => void;
|
|
24
|
+
toggle: () => void;
|
|
25
|
+
isOpen: () => boolean;
|
|
26
|
+
destroy: () => void;
|
|
27
|
+
}
|
|
28
|
+
export declare function createWidget(container: string | HTMLElement | null, state: StateStore, client: StreamClient, config?: WidgetConfig): Widget;
|
|
29
|
+
//# sourceMappingURL=widget.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"widget.d.ts","sourceRoot":"","sources":["../../src/builder/widget.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAW,MAAM,SAAS,CAAC;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAE7C,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,QAAQ,CAAC;AAC/C,MAAM,MAAM,cAAc,GAAG,cAAc,GAAG,aAAa,CAAC;AAC5D,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,MAAM,CAAC;AAE3C,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,MAAM,EAAE,MAAM,OAAO,CAAC;IACtB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,wBAAgB,YAAY,CAC1B,SAAS,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,EACtC,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,YAAY,EACpB,MAAM,GAAE,YAAiB,GACxB,MAAM,CA6MR"}
|