juxscript 1.0.68 → 1.0.69

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/cli.js CHANGED
@@ -11,7 +11,7 @@ const command = process.argv[2];
11
11
  function scaffoldProject(targetDir, packageRoot) {
12
12
  const juxDir = path.join(targetDir, 'jux');
13
13
  if (!fs.existsSync(juxDir)) fs.mkdirSync(juxDir, { recursive: true });
14
- const presetDir = path.join(packageRoot, 'presets', 'default');
14
+ const presetDir = path.join(packageRoot, 'create');
15
15
  let copied = 0;
16
16
  if (fs.existsSync(presetDir)) {
17
17
  fs.readdirSync(presetDir).forEach(file => {
package/create/index.jux CHANGED
@@ -0,0 +1,79 @@
1
+ import { Element, List } from 'juxscript';
2
+ import { LandingLayout } from './layout.jux';
3
+
4
+ // 1. Initialize Layout
5
+ LandingLayout();
6
+
7
+ // 2. Header: Logo & Nav
8
+ Element('logo', { tag: 'h2' })
9
+ .text('JUX STUDIO')
10
+ .style('margin: 0; padding: 25px 40px; font-weight: 800; letter-spacing: -1px; display: flex; align-items: center; height: 100%; box-sizing: border-box;')
11
+ .render('landing-layout-0-0');
12
+
13
+ // 3. Hero Section
14
+ const hero = Element('hero-content', { tag: 'div' })
15
+ .style('display: flex; flex-direction: column; justify-content: center; align-items: center; height: 100%; text-align: center; background: #f8f9fa;')
16
+ .render('landing-layout-1-0');
17
+
18
+ Element('hero-h1', { tag: 'h1' })
19
+ .text('Build Web Apps. Simply.')
20
+ .style('font-size: 4rem; margin: 0 0 20px 0; font-weight: 900; background: linear-gradient(45deg, #111, #555); -webkit-background-clip: text; -webkit-text-fill-color: transparent;')
21
+ .render('hero-content');
22
+
23
+ Element('hero-p', { tag: 'p' })
24
+ .text('No complex build steps. No magic. Just standard JavaScript components.')
25
+ .style('font-size: 1.5rem; color: #666; max-width: 600px; line-height: 1.5;')
26
+ .render('hero-content');
27
+
28
+ Element('cta-btn', { tag: 'button' })
29
+ .text('Get Started')
30
+ .style('margin-top: 30px; padding: 15px 40px; font-size: 1.2rem; background: #000; color: #fff; border: none; border-radius: 50px; cursor: pointer; transition: transform 0.2s;')
31
+ .render('hero-content');
32
+
33
+ // 4. Features Section (Nested Content)
34
+ const featuresWrapper = Element('features-wrapper', { tag: 'div' })
35
+ .style('max-width: 1000px; margin: 60px auto; display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 40px; padding: 0 20px;')
36
+ .render('landing-layout-2-0');
37
+
38
+ // Component Showcase within Landing Page
39
+ const listDemo = Element('list-demo-card', { tag: 'div' })
40
+ .style('background: #fff; padding: 30px; border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,0.05); border: 1px solid #eee;')
41
+ .render('features-wrapper');
42
+
43
+ Element('list-label', { tag: 'h3' }).text('Interactive Components').render('list-demo-card');
44
+ Element('list-desc', { tag: 'p' }).text('Try dragging or sorting this list:').style('color:#666; margin-bottom:20px;').render('list-demo-card');
45
+
46
+ List('feature-list', { items: ['Zero Config', 'Standard Web APIs', 'Hot Reloading', 'TypeScript Ready'] })
47
+ .listType('unordered')
48
+ .enableSort()
49
+ .enableMove()
50
+ .render('list-demo-card');
51
+
52
+ // Feature Cards
53
+ const cardData = [
54
+ { title: 'Reactive State', desc: 'Granular binding means high performance without a virtual DOM.' },
55
+ { title: 'Time Travel', desc: 'Built-in undo/redo capabilities for complex application state.' }
56
+ ];
57
+
58
+ cardData.forEach((f, i) => {
59
+ const card = Element(`card-${i}`, { tag: 'div' })
60
+ .style('background: #fff; padding: 30px; border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,0.05); border: 1px solid #eee;')
61
+ .render('features-wrapper');
62
+
63
+ Element(`ct-${i}`, { tag: 'h3' }).text(f.title).render(`card-${i}`);
64
+ Element(`cd-${i}`, { tag: 'p' }).text(f.desc).style('color: #666; line-height: 1.6;').render(`card-${i}`);
65
+ });
66
+
67
+ // 5. Footer
68
+ const footer = Element('footer-content', { tag: 'div' })
69
+ .html('© 2025 Jux Project. Open Source.')
70
+ .style('display:flex; align-items:center; justify-content:center; height:100%; background:#111; color:#666;')
71
+ .render('landing-layout-3-0');
72
+
73
+ // 6. Global Reset (injected via Element)
74
+ Element('reset-styles', { tag: 'style' })
75
+ .text(`
76
+ body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; background: #fff; color: #111; }
77
+ button:hover { transform: scale(1.05); }
78
+ `)
79
+ .render('app');
package/create/layout.jux CHANGED
@@ -1,4 +1,4 @@
1
- import { jux } from 'juxscript';
1
+ import { Element, Grid } from 'juxscript';
2
2
 
3
3
  // Import the layout styles - testing link.
4
4
 
@@ -9,32 +9,35 @@ import { jux } from 'juxscript';
9
9
  // Note: #app is automatically created by the Jux compiler
10
10
 
11
11
  export function initializeGrid() {
12
- // add the base jux styles
13
- jux.include('style.css');
12
+ // add the base jux styles via style tag import
13
+ Element('layout-css', { tag: 'style' })
14
+ .text('@import "style.css";')
15
+ .render(document.head);
16
+
14
17
  // Header area
15
- const appHeader = jux.container('appheader').render('#app');
16
- const appHeaderContent = jux.container('appheader-content').render('#appheader');
17
- const appHeaderLogo = jux.container('appheader-logo').render('#appheader-content');
18
- const appHeaderNav = jux.container('appheader-nav').render('#appheader-content');
19
- const appHeaderActions = jux.container('appheader-actions').render('#appheader-content');
18
+ const appHeader = Element('appheader').render('#app');
19
+ const appHeaderContent = Element('appheader-content').render('#appheader');
20
+ const appHeaderLogo = Element('appheader-logo').render('#appheader-content');
21
+ const appHeaderNav = Element('appheader-nav').render('#appheader-content');
22
+ const appHeaderActions = Element('appheader-actions').render('#appheader-content');
20
23
 
21
24
  // Left sidebar
22
- const appAside = jux.container('appaside').render('#app');
25
+ const appAside = Element('appaside').render('#app');
23
26
 
24
27
  // Main content area
25
- const appMain = jux.container('appmain').render('#app');
26
- const appMainContent = jux.container('appmain-content').render('#appmain');
28
+ const appMain = Element('appmain').render('#app');
29
+ const appMainContent = Element('appmain-content').render('#appmain');
27
30
 
28
31
  // Right sidebar (optional - starts hidden)
29
- const appSidebar = jux.container('appsidebar').render('#app');
30
- const appSidebarHeader = jux.container('appsidebar-header').render('#appsidebar');
31
- const appSidebarContent = jux.container('appsidebar-content').render('#appsidebar');
32
- const appSidebarFooter = jux.container('appsidebar-footer').render('#appsidebar');
32
+ const appSidebar = Element('appsidebar').render('#app');
33
+ const appSidebarHeader = Element('appsidebar-header').render('#appsidebar');
34
+ const appSidebarContent = Element('appsidebar-content').render('#appsidebar');
35
+ const appSidebarFooter = Element('appsidebar-footer').render('#appsidebar');
33
36
 
34
37
  // Footer area
35
- const appFooter = jux.container('appfooter').render('#app');
36
- const appFooterContent = jux.container('appfooter-content').render('#appfooter');
37
- const appFooterLegal = jux.container('appfooter-legal').render('#appfooter-content');
38
+ const appFooter = Element('appfooter').render('#app');
39
+ const appFooterContent = Element('appfooter-content').render('#appfooter');
40
+ const appFooterLegal = Element('appfooter-legal').render('#appfooter-content');
38
41
 
39
42
  // Return references to all containers
40
43
  return {
@@ -54,4 +57,21 @@ export function initializeGrid() {
54
57
  appFooterContent,
55
58
  appFooterLegal
56
59
  };
60
+ }
61
+
62
+ export function LandingLayout() {
63
+ // Defines a classic landing page structure using JUX Grid
64
+ return Grid('landing-layout')
65
+ .rows([
66
+ { size: '80px', class: 'layout-header' }, // 0: Navbar
67
+ { size: '500px', class: 'layout-hero' }, // 1: Hero Banner
68
+ { size: 'auto', class: 'layout-features' }, // 2: Main Content
69
+ { size: '120px', class: 'layout-footer' } // 3: Footer
70
+ ])
71
+ .columns([
72
+ { size: '1fr', class: 'col-main' } // Single column flow
73
+ ])
74
+ .gap('0px')
75
+ .width('100%')
76
+ .render('app');
57
77
  }
@@ -13,13 +13,14 @@ export class ElementEngine extends BaseEngine {
13
13
  attributes: {},
14
14
  tagName: options.tag || 'div',
15
15
  content: options.html || options.text || '',
16
- contentType: options.html ? 'html' : 'text'
16
+ contentType: options.html ? 'html' : 'text',
17
+ inlineStyle: options.style || '' // ✅ Initialize logic
17
18
  };
18
19
  }
19
20
  /**
20
21
  * Set the HTML Tag name (e.g. 'div', 'span', 'h1', 'section')
21
22
  */
22
- tagType(tagName) {
23
+ tag(tagName) {
23
24
  this.updateState({ tagName });
24
25
  this.emit('config', { tagName });
25
26
  return this;
@@ -47,5 +48,19 @@ export class ElementEngine extends BaseEngine {
47
48
  this.updateState({ content: current + value, contentType: 'html' });
48
49
  return this;
49
50
  }
51
+ /**
52
+ * Set inline styles (e.g. "color: red; margin: 10px;")
53
+ */
54
+ style(cssText) {
55
+ this.updateState({ inlineStyle: cssText });
56
+ return this;
57
+ }
58
+ /**
59
+ * Receives RAW DOM events from the Skin and emits them to listeners.
60
+ * This keeps the 'emit' logic encapsulated within the Engine.
61
+ */
62
+ handleEvent(eventName, content) {
63
+ this.emit(eventName, content);
64
+ }
50
65
  }
51
66
  //# sourceMappingURL=engine.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"engine.js","sourceRoot":"","sources":["engine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,uBAAuB,CAAC;AAe9D,MAAM,OAAO,aAAc,SAAQ,UAAwC;IACvE,YAAY,EAAU,EAAE,UAA0B,EAAE;QAChD,KAAK,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;IACvB,CAAC;IAES,YAAY,CAAC,EAAU,EAAE,OAAuB;QACtD,OAAO;YACH,EAAE;YACF,OAAO,EAAE,CAAC,aAAa,CAAC;YACxB,OAAO,EAAE,IAAI;YACb,QAAQ,EAAE,KAAK;YACf,OAAO,EAAE,KAAK;YACd,UAAU,EAAE,EAAE;YACd,OAAO,EAAE,OAAO,CAAC,GAAG,IAAI,KAAK;YAC7B,OAAO,EAAE,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,IAAI,EAAE;YAC3C,WAAW,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM;SAC9C,CAAC;IACN,CAAC;IAED;;OAEG;IACH,OAAO,CAAC,OAAe;QACnB,IAAI,CAAC,WAAW,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;QAC9B,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;QACjC,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;OAEG;IACH,IAAI,CAAC,KAAa;QACd,IAAI,CAAC,WAAW,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC;QAC1D,8CAA8C;QAC9C,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;OAEG;IACH,IAAI,CAAC,KAAa;QACd,IAAI,CAAC,WAAW,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC;QAC1D,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;OAEG;IACH,UAAU,CAAC,KAAa;QACpB,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC;QACzC,IAAI,CAAC,WAAW,CAAC,EAAE,OAAO,EAAE,OAAO,GAAG,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC;QACpE,OAAO,IAAI,CAAC;IAChB,CAAC;CACJ"}
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["engine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,uBAAuB,CAAC;AAiB9D,MAAM,OAAO,aAAc,SAAQ,UAAwC;IACvE,YAAY,EAAU,EAAE,UAA0B,EAAE;QAChD,KAAK,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;IACvB,CAAC;IAES,YAAY,CAAC,EAAU,EAAE,OAAuB;QACtD,OAAO;YACH,EAAE;YACF,OAAO,EAAE,CAAC,aAAa,CAAC;YACxB,OAAO,EAAE,IAAI;YACb,QAAQ,EAAE,KAAK;YACf,OAAO,EAAE,KAAK;YACd,UAAU,EAAE,EAAE;YACd,OAAO,EAAE,OAAO,CAAC,GAAG,IAAI,KAAK;YAC7B,OAAO,EAAE,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,IAAI,EAAE;YAC3C,WAAW,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM;YAC3C,WAAW,EAAE,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC,qBAAqB;SACzD,CAAC;IACN,CAAC;IAED;;OAEG;IACH,GAAG,CAAC,OAAe;QACf,IAAI,CAAC,WAAW,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;QAC9B,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;QACjC,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;OAEG;IACH,IAAI,CAAC,KAAa;QACd,IAAI,CAAC,WAAW,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC;QAC1D,8CAA8C;QAC9C,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;OAEG;IACH,IAAI,CAAC,KAAa;QACd,IAAI,CAAC,WAAW,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC;QAC1D,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;OAEG;IACH,UAAU,CAAC,KAAa;QACpB,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC;QACzC,IAAI,CAAC,WAAW,CAAC,EAAE,OAAO,EAAE,OAAO,GAAG,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC;QACpE,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAe;QACjB,IAAI,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC;QAC3C,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;;OAGG;IACH,WAAW,CAAC,SAAiB,EAAE,OAAY;QACvC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAClC,CAAC;CACJ"}
@@ -4,6 +4,7 @@ export interface ElementState extends BaseState {
4
4
  tagName: string;
5
5
  content: string | null;
6
6
  contentType: 'text' | 'html';
7
+ inlineStyle: string; // ✅ Added inlineStyle to state
7
8
  // BaseState provides: classes, attributes, visible, etc.
8
9
  }
9
10
 
@@ -11,6 +12,7 @@ export interface ElementOptions {
11
12
  tag?: string;
12
13
  text?: string;
13
14
  html?: string;
15
+ style?: string; // ✅ Added style option
14
16
  }
15
17
 
16
18
  export class ElementEngine extends BaseEngine<ElementState, ElementOptions> {
@@ -28,14 +30,15 @@ export class ElementEngine extends BaseEngine<ElementState, ElementOptions> {
28
30
  attributes: {},
29
31
  tagName: options.tag || 'div',
30
32
  content: options.html || options.text || '',
31
- contentType: options.html ? 'html' : 'text'
33
+ contentType: options.html ? 'html' : 'text',
34
+ inlineStyle: options.style || '' // ✅ Initialize logic
32
35
  };
33
36
  }
34
37
 
35
38
  /**
36
39
  * Set the HTML Tag name (e.g. 'div', 'span', 'h1', 'section')
37
40
  */
38
- tagType(tagName: string): this {
41
+ tag(tagName: string): this {
39
42
  this.updateState({ tagName });
40
43
  this.emit('config', { tagName });
41
44
  return this;
@@ -66,4 +69,20 @@ export class ElementEngine extends BaseEngine<ElementState, ElementOptions> {
66
69
  this.updateState({ content: current + value, contentType: 'html' });
67
70
  return this;
68
71
  }
72
+
73
+ /**
74
+ * Set inline styles (e.g. "color: red; margin: 10px;")
75
+ */
76
+ style(cssText: string): this {
77
+ this.updateState({ inlineStyle: cssText });
78
+ return this;
79
+ }
80
+
81
+ /**
82
+ * Receives RAW DOM events from the Skin and emits them to listeners.
83
+ * This keeps the 'emit' logic encapsulated within the Engine.
84
+ */
85
+ handleEvent(eventName: string, content: any): void {
86
+ this.emit(eventName, content);
87
+ }
69
88
  }
@@ -6,45 +6,47 @@ export class ElementSkin extends BaseSkin {
6
6
  get structureCss() {
7
7
  return new URL('./structure.css', import.meta.url).href;
8
8
  }
9
- /**
10
- * Defines the initial root element creation strategy
11
- */
9
+ // ✅ Fix: Use state to determine initial tag, default to div
12
10
  createRoot() {
13
- return document.createElement(this.engine.state.tagName || 'div');
11
+ const tag = this.engine.state.tagName || 'div';
12
+ return document.createElement(tag);
14
13
  }
15
14
  bindEvents(root) {
16
- // Generic Elements rely on the BaseSkin's .on() event system
17
- // No default listeners are enforced here to keep it lightweight
15
+ // Forward common events to the engine via public handler
16
+ // This allows engine.on('click', ...) to work without skin accessing protected 'emit'
17
+ const events = ['click', 'mouseenter', 'mouseleave', 'input', 'change', 'focus', 'blur'];
18
+ events.forEach(eventName => {
19
+ root.addEventListener(eventName, (e) => {
20
+ this.engine.handleEvent(eventName, e);
21
+ });
22
+ });
18
23
  }
19
24
  updateSkin(state) {
20
- if (!this.root)
21
- return;
22
- // 1. Tag Name Reconciliation
23
- // If the tag name changes, we must replace the root element entirely.
24
- if (this.root.tagName.toLowerCase() !== state.tagName.toLowerCase()) {
25
+ // 1. Tag Replacement Logic
26
+ if (this.root && this.root.tagName.toLowerCase() !== state.tagName.toLowerCase()) {
25
27
  const newRoot = document.createElement(state.tagName);
26
- // Move children (if any were appended externally/not managed by content state)
27
- while (this.root.firstChild) {
28
- newRoot.appendChild(this.root.firstChild);
28
+ // Transfer attributes/classes/listeners logic is complex
29
+ // We rely on re-binding events and re-applying attributes for the new root.
30
+ if (this.root.parentNode) {
31
+ this.root.parentNode.replaceChild(newRoot, this.root);
29
32
  }
30
- this.root.replaceWith(newRoot);
31
33
  this.root = newRoot;
32
- // Re-bind (BaseSkin doesn't automatically rebind on replace, so basic events might drop
33
- // if they were attached locally, but this is an edge case for "readonly" building blocks).
34
+ this.bindEvents(this.root); // Re-bind events to new root
34
35
  }
35
- // 2. Attributes & Classes (Handled largely by BaseSkin helper, but explicit here for clarity)
36
+ if (!this.root)
37
+ return;
38
+ // 2. Base Attributes
36
39
  this.applySkinAttributes(this.root, state);
37
- // 3. Content Rendering
38
- // We only touch the DOM if content differs to avoid stomping on interactive children
40
+ // 3. Content
39
41
  if (state.contentType === 'html') {
40
- if (this.root.innerHTML !== state.content) {
41
- this.root.innerHTML = state.content || '';
42
- }
42
+ this.root.innerHTML = state.content || '';
43
43
  }
44
44
  else {
45
- if (this.root.textContent !== state.content) {
46
- this.root.textContent = state.content || '';
47
- }
45
+ this.root.textContent = state.content || '';
46
+ }
47
+ // 4. Inline Styles
48
+ if (state.inlineStyle) {
49
+ this.root.style.cssText = state.inlineStyle;
48
50
  }
49
51
  }
50
52
  }
@@ -1 +1 @@
1
- {"version":3,"file":"skin.js","sourceRoot":"","sources":["skin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAG/C,MAAM,OAAO,WAAY,SAAQ,QAAqC;IAClE,YAAY,MAAqB;QAC7B,KAAK,CAAC,MAAM,CAAC,CAAC;IAClB,CAAC;IAED,IAAc,YAAY;QACtB,OAAO,IAAI,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;IAC5D,CAAC;IAED;;OAEG;IACO,UAAU;QAChB,OAAO,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,CAAC;IACtE,CAAC;IAES,UAAU,CAAC,IAAiB;QAClC,6DAA6D;QAC7D,gEAAgE;IACpE,CAAC;IAES,UAAU,CAAC,KAAmB;QACpC,IAAI,CAAC,IAAI,CAAC,IAAI;YAAE,OAAO;QAEvB,6BAA6B;QAC7B,sEAAsE;QACtE,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;YAClE,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAEtD,+EAA+E;YAC/E,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;gBAC1B,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAC9C,CAAC;YAED,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;YAC/B,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC;YAEpB,wFAAwF;YACxF,2FAA2F;QAC/F,CAAC;QAED,8FAA8F;QAC9F,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAE3C,uBAAuB;QACvB,qFAAqF;QACrF,IAAI,KAAK,CAAC,WAAW,KAAK,MAAM,EAAE,CAAC;YAC/B,IAAI,IAAI,CAAC,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC;gBACxC,IAAI,CAAC,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC;YAC9C,CAAC;QACL,CAAC;aAAM,CAAC;YACJ,IAAI,IAAI,CAAC,IAAI,CAAC,WAAW,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC;gBAC1C,IAAI,CAAC,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC;YAChD,CAAC;QACL,CAAC;IACL,CAAC;CACJ"}
1
+ {"version":3,"file":"skin.js","sourceRoot":"","sources":["skin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAG/C,MAAM,OAAO,WAAY,SAAQ,QAAqC;IAClE,YAAY,MAAqB;QAC7B,KAAK,CAAC,MAAM,CAAC,CAAC;IAClB,CAAC;IAED,IAAc,YAAY;QACtB,OAAO,IAAI,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;IAC5D,CAAC;IAED,4DAA4D;IAClD,UAAU;QAChB,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC;QAC/C,OAAO,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IACvC,CAAC;IAES,UAAU,CAAC,IAAiB;QAClC,2DAA2D;QAC3D,sFAAsF;QACtF,MAAM,MAAM,GAAG,CAAC,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;QAEzF,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE;YACvB,IAAI,CAAC,gBAAgB,CAAC,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE;gBACnC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;YAC1C,CAAC,CAAC,CAAC;QACP,CAAC,CAAC,CAAC;IACP,CAAC;IAES,UAAU,CAAC,KAAmB;QACpC,2BAA2B;QAC3B,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;YAC/E,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAEtD,yDAAyD;YACzD,4EAA4E;YAE5E,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;gBACvB,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1D,CAAC;YAED,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC;YACpB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,6BAA6B;QAC7D,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,IAAI;YAAE,OAAO;QAEvB,qBAAqB;QACrB,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAE3C,aAAa;QACb,IAAI,KAAK,CAAC,WAAW,KAAK,MAAM,EAAE,CAAC;YAC/B,IAAI,CAAC,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC;QAC9C,CAAC;aAAM,CAAC;YACJ,IAAI,CAAC,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC;QAChD,CAAC;QAED,mBAAmB;QACnB,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC;YACpB,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC,WAAW,CAAC;QAChD,CAAC;IACL,CAAC;CACJ"}
@@ -10,51 +10,55 @@ export class ElementSkin extends BaseSkin<ElementState, ElementEngine> {
10
10
  return new URL('./structure.css', import.meta.url).href;
11
11
  }
12
12
 
13
- /**
14
- * Defines the initial root element creation strategy
15
- */
13
+ // ✅ Fix: Use state to determine initial tag, default to div
16
14
  protected createRoot(): HTMLElement {
17
- return document.createElement(this.engine.state.tagName || 'div');
15
+ const tag = this.engine.state.tagName || 'div';
16
+ return document.createElement(tag);
18
17
  }
19
18
 
20
19
  protected bindEvents(root: HTMLElement): void {
21
- // Generic Elements rely on the BaseSkin's .on() event system
22
- // No default listeners are enforced here to keep it lightweight
20
+ // Forward common events to the engine via public handler
21
+ // This allows engine.on('click', ...) to work without skin accessing protected 'emit'
22
+ const events = ['click', 'mouseenter', 'mouseleave', 'input', 'change', 'focus', 'blur'];
23
+
24
+ events.forEach(eventName => {
25
+ root.addEventListener(eventName, (e) => {
26
+ this.engine.handleEvent(eventName, e);
27
+ });
28
+ });
23
29
  }
24
30
 
25
31
  protected updateSkin(state: ElementState): void {
26
- if (!this.root) return;
27
-
28
- // 1. Tag Name Reconciliation
29
- // If the tag name changes, we must replace the root element entirely.
30
- if (this.root.tagName.toLowerCase() !== state.tagName.toLowerCase()) {
32
+ // 1. Tag Replacement Logic
33
+ if (this.root && this.root.tagName.toLowerCase() !== state.tagName.toLowerCase()) {
31
34
  const newRoot = document.createElement(state.tagName);
32
35
 
33
- // Move children (if any were appended externally/not managed by content state)
34
- while (this.root.firstChild) {
35
- newRoot.appendChild(this.root.firstChild);
36
+ // Transfer attributes/classes/listeners logic is complex
37
+ // We rely on re-binding events and re-applying attributes for the new root.
38
+
39
+ if (this.root.parentNode) {
40
+ this.root.parentNode.replaceChild(newRoot, this.root);
36
41
  }
37
42
 
38
- this.root.replaceWith(newRoot);
39
43
  this.root = newRoot;
40
-
41
- // Re-bind (BaseSkin doesn't automatically rebind on replace, so basic events might drop
42
- // if they were attached locally, but this is an edge case for "readonly" building blocks).
44
+ this.bindEvents(this.root); // Re-bind events to new root
43
45
  }
44
46
 
45
- // 2. Attributes & Classes (Handled largely by BaseSkin helper, but explicit here for clarity)
47
+ if (!this.root) return;
48
+
49
+ // 2. Base Attributes
46
50
  this.applySkinAttributes(this.root, state);
47
51
 
48
- // 3. Content Rendering
49
- // We only touch the DOM if content differs to avoid stomping on interactive children
52
+ // 3. Content
50
53
  if (state.contentType === 'html') {
51
- if (this.root.innerHTML !== state.content) {
52
- this.root.innerHTML = state.content || '';
53
- }
54
+ this.root.innerHTML = state.content || '';
54
55
  } else {
55
- if (this.root.textContent !== state.content) {
56
- this.root.textContent = state.content || '';
57
- }
56
+ this.root.textContent = state.content || '';
57
+ }
58
+
59
+ // 4. Inline Styles
60
+ if (state.inlineStyle) {
61
+ this.root.style.cssText = state.inlineStyle;
58
62
  }
59
63
  }
60
64
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "juxscript",
3
- "version": "1.0.68",
3
+ "version": "1.0.69",
4
4
  "type": "module",
5
5
  "description": "A JavaScript UX authorship platform",
6
6
  "main": "lib/jux.js",