juxscript 1.1.63 → 1.1.64

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/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { alert } from './lib/components/alert.js';
2
2
  import { app } from './lib/components/app.js';
3
3
  import { badge } from './lib/components/badge.js';
4
+ import { blueprint } from './lib/components/blueprint.js';
4
5
  import { button } from './lib/components/button.js';
5
6
  import { card } from './lib/components/card.js';
6
7
  import { chart } from './lib/components/chart.js';
@@ -49,6 +50,7 @@ export declare const jux: {
49
50
  alert: typeof alert;
50
51
  app: typeof app;
51
52
  badge: typeof badge;
53
+ blueprint: typeof blueprint;
52
54
  button: typeof button;
53
55
  card: typeof card;
54
56
  chart: typeof chart;
package/index.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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"}
package/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { alert } from './lib/components/alert.js';
2
2
  import { app } from './lib/components/app.js';
3
3
  import { badge } from './lib/components/badge.js';
4
+ import { blueprint } from './lib/components/blueprint.js';
4
5
  import { button } from './lib/components/button.js';
5
6
  import { card } from './lib/components/card.js';
6
7
  import { chart } from './lib/components/chart.js';
@@ -52,6 +53,7 @@ export const jux = {
52
53
  alert,
53
54
  app,
54
55
  badge,
56
+ blueprint,
55
57
  button,
56
58
  card,
57
59
  chart,
@@ -0,0 +1,47 @@
1
+ import { BaseComponent, BaseState } from './base/BaseComponent.js';
2
+ export interface BlueprintOptions {
3
+ componentId?: string;
4
+ showClasses?: boolean;
5
+ showIds?: boolean;
6
+ showTags?: boolean;
7
+ expandAll?: boolean;
8
+ theme?: 'light' | 'dark' | 'auto';
9
+ interactive?: boolean;
10
+ style?: string;
11
+ class?: string;
12
+ }
13
+ interface BlueprintState extends BaseState {
14
+ componentId: string;
15
+ showClasses: boolean;
16
+ showIds: boolean;
17
+ showTags: boolean;
18
+ expandAll: boolean;
19
+ theme: string;
20
+ interactive: boolean;
21
+ selectedNode: string | null;
22
+ hoveredNode: string | null;
23
+ }
24
+ export declare class Blueprint extends BaseComponent<BlueprintState> {
25
+ private _domStructure;
26
+ constructor(id: string, options?: BlueprintOptions);
27
+ protected getTriggerEvents(): readonly string[];
28
+ protected getCallbackEvents(): readonly string[];
29
+ componentId(value: string): this;
30
+ showClasses(value: boolean): this;
31
+ showIds(value: boolean): this;
32
+ showTags(value: boolean): this;
33
+ expandAll(value: boolean): this;
34
+ theme(value: 'light' | 'dark' | 'auto'): this;
35
+ interactive(value: boolean): this;
36
+ selectNode(path: string): this;
37
+ private _analyzeDOMStructure;
38
+ private _renderNode;
39
+ private _renderTree;
40
+ update(prop: string, value: any): void;
41
+ private _rebuildTree;
42
+ private _updateNodeStates;
43
+ render(targetId?: string | HTMLElement | BaseComponent<any>): this;
44
+ }
45
+ export declare function blueprint(id: string, options?: BlueprintOptions): Blueprint;
46
+ export {};
47
+ //# sourceMappingURL=blueprint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blueprint.d.ts","sourceRoot":"","sources":["blueprint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAOnE,MAAM,WAAW,gBAAgB;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IAClC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,cAAe,SAAQ,SAAS;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,OAAO,CAAC;IACrB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAWD,qBAAa,SAAU,SAAQ,aAAa,CAAC,cAAc,CAAC;IACxD,OAAO,CAAC,aAAa,CAAiB;gBAE1B,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB;IAgBtD,SAAS,CAAC,gBAAgB,IAAI,SAAS,MAAM,EAAE;IAI/C,SAAS,CAAC,iBAAiB,IAAI,SAAS,MAAM,EAAE;IAQhD,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAKhC,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAKjC,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAK7B,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAK9B,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAK/B,KAAK,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI;IAK7C,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAKjC,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAU9B,OAAO,CAAC,oBAAoB;IAmC5B,OAAO,CAAC,WAAW;IAyFnB,OAAO,CAAC,WAAW;IAwBnB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI;IAiCtC,OAAO,CAAC,YAAY;IAmBpB,OAAO,CAAC,iBAAiB;IAiBzB,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,IAAI;CA8FrE;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,SAAS,CAE/E"}
@@ -0,0 +1,330 @@
1
+ import { BaseComponent } from './base/BaseComponent.js';
2
+ import { registry } from './registry.js';
3
+ // Event definitions
4
+ const TRIGGER_EVENTS = [];
5
+ const CALLBACK_EVENTS = ['select', 'hover'];
6
+ export class Blueprint extends BaseComponent {
7
+ constructor(id, options = {}) {
8
+ super(id, {
9
+ componentId: options.componentId ?? '',
10
+ showClasses: options.showClasses ?? true,
11
+ showIds: options.showIds ?? true,
12
+ showTags: options.showTags ?? true,
13
+ expandAll: options.expandAll ?? true,
14
+ theme: options.theme ?? 'auto',
15
+ interactive: options.interactive ?? true,
16
+ selectedNode: null,
17
+ hoveredNode: null,
18
+ style: options.style ?? '',
19
+ class: options.class ?? ''
20
+ });
21
+ this._domStructure = [];
22
+ }
23
+ getTriggerEvents() {
24
+ return TRIGGER_EVENTS;
25
+ }
26
+ getCallbackEvents() {
27
+ return CALLBACK_EVENTS;
28
+ }
29
+ /* ═════════════════════════════════════════════════════════════════
30
+ * FLUENT API
31
+ * ═════════════════════════════════════════════════════════════════ */
32
+ componentId(value) {
33
+ this.state.componentId = value;
34
+ return this;
35
+ }
36
+ showClasses(value) {
37
+ this.state.showClasses = value;
38
+ return this;
39
+ }
40
+ showIds(value) {
41
+ this.state.showIds = value;
42
+ return this;
43
+ }
44
+ showTags(value) {
45
+ this.state.showTags = value;
46
+ return this;
47
+ }
48
+ expandAll(value) {
49
+ this.state.expandAll = value;
50
+ return this;
51
+ }
52
+ theme(value) {
53
+ this.state.theme = value;
54
+ return this;
55
+ }
56
+ interactive(value) {
57
+ this.state.interactive = value;
58
+ return this;
59
+ }
60
+ selectNode(path) {
61
+ this.state.selectedNode = path;
62
+ this._triggerCallback('select', path);
63
+ return this;
64
+ }
65
+ /* ═════════════════════════════════════════════════════════════════
66
+ * DOM ANALYSIS
67
+ * ═════════════════════════════════════════════════════════════════ */
68
+ _analyzeDOMStructure(componentId) {
69
+ const component = registry.get(componentId);
70
+ if (!component || !component.container) {
71
+ return [];
72
+ }
73
+ const rootElement = component.container.querySelector(`#${componentId}`);
74
+ if (!rootElement) {
75
+ return [];
76
+ }
77
+ const extractNode = (element, depth = 0, path = '0') => {
78
+ const node = {
79
+ tag: element.tagName.toLowerCase(),
80
+ classes: Array.from(element.classList),
81
+ id: element.id || null,
82
+ depth,
83
+ path,
84
+ children: []
85
+ };
86
+ Array.from(element.children).forEach((child, index) => {
87
+ node.children.push(extractNode(child, depth + 1, `${path}.${index}`));
88
+ });
89
+ return node;
90
+ };
91
+ return [extractNode(rootElement)];
92
+ }
93
+ /* ═════════════════════════════════════════════════════════════════
94
+ * RENDER NODE
95
+ * ═════════════════════════════════════════════════════════════════ */
96
+ _renderNode(node, isLast = false, prefix = '') {
97
+ const { showClasses, showIds, showTags, interactive, selectedNode, hoveredNode } = this.state;
98
+ const nodeEl = document.createElement('div');
99
+ nodeEl.className = 'jux-blueprint-node';
100
+ nodeEl.dataset.path = node.path;
101
+ if (selectedNode === node.path) {
102
+ nodeEl.classList.add('jux-blueprint-node-selected');
103
+ }
104
+ if (hoveredNode === node.path) {
105
+ nodeEl.classList.add('jux-blueprint-node-hovered');
106
+ }
107
+ // Tree line
108
+ const lineEl = document.createElement('span');
109
+ lineEl.className = 'jux-blueprint-line';
110
+ const connector = isLast ? '└─' : '├─';
111
+ const indent = prefix + connector;
112
+ lineEl.textContent = indent;
113
+ nodeEl.appendChild(lineEl);
114
+ // Node content
115
+ const contentEl = document.createElement('span');
116
+ contentEl.className = 'jux-blueprint-content';
117
+ // Tag
118
+ if (showTags) {
119
+ const tagEl = document.createElement('span');
120
+ tagEl.className = 'jux-blueprint-tag';
121
+ tagEl.textContent = `<${node.tag}>`;
122
+ contentEl.appendChild(tagEl);
123
+ }
124
+ // ID
125
+ if (showIds && node.id) {
126
+ const idEl = document.createElement('span');
127
+ idEl.className = 'jux-blueprint-id';
128
+ idEl.textContent = `#${node.id}`;
129
+ contentEl.appendChild(idEl);
130
+ }
131
+ // Classes
132
+ if (showClasses && node.classes.length > 0) {
133
+ const classesEl = document.createElement('span');
134
+ classesEl.className = 'jux-blueprint-classes';
135
+ node.classes.forEach((cls, index) => {
136
+ const classEl = document.createElement('span');
137
+ classEl.className = 'jux-blueprint-class';
138
+ classEl.textContent = `.${cls}`;
139
+ classesEl.appendChild(classEl);
140
+ if (index < node.classes.length - 1) {
141
+ classesEl.appendChild(document.createTextNode(' '));
142
+ }
143
+ });
144
+ contentEl.appendChild(classesEl);
145
+ }
146
+ nodeEl.appendChild(contentEl);
147
+ // Interactive behavior
148
+ if (interactive) {
149
+ nodeEl.style.cursor = 'pointer';
150
+ nodeEl.addEventListener('click', (e) => {
151
+ e.stopPropagation();
152
+ this.state.selectedNode = node.path;
153
+ this._triggerCallback('select', node);
154
+ });
155
+ nodeEl.addEventListener('mouseenter', () => {
156
+ this.state.hoveredNode = node.path;
157
+ this._triggerCallback('hover', node);
158
+ });
159
+ nodeEl.addEventListener('mouseleave', () => {
160
+ if (this.state.hoveredNode === node.path) {
161
+ this.state.hoveredNode = null;
162
+ }
163
+ });
164
+ }
165
+ return nodeEl;
166
+ }
167
+ _renderTree(nodes, prefix = '') {
168
+ const treeEl = document.createElement('div');
169
+ treeEl.className = 'jux-blueprint-tree';
170
+ nodes.forEach((node, index) => {
171
+ const isLast = index === nodes.length - 1;
172
+ const nodeEl = this._renderNode(node, isLast, prefix);
173
+ treeEl.appendChild(nodeEl);
174
+ // Render children
175
+ if (node.children.length > 0) {
176
+ const childPrefix = prefix + (isLast ? ' ' : '│ ');
177
+ const childrenEl = this._renderTree(node.children, childPrefix);
178
+ treeEl.appendChild(childrenEl);
179
+ }
180
+ });
181
+ return treeEl;
182
+ }
183
+ /* ═════════════════════════════════════════════════════════════════
184
+ * REACTIVE UPDATE
185
+ * ═════════════════════════════════════════════════════════════════ */
186
+ update(prop, value) {
187
+ super.update(prop, value);
188
+ if (!this.container)
189
+ return;
190
+ const blueprint = this.container.querySelector(`#${this._id}`);
191
+ if (!blueprint)
192
+ return;
193
+ switch (prop) {
194
+ case 'componentId':
195
+ this._domStructure = this._analyzeDOMStructure(value);
196
+ this._rebuildTree();
197
+ break;
198
+ case 'showClasses':
199
+ case 'showIds':
200
+ case 'showTags':
201
+ case 'expandAll':
202
+ case 'interactive':
203
+ this._rebuildTree();
204
+ break;
205
+ case 'theme':
206
+ blueprint.dataset.theme = value;
207
+ break;
208
+ case 'selectedNode':
209
+ case 'hoveredNode':
210
+ this._updateNodeStates();
211
+ break;
212
+ }
213
+ }
214
+ _rebuildTree() {
215
+ if (!this.container)
216
+ return;
217
+ const treeContainer = this.container.querySelector('.jux-blueprint-tree-container');
218
+ if (!treeContainer)
219
+ return;
220
+ treeContainer.innerHTML = '';
221
+ if (this._domStructure.length > 0) {
222
+ const treeEl = this._renderTree(this._domStructure);
223
+ treeContainer.appendChild(treeEl);
224
+ }
225
+ else {
226
+ const emptyEl = document.createElement('div');
227
+ emptyEl.className = 'jux-blueprint-empty';
228
+ emptyEl.textContent = 'No component selected or component not found';
229
+ treeContainer.appendChild(emptyEl);
230
+ }
231
+ }
232
+ _updateNodeStates() {
233
+ if (!this.container)
234
+ return;
235
+ const nodes = this.container.querySelectorAll('.jux-blueprint-node');
236
+ nodes.forEach(node => {
237
+ const el = node;
238
+ const path = el.dataset.path;
239
+ el.classList.toggle('jux-blueprint-node-selected', path === this.state.selectedNode);
240
+ el.classList.toggle('jux-blueprint-node-hovered', path === this.state.hoveredNode);
241
+ });
242
+ }
243
+ /* ═════════════════════════════════════════════════════════════════
244
+ * RENDER
245
+ * ═════════════════════════════════════════════════════════════════ */
246
+ render(targetId) {
247
+ const container = this._setupContainer(targetId);
248
+ const { componentId, theme, style, class: className } = this.state;
249
+ // Analyze structure if componentId is provided
250
+ if (componentId) {
251
+ this._domStructure = this._analyzeDOMStructure(componentId);
252
+ }
253
+ // Create wrapper
254
+ const wrapper = document.createElement('div');
255
+ wrapper.className = 'jux-blueprint';
256
+ wrapper.id = this._id;
257
+ wrapper.dataset.theme = theme;
258
+ if (className)
259
+ wrapper.className += ` ${className}`;
260
+ if (style)
261
+ wrapper.setAttribute('style', style);
262
+ // Header
263
+ const header = document.createElement('div');
264
+ header.className = 'jux-blueprint-header';
265
+ const title = document.createElement('h3');
266
+ title.className = 'jux-blueprint-title';
267
+ title.textContent = componentId ? `Blueprint: ${componentId}` : 'Component Blueprint';
268
+ header.appendChild(title);
269
+ // Controls
270
+ const controls = document.createElement('div');
271
+ controls.className = 'jux-blueprint-controls';
272
+ const toggles = [
273
+ { prop: 'showTags', label: 'Tags' },
274
+ { prop: 'showIds', label: 'IDs' },
275
+ { prop: 'showClasses', label: 'Classes' }
276
+ ];
277
+ toggles.forEach(({ prop, label }) => {
278
+ const toggleEl = document.createElement('label');
279
+ toggleEl.className = 'jux-blueprint-toggle';
280
+ const checkbox = document.createElement('input');
281
+ checkbox.type = 'checkbox';
282
+ checkbox.checked = this.state[prop];
283
+ checkbox.addEventListener('change', () => {
284
+ this.state[prop] = checkbox.checked;
285
+ });
286
+ toggleEl.appendChild(checkbox);
287
+ toggleEl.appendChild(document.createTextNode(` ${label}`));
288
+ controls.appendChild(toggleEl);
289
+ });
290
+ header.appendChild(controls);
291
+ wrapper.appendChild(header);
292
+ // Tree container
293
+ const treeContainer = document.createElement('div');
294
+ treeContainer.className = 'jux-blueprint-tree-container';
295
+ if (this._domStructure.length > 0) {
296
+ const treeEl = this._renderTree(this._domStructure);
297
+ treeContainer.appendChild(treeEl);
298
+ }
299
+ else {
300
+ const emptyEl = document.createElement('div');
301
+ emptyEl.className = 'jux-blueprint-empty';
302
+ emptyEl.textContent = componentId
303
+ ? `Component "${componentId}" not found or not rendered`
304
+ : 'Set componentId to analyze a component';
305
+ treeContainer.appendChild(emptyEl);
306
+ }
307
+ wrapper.appendChild(treeContainer);
308
+ // Legend
309
+ const legend = document.createElement('div');
310
+ legend.className = 'jux-blueprint-legend';
311
+ legend.innerHTML = `
312
+ <span class="jux-blueprint-legend-item">
313
+ <span class="jux-blueprint-tag">&lt;tag&gt;</span> HTML Element
314
+ </span>
315
+ <span class="jux-blueprint-legend-item">
316
+ <span class="jux-blueprint-id">#id</span> Element ID
317
+ </span>
318
+ <span class="jux-blueprint-legend-item">
319
+ <span class="jux-blueprint-class">.class</span> CSS Class
320
+ </span>
321
+ `;
322
+ wrapper.appendChild(legend);
323
+ this._wireStandardEvents(wrapper);
324
+ container.appendChild(wrapper);
325
+ return this;
326
+ }
327
+ }
328
+ export function blueprint(id, options = {}) {
329
+ return new Blueprint(id, options);
330
+ }
@@ -0,0 +1,432 @@
1
+ import { BaseComponent, BaseState } from './base/BaseComponent.js';
2
+ import { registry } from './registry.js';
3
+
4
+ // Event definitions
5
+ const TRIGGER_EVENTS = [] as const;
6
+ const CALLBACK_EVENTS = ['select', 'hover'] as const;
7
+
8
+ export interface BlueprintOptions {
9
+ componentId?: string;
10
+ showClasses?: boolean;
11
+ showIds?: boolean;
12
+ showTags?: boolean;
13
+ expandAll?: boolean;
14
+ theme?: 'light' | 'dark' | 'auto';
15
+ interactive?: boolean;
16
+ style?: string;
17
+ class?: string;
18
+ }
19
+
20
+ interface BlueprintState extends BaseState {
21
+ componentId: string;
22
+ showClasses: boolean;
23
+ showIds: boolean;
24
+ showTags: boolean;
25
+ expandAll: boolean;
26
+ theme: string;
27
+ interactive: boolean;
28
+ selectedNode: string | null;
29
+ hoveredNode: string | null;
30
+ }
31
+
32
+ interface DOMNode {
33
+ tag: string;
34
+ classes: string[];
35
+ id: string | null;
36
+ depth: number;
37
+ path: string;
38
+ children: DOMNode[];
39
+ }
40
+
41
+ export class Blueprint extends BaseComponent<BlueprintState> {
42
+ private _domStructure: DOMNode[] = [];
43
+
44
+ constructor(id: string, options: BlueprintOptions = {}) {
45
+ super(id, {
46
+ componentId: options.componentId ?? '',
47
+ showClasses: options.showClasses ?? true,
48
+ showIds: options.showIds ?? true,
49
+ showTags: options.showTags ?? true,
50
+ expandAll: options.expandAll ?? true,
51
+ theme: options.theme ?? 'auto',
52
+ interactive: options.interactive ?? true,
53
+ selectedNode: null,
54
+ hoveredNode: null,
55
+ style: options.style ?? '',
56
+ class: options.class ?? ''
57
+ });
58
+ }
59
+
60
+ protected getTriggerEvents(): readonly string[] {
61
+ return TRIGGER_EVENTS;
62
+ }
63
+
64
+ protected getCallbackEvents(): readonly string[] {
65
+ return CALLBACK_EVENTS;
66
+ }
67
+
68
+ /* ═════════════════════════════════════════════════════════════════
69
+ * FLUENT API
70
+ * ═════════════════════════════════════════════════════════════════ */
71
+
72
+ componentId(value: string): this {
73
+ this.state.componentId = value;
74
+ return this;
75
+ }
76
+
77
+ showClasses(value: boolean): this {
78
+ this.state.showClasses = value;
79
+ return this;
80
+ }
81
+
82
+ showIds(value: boolean): this {
83
+ this.state.showIds = value;
84
+ return this;
85
+ }
86
+
87
+ showTags(value: boolean): this {
88
+ this.state.showTags = value;
89
+ return this;
90
+ }
91
+
92
+ expandAll(value: boolean): this {
93
+ this.state.expandAll = value;
94
+ return this;
95
+ }
96
+
97
+ theme(value: 'light' | 'dark' | 'auto'): this {
98
+ this.state.theme = value;
99
+ return this;
100
+ }
101
+
102
+ interactive(value: boolean): this {
103
+ this.state.interactive = value;
104
+ return this;
105
+ }
106
+
107
+ selectNode(path: string): this {
108
+ this.state.selectedNode = path;
109
+ this._triggerCallback('select', path);
110
+ return this;
111
+ }
112
+
113
+ /* ═════════════════════════════════════════════════════════════════
114
+ * DOM ANALYSIS
115
+ * ═════════════════════════════════════════════════════════════════ */
116
+
117
+ private _analyzeDOMStructure(componentId: string): DOMNode[] {
118
+ const component = registry.get(componentId);
119
+ if (!component || !component.container) {
120
+ return [];
121
+ }
122
+
123
+ const rootElement = component.container.querySelector(`#${componentId}`);
124
+ if (!rootElement) {
125
+ return [];
126
+ }
127
+
128
+ const extractNode = (element: Element, depth: number = 0, path: string = '0'): DOMNode => {
129
+ const node: DOMNode = {
130
+ tag: element.tagName.toLowerCase(),
131
+ classes: Array.from(element.classList),
132
+ id: element.id || null,
133
+ depth,
134
+ path,
135
+ children: []
136
+ };
137
+
138
+ Array.from(element.children).forEach((child, index) => {
139
+ node.children.push(extractNode(child, depth + 1, `${path}.${index}`));
140
+ });
141
+
142
+ return node;
143
+ };
144
+
145
+ return [extractNode(rootElement)];
146
+ }
147
+
148
+ /* ═════════════════════════════════════════════════════════════════
149
+ * RENDER NODE
150
+ * ═════════════════════════════════════════════════════════════════ */
151
+
152
+ private _renderNode(node: DOMNode, isLast: boolean = false, prefix: string = ''): HTMLElement {
153
+ const { showClasses, showIds, showTags, interactive, selectedNode, hoveredNode } = this.state;
154
+
155
+ const nodeEl = document.createElement('div');
156
+ nodeEl.className = 'jux-blueprint-node';
157
+ nodeEl.dataset.path = node.path;
158
+
159
+ if (selectedNode === node.path) {
160
+ nodeEl.classList.add('jux-blueprint-node-selected');
161
+ }
162
+ if (hoveredNode === node.path) {
163
+ nodeEl.classList.add('jux-blueprint-node-hovered');
164
+ }
165
+
166
+ // Tree line
167
+ const lineEl = document.createElement('span');
168
+ lineEl.className = 'jux-blueprint-line';
169
+
170
+ const connector = isLast ? '└─' : '├─';
171
+ const indent = prefix + connector;
172
+ lineEl.textContent = indent;
173
+ nodeEl.appendChild(lineEl);
174
+
175
+ // Node content
176
+ const contentEl = document.createElement('span');
177
+ contentEl.className = 'jux-blueprint-content';
178
+
179
+ // Tag
180
+ if (showTags) {
181
+ const tagEl = document.createElement('span');
182
+ tagEl.className = 'jux-blueprint-tag';
183
+ tagEl.textContent = `<${node.tag}>`;
184
+ contentEl.appendChild(tagEl);
185
+ }
186
+
187
+ // ID
188
+ if (showIds && node.id) {
189
+ const idEl = document.createElement('span');
190
+ idEl.className = 'jux-blueprint-id';
191
+ idEl.textContent = `#${node.id}`;
192
+ contentEl.appendChild(idEl);
193
+ }
194
+
195
+ // Classes
196
+ if (showClasses && node.classes.length > 0) {
197
+ const classesEl = document.createElement('span');
198
+ classesEl.className = 'jux-blueprint-classes';
199
+
200
+ node.classes.forEach((cls, index) => {
201
+ const classEl = document.createElement('span');
202
+ classEl.className = 'jux-blueprint-class';
203
+ classEl.textContent = `.${cls}`;
204
+ classesEl.appendChild(classEl);
205
+
206
+ if (index < node.classes.length - 1) {
207
+ classesEl.appendChild(document.createTextNode(' '));
208
+ }
209
+ });
210
+
211
+ contentEl.appendChild(classesEl);
212
+ }
213
+
214
+ nodeEl.appendChild(contentEl);
215
+
216
+ // Interactive behavior
217
+ if (interactive) {
218
+ nodeEl.style.cursor = 'pointer';
219
+
220
+ nodeEl.addEventListener('click', (e) => {
221
+ e.stopPropagation();
222
+ this.state.selectedNode = node.path;
223
+ this._triggerCallback('select', node);
224
+ });
225
+
226
+ nodeEl.addEventListener('mouseenter', () => {
227
+ this.state.hoveredNode = node.path;
228
+ this._triggerCallback('hover', node);
229
+ });
230
+
231
+ nodeEl.addEventListener('mouseleave', () => {
232
+ if (this.state.hoveredNode === node.path) {
233
+ this.state.hoveredNode = null;
234
+ }
235
+ });
236
+ }
237
+
238
+ return nodeEl;
239
+ }
240
+
241
+ private _renderTree(nodes: DOMNode[], prefix: string = ''): HTMLElement {
242
+ const treeEl = document.createElement('div');
243
+ treeEl.className = 'jux-blueprint-tree';
244
+
245
+ nodes.forEach((node, index) => {
246
+ const isLast = index === nodes.length - 1;
247
+ const nodeEl = this._renderNode(node, isLast, prefix);
248
+ treeEl.appendChild(nodeEl);
249
+
250
+ // Render children
251
+ if (node.children.length > 0) {
252
+ const childPrefix = prefix + (isLast ? ' ' : '│ ');
253
+ const childrenEl = this._renderTree(node.children, childPrefix);
254
+ treeEl.appendChild(childrenEl);
255
+ }
256
+ });
257
+
258
+ return treeEl;
259
+ }
260
+
261
+ /* ═════════════════════════════════════════════════════════════════
262
+ * REACTIVE UPDATE
263
+ * ═════════════════════════════════════════════════════════════════ */
264
+
265
+ update(prop: string, value: any): void {
266
+ super.update(prop, value);
267
+
268
+ if (!this.container) return;
269
+
270
+ const blueprint = this.container.querySelector(`#${this._id}`) as HTMLElement;
271
+ if (!blueprint) return;
272
+
273
+ switch (prop) {
274
+ case 'componentId':
275
+ this._domStructure = this._analyzeDOMStructure(value);
276
+ this._rebuildTree();
277
+ break;
278
+
279
+ case 'showClasses':
280
+ case 'showIds':
281
+ case 'showTags':
282
+ case 'expandAll':
283
+ case 'interactive':
284
+ this._rebuildTree();
285
+ break;
286
+
287
+ case 'theme':
288
+ blueprint.dataset.theme = value;
289
+ break;
290
+
291
+ case 'selectedNode':
292
+ case 'hoveredNode':
293
+ this._updateNodeStates();
294
+ break;
295
+ }
296
+ }
297
+
298
+ private _rebuildTree(): void {
299
+ if (!this.container) return;
300
+
301
+ const treeContainer = this.container.querySelector('.jux-blueprint-tree-container');
302
+ if (!treeContainer) return;
303
+
304
+ treeContainer.innerHTML = '';
305
+
306
+ if (this._domStructure.length > 0) {
307
+ const treeEl = this._renderTree(this._domStructure);
308
+ treeContainer.appendChild(treeEl);
309
+ } else {
310
+ const emptyEl = document.createElement('div');
311
+ emptyEl.className = 'jux-blueprint-empty';
312
+ emptyEl.textContent = 'No component selected or component not found';
313
+ treeContainer.appendChild(emptyEl);
314
+ }
315
+ }
316
+
317
+ private _updateNodeStates(): void {
318
+ if (!this.container) return;
319
+
320
+ const nodes = this.container.querySelectorAll('.jux-blueprint-node');
321
+ nodes.forEach(node => {
322
+ const el = node as HTMLElement;
323
+ const path = el.dataset.path;
324
+
325
+ el.classList.toggle('jux-blueprint-node-selected', path === this.state.selectedNode);
326
+ el.classList.toggle('jux-blueprint-node-hovered', path === this.state.hoveredNode);
327
+ });
328
+ }
329
+
330
+ /* ═════════════════════════════════════════════════════════════════
331
+ * RENDER
332
+ * ═════════════════════════════════════════════════════════════════ */
333
+
334
+ render(targetId?: string | HTMLElement | BaseComponent<any>): this {
335
+ const container = this._setupContainer(targetId);
336
+ const { componentId, theme, style, class: className } = this.state;
337
+
338
+ // Analyze structure if componentId is provided
339
+ if (componentId) {
340
+ this._domStructure = this._analyzeDOMStructure(componentId);
341
+ }
342
+
343
+ // Create wrapper
344
+ const wrapper = document.createElement('div');
345
+ wrapper.className = 'jux-blueprint';
346
+ wrapper.id = this._id;
347
+ wrapper.dataset.theme = theme;
348
+ if (className) wrapper.className += ` ${className}`;
349
+ if (style) wrapper.setAttribute('style', style);
350
+
351
+ // Header
352
+ const header = document.createElement('div');
353
+ header.className = 'jux-blueprint-header';
354
+
355
+ const title = document.createElement('h3');
356
+ title.className = 'jux-blueprint-title';
357
+ title.textContent = componentId ? `Blueprint: ${componentId}` : 'Component Blueprint';
358
+ header.appendChild(title);
359
+
360
+ // Controls
361
+ const controls = document.createElement('div');
362
+ controls.className = 'jux-blueprint-controls';
363
+
364
+ const toggles = [
365
+ { prop: 'showTags', label: 'Tags' },
366
+ { prop: 'showIds', label: 'IDs' },
367
+ { prop: 'showClasses', label: 'Classes' }
368
+ ];
369
+
370
+ toggles.forEach(({ prop, label }) => {
371
+ const toggleEl = document.createElement('label');
372
+ toggleEl.className = 'jux-blueprint-toggle';
373
+
374
+ const checkbox = document.createElement('input');
375
+ checkbox.type = 'checkbox';
376
+ checkbox.checked = (this.state as any)[prop];
377
+ checkbox.addEventListener('change', () => {
378
+ (this.state as any)[prop] = checkbox.checked;
379
+ });
380
+
381
+ toggleEl.appendChild(checkbox);
382
+ toggleEl.appendChild(document.createTextNode(` ${label}`));
383
+ controls.appendChild(toggleEl);
384
+ });
385
+
386
+ header.appendChild(controls);
387
+ wrapper.appendChild(header);
388
+
389
+ // Tree container
390
+ const treeContainer = document.createElement('div');
391
+ treeContainer.className = 'jux-blueprint-tree-container';
392
+
393
+ if (this._domStructure.length > 0) {
394
+ const treeEl = this._renderTree(this._domStructure);
395
+ treeContainer.appendChild(treeEl);
396
+ } else {
397
+ const emptyEl = document.createElement('div');
398
+ emptyEl.className = 'jux-blueprint-empty';
399
+ emptyEl.textContent = componentId
400
+ ? `Component "${componentId}" not found or not rendered`
401
+ : 'Set componentId to analyze a component';
402
+ treeContainer.appendChild(emptyEl);
403
+ }
404
+
405
+ wrapper.appendChild(treeContainer);
406
+
407
+ // Legend
408
+ const legend = document.createElement('div');
409
+ legend.className = 'jux-blueprint-legend';
410
+ legend.innerHTML = `
411
+ <span class="jux-blueprint-legend-item">
412
+ <span class="jux-blueprint-tag">&lt;tag&gt;</span> HTML Element
413
+ </span>
414
+ <span class="jux-blueprint-legend-item">
415
+ <span class="jux-blueprint-id">#id</span> Element ID
416
+ </span>
417
+ <span class="jux-blueprint-legend-item">
418
+ <span class="jux-blueprint-class">.class</span> CSS Class
419
+ </span>
420
+ `;
421
+ wrapper.appendChild(legend);
422
+
423
+ this._wireStandardEvents(wrapper);
424
+ container.appendChild(wrapper);
425
+
426
+ return this;
427
+ }
428
+ }
429
+
430
+ export function blueprint(id: string, options: BlueprintOptions = {}): Blueprint {
431
+ return new Blueprint(id, options);
432
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "juxscript",
3
- "version": "1.1.63",
3
+ "version": "1.1.64",
4
4
  "type": "module",
5
5
  "description": "A JavaScript UX authorship platform",
6
6
  "main": "index.js",
@@ -47,7 +47,8 @@
47
47
  "build": "tsc",
48
48
  "dev": "tsc --watch",
49
49
  "prepublishOnly": "npm run build",
50
- "postpublish": "echo '✅ Published successfully. Verify with: npm info juxscript'"
50
+ "postpublish": "echo '✅ Published successfully. Verify with: npm info juxscript'",
51
+ "scan-css": "node scripts/scan-css-classes.js"
51
52
  },
52
53
  "dependencies": {
53
54
  "acorn": "^8.15.0",