neo.mjs 10.0.0-alpha.1 → 10.0.0-alpha.2

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 CHANGED
@@ -1,5 +1,5 @@
1
1
  <p align="center">
2
- <img height="100"src="https://raw.githubusercontent.com/neomjs/pages/main/resources_pub/images/logo/neo_logo_text_primary.svg">
2
+ <img height="100"src="https://raw.githubusercontent.com/neomjs/pages/main/resources_pub/images/logo/neo_logo_text_primary.svg" alt="Neo.mjs Logo">
3
3
  </p>
4
4
  </br>
5
5
  <p align="center">
@@ -11,104 +11,114 @@
11
11
  <a href="./CONTRIBUTING.md"><img src="https://img.shields.io/badge/PRs-welcome-green.svg?logo=GitHub&logoColor=white" alt="PRs Welcome"></a>
12
12
  </p>
13
13
 
14
- # Build Ultra-Fast, Scalable, and Extensible Web Apps :zap:
15
- :rocket: **Break Free from the Main Thread Experience True Multi-Threading**
14
+ # Build Ultra-Fast, Desktop-Like Web Apps. Period. :zap:
15
+ 🚀 **Break Free from UI Freezes Experience True Multithreading & Uncompromised Responsiveness.**
16
16
 
17
- Neo.mjs enables the creation of highly dynamic web applications by running everything **inside an App Worker**.
18
- From components and state providers to view controllers, everything operates within the worker,
19
- allowing the main thread to focus only on **DOM updates** and **delegating UI events**.
20
- This ensures **high performance, smooth reactivity**, and **extensibility** for both **multi-window** and **SPA applications**.
17
+ 💻 ***Neo.mjs v10 isn't an upgrade it's a new operating system for the web. Where others optimize at the margins, we reinvented the engine.***
18
+
19
+ Imagine web applications that never jank, no matter how complex the logic, how many real-time updates they handle, or how
20
+ many browser windows they span. Neo.mjs is engineered from the ground up to deliver **desktop-like fluidity and scalability**.
21
+ **While it excels for Single Page Apps (SPAs), Neo.mjs is simply the best option for browser-based multi-window applications**,
22
+ operating fundamentally different from traditional frameworks.
23
+
24
+ By leveraging a **pioneering Off-Main-Thread (OMT) architecture**, Neo.mjs ensures your UI remains butter-smooth, even during computationally intensive tasks like complex data processing or advanced graphics rendering. The main thread is kept free for one purpose: **flawless user interactions and seamless DOM updates.**
21
25
 
22
26
  <p align="center">
23
- <a href="https://youtu.be/pYfM28Pz6_0"><img height="316px" width="400px" src="https://raw.githubusercontent.com/neomjs/pages/master/resources_pub/images/neo33s.png"></a>
24
- <a href="https://youtu.be/aEA5333WiWY"><img height="316px" width="400px" src="https://raw.githubusercontent.com/neomjs/pages/master/resources_pub/images/neo-movie.png"></a>
27
+ <a href="https://youtu.be/pYfM28Pz6_0"><img height="316px" width="400px" src="https://raw.githubusercontent.com/neomjs/pages/master/resources_pub/images/neo33s.png" alt="Neo.mjs Performance Demo 1 (YouTube Video)"></a>
28
+ <a href="https://youtu.be/aEA5333WiWY"><img height="316px" width="400px" src="https://raw.githubusercontent.com/neomjs/pages/master/resources_pub/images/neo-movie.png" alt="Neo.mjs Performance Demo 2 (YouTube Video)"></a>
25
29
  </p>
26
30
 
27
31
  </br></br>
28
- ## 🔍 Why Neo.mjs?
29
- Neo is a worker thread-driven frontend framework designed to create multithreaded GUIs using JavaScript.
30
- By leveraging the **Off Main Thread (OMT) paradigm**, Neo ensures that your application’s UI remains responsive,
31
- even during computationally intensive tasks.
32
-
33
- Unlike traditional single-threaded frameworks, Neo distributes workloads across multiple threads, unlocking new levels of performance and scalability.
34
- Whether you’re building a small app or a large-scale enterprise solution, Neo’s architecture grows with your needs.
35
-
36
- Key Benefits of Neo.mjs
37
- 1. **Multithreading for Performance**:
38
- - Neo’s OMT paradigm moves tasks like data processing, state management, and rendering to worker threads, keeping the main thread free for rendering and user interactions.
39
- - This approach eliminates UI freezes and ensures a smooth user experience, even for complex applications.
40
- 2. **Declarative Class Configuration**:
41
- - Neo’s class config system allows you to define and manage classes in a clean, reusable way. This reduces boilerplate code and makes your codebase more maintainable.
42
- - With declarative configurations, you can focus on building features instead of wrestling with setup and initialization.
43
- 3. **Modular and Scalable Architecture**:
44
- - Neo’s modular design makes it easy to build scalable applications. Components are self-contained and reusable, promoting a clean separation of concerns.
45
- - Whether you’re building a small app or a large-scale enterprise solution, Neo’s architecture grows with your needs.
46
- 4. **Ease of Use**:
47
- - Neo’s intuitive API and comprehensive documentation make it easy to get started, even for developers new to multithreaded programming.
48
- - The framework’s design prioritizes developer productivity, allowing you to focus on solving real-world problems.
49
- 5. **Future-Proof Technology**:
50
- - Neo is built on modern web standards like JavaScript modules and worker threads, ensuring compatibility with the latest browser features.
51
- - By embracing the OMT paradigm, Neo is uniquely positioned to take advantage of future advancements in web development.
52
-
53
- ## Real-World Applications
54
-
55
- Neo is ideal for:
56
- - **Data-intensive applications**: Handle large datasets and complex calculations without compromising UI responsiveness.
57
- - **Real-time dashboards**: Build dynamic, real-time dashboards that update seamlessly.
58
- - **Enterprise solutions**: Scale your application to meet the demands of large organizations.
59
-
60
- **Why Choose Neo Over Traditional Frameworks?**
61
-
62
- Traditional single-threaded frameworks often struggle with performance bottlenecks, especially when handling complex UIs or large datasets.
63
- Neo’s **multithreaded architecture** addresses these challenges head-on, delivering a responsive and scalable solution for modern web applications.
64
-
65
- With Neo, you get:
66
- - **Responsive UIs**: No more UI freezes or janky animations.
67
- - 🚀 **Scalability**: Build applications that can handle increasing complexity without compromising performance.
68
- - 💻 **Developer Productivity**: Spend less time optimizing and more time building features.
69
- </br></br>
70
- ## 🌟 Key Features
71
- 🎭 **Actor Model**:</br>
72
- The App Worker acts as the central actor, handling application state and logic, independent of the main thread.
73
- This drives the OMT (off the main thread) paradigm into perfection,
74
- since it is keeping the main thread free for non-blocking DOM updates and UI interactions.
75
-
76
- <img src="./resources/images/workers-focus.svg">
77
-
78
- 🔄 **Reactive State Management**:</br>
79
- Built-in reactivity allows dynamic, efficient updates between components and state providers.
80
-
81
- **Instant JavaScript module based Development Mode**:</br>
82
- **Zero builds or transpilations** required. Run your app directly in the browser, modify reactive properties in real-time, and see instant updates.
83
- This gives you an **unmatched debugging experience**, saving time and reducing development costs.
84
- You can even build **entire apps inside the console** if you wish.
85
-
86
- 📊 **Hierarchical State Management**:</br>
87
- Seamlessly manage state between parent and child components with nested state providers.
88
- Each component binds to the state data from its **closest** provider,
89
- even combining data from multiple providers inside one binding.
90
-
91
- 🧩 **Clean Architecture**:</br>
92
- View controllers ensure a **separation of concerns**, isolating business logic from UI components for easier maintenance and testing.
93
-
94
- 🌐 **Multi-Window & SPAs**:</br>
95
- Easily build and manage complex, highly interactive applications that require multiple windows or traditional SPAs.
96
- No native shell required.
97
-
98
- 🌀 **Dynamic Component Management**:</br>
99
- Unmount, move, and remount components across the UI or even in separate browser windows
100
- — without losing the component’s state or logic. This **runtime flexibility** is a game-changer, **preserving JS instances** while still updating the UI dynamically.
101
-
102
- :dependabot: **No npm dependency hell**:</br>
103
- Neo.mjs apps do not need any dependencies at all, just some dev dependencies for tooling.
32
+ ## 🚀 Why Choose Neo.mjs? Solving the Toughest UI Challenges
33
+ Traditional single-threaded frontend frameworks often struggle with performance bottlenecks and UI freezes, especially for
34
+ large-scale, data-intensive, or real-time applications. Neo.mjs offers a fundamentally different solution, designed for
35
+ **uncompromising performance, enhanced security, and superior developer experience.**
36
+
37
+ 1. **Eliminate UI Freezes with True Multithreading**:
38
+ > *"The browser's main thread should be treated like a neurosurgeon: only perform precise, scheduled operations with zero distractions."*</br></br>
39
+ — Neo.mjs Core Philosophy
40
+
41
+ Neo.mjs's OMT architecture inherently prevents UI freezes. With v10's optimized rendering pipeline, your UI will remain even *more*
42
+ consistently responsive, even during intense data processing or complex graphics rendering. It achieves an astonishing
43
+ rate of **over 40,000 delta updates per second** in optimized environments. This translates to an engine with vast untapped
44
+ potential, limited only by user interaction, not the framework.
45
+
46
+ 2. **Unmatched Developer Experience: Transpilation-Free ESM**:
47
+ Say goodbye to complex build steps for development. Neo.mjs apps run **natively as ES Modules directly in the browser**.
48
+ This means **zero builds or transpilations** in dev mode, offering instant reloads and an **unmatched debugging experience**.
49
+ You modify code, and your app updates in real-time.
50
+
51
+ 3. **Inherent Security by Design**:
52
+ By prioritizing direct DOM API manipulation over string-based methods (like `innerHTML`), Neo.mjs fundamentally reduces
53
+ the attack surface for vulnerabilities like Cross-Site Scripting (XSS), building a more robust and secure application from the ground up.
54
+
55
+ 4. **Declarative, Consistent, & Reusable Architecture**:
56
+ Neo.mjs's unique **unified class config system** allows you to define components, layouts, and logic in a clean, declarative,
57
+ and highly consistent way. This significantly reduces boilerplate, improves maintainability, and makes complex UI composition surprisingly straightforward.
58
+
59
+ 5. **Scalability for Enterprise & Beyond**:
60
+ Whether building sophisticated enterprise dashboards, data-intensive Gen AI interfaces, or desktop-like multi-window applications,
61
+ Neo.mjs's modular, worker-driven architecture effortlessly scales. Components are persistent, stateful instances that can be unmounted,
62
+ moved, and even remounted across browser windows without losing their logic or state. This is key to preventing the "re-rendering madness"
63
+ common in other frameworks.
64
+
65
+ </br></br>
66
+ ## 📊 Real-World Win: Crushing UI Lag in Action
67
+ Imagine a developer building a stock trading app with live feeds updating every millisecond. Traditional frameworks often choke,
68
+ freezing the UI under the data flood. With Neo.mjs, the heavy lifting happens in worker threads, keeping the main thread free.
69
+ Traders get real-time updates with zero lag, and the app feels like a native desktop tool. Now, imagine extending this with
70
+ **multiple synchronized browser windows**, each displaying different real-time views, all remaining butter-smooth.
71
+ That’s Neo.mjs in action—solving problems others can’t touch.
72
+
73
+ </br></br>
74
+ ## 🌟 Key Features (and How They Supercharge Your App)
75
+
76
+ * **Persistent Component Instances**: Components maintain their state and logic even when their DOM is removed or moved.
77
+ No more wasteful re-creations just surgical, efficient updates.
78
+
79
+ * **Reactive State Management**: Built-in reactivity ensures dynamic, efficient updates between components and state providers,
80
+ all handled off the main thread.
81
+
82
+ * **Hierarchical State Management**: Seamlessly manage state between parent and child components with nested state providers.
83
+ Components intelligently bind to the closest provider, combining data for powerful, maintainable patterns.
84
+
85
+ * **Clean Architecture (MVVM-inspired)**: View controllers ensure a clear separation of concerns, isolating business logic
86
+ from UI components for easier maintenance, testing, and team collaboration.
87
+
88
+ * **Multi-Window & Single-Page Applications (SPAs)**: Easily build and manage complex applications that require multiple
89
+ browser windows or traditional SPAs, all powered by the same underlying multi-threaded architecture without requiring any native shell.
90
+
91
+ * **No npm Dependency Hell**: Neo.mjs apps run with **zero runtime dependencies**, just a few dev dependencies for tooling.
92
+ This means smaller bundles, fewer conflicts, and a simpler dependency graph.
93
+
94
+ * **Cutting-Edge Use Cases**: Ideal for **data-intensive applications, real-time dashboards, web-based IDEs, banking
95
+ applications, and complex multi-window Gen AI interfaces** where performance and responsiveness are non-negotiable.
96
+
97
+ <p align="center">
98
+ <img src="./resources/images/workers-focus.svg" alt="Neo.mjs Worker Architecture Diagram - Shows Main Thread, App Worker, VDom Worker, Canvas Worker, Data Worker, Service Worker, Backend connections.">
99
+ </p>
100
+ *Diagram: A high-level overview of Neo.mjs's multi-threaded architecture (Main Thread, App Worker, VDom Worker, Canvas Worker, Data Worker, Service Worker, Backend). Optional workers fade in on hover on neomjs.com.*
101
+
104
102
  </br></br>
105
- ## :bulb: Perfect for Complex Use Cases
106
- Need a **web-based IDE, banking dashboard**, or an **enterprise-grade multi-window app**? Neo.mjs is built for it.
103
+ ## 🔍 Neo.mjs vs. The Rest: Key Differentiators
104
+ Wondering how Neo.mjs stacks up against React, Angular, or Vue.js? Here’s the breakdown:
105
+
106
+ | Feature | Neo.mjs | React / Angular / Vue.js |
107
+ | :------------------------ | :--------------------------------------------- | :------------------------------------------ |
108
+ | **UI Responsiveness** | **Guaranteed smooth**: Heavy tasks off-main-thread; main thread free for UI. | **Prone to jank**: Main thread handles all logic + UI, easily blocked. |
109
+ | **Multithreading** | Native OMT architecture for core app logic, VDom, data, & graphics. | Single-threaded by default; requires complex workarounds (e.g., Web Workers for *specific* tasks). |
110
+ | **Dev Mode Experience** | **No transpilation, instant reloads**: Native ES Modules directly in browser. | Build tools (Webpack, Babel) required for dev; slower reloads. |
111
+ | **Component Persistence** | State survives DOM changes; instances move across windows. | Full re-renders common; state often lost on unmount unless managed externally. |
112
+ | **Security** | Direct DOM API, inherently XSS-resistant by design. | Relies heavily on careful string sanitization; higher XSS risk if not diligent. |
113
+ | **Multi-Window Apps** | Seamless, browser-native multi-window support. | Complex to achieve; hacky or unsupported natively. |
114
+ | **Bundle Size** | Zero runtime dependencies for lean apps. | Can be large with many third-party dependencies. |
115
+
116
+ **Neo.mjs Edge**: True multithreading, a no-build development mode, and a scalable, secure architecture combine to deliver a framework that's faster to build with and fundamentally faster and more stable to run.
107
117
 
108
118
  </br></br>
109
- ## 📦 Declarative Class Configuration
110
- Neo’s class config system allows you to define and manage classes in a declarative and reusable way.
111
- This simplifies class creation, reduces boilerplate code, and improves maintainability.
119
+ ## 📦 Declarative Class Configuration: Build Faster, Maintain Easier
120
+
121
+ Neo.mjs’s class config system allows you to define and manage classes in a declarative and reusable way. This simplifies class creation, reduces boilerplate code, and improves maintainability.
112
122
 
113
123
  ```javascript
114
124
  import Component from '../../src/component/Base.mjs';
@@ -116,65 +126,61 @@ import Component from '../../src/component/Base.mjs';
116
126
  class MyComponent extends Component {
117
127
  static config = {
118
128
  className : 'MyComponent',
119
- myConfig_ : 'defaultValue',
120
- domListeners: {
129
+ myConfig_ : 'defaultValue', // Reactive property
130
+ domListeners: { // Direct DOM event binding
121
131
  click: 'onClick'
122
132
  }
123
133
  }
124
134
 
135
+ // Automatically called when myConfig_ changes
125
136
  afterSetMyConfig(value, oldValue) {
126
- console.log('myConfig changed:', value, oldValue)
137
+ console.log('myConfig changed:', value, oldValue);
127
138
  }
128
139
 
140
+ // Handled in the App Worker, main thread remains free
129
141
  onClick(data) {
130
- console.log('Clicked!', data)
142
+ console.log('Clicked!', data);
131
143
  }
132
144
  }
133
145
 
134
146
  export default Neo.setupClass(MyComponent);
135
147
  ```
136
- With Neo.mjs’s class config system, you can:
137
148
 
138
- * Define default properties and methods in a clean, structured way.
139
- * Easily extend and reuse configurations across classes.
140
- * Keep your codebase organized and scalable.
141
-
142
- For more details, check out the <a href="https://neomjs.com/dist/production/apps/portal/index.html#/learn/gettingstarted.Config">Class Config System documentation</a>.
149
+ For more details, check out the [Class Config System documentation](https://neomjs.com/dist/production/apps/portal/index.html#/learn/gettingstarted.Config).
143
150
 
144
151
  </br></br>
145
- ## 🚀 Get Started
146
- Quick Start
152
+ ## 🚀 Jump In: Your First Neo.mjs App in Minutes
147
153
 
148
- Run the following command in your terminal, and your new Neo app will be created, the local web server will start, and your app will open in a new browser window:
154
+ Run this command:
149
155
 
150
156
  ```bash
151
157
  npx neo-app@latest
152
158
  ```
153
- This one-liner sets up everything you need to start building with Neo, including:
154
- - A new app workspace.
155
- - A pre-configured app shell.
156
- - A local development server.
157
- - Opening your app inside a new browser window
158
159
 
159
- :book: More details? Check out our <a href="./.github/GETTING_STARTED.md">Getting Started Guide</a>
160
+ This one-liner sets up everything you need to start building with Neo.mjs, including:
161
+
162
+ * A new app workspace.
163
+ * A pre-configured app shell.
164
+ * A local development server.
165
+ * Launching your app in a new browser window—all in one go.
160
166
 
161
- :student: Make sure to dive into the <a href="https://neomjs.com/dist/production/apps/portal/#/learn/gettingstarted.Setup">Learning Section</a>
167
+ :book: More details? Check out our [Getting Started Guide](./.github/GETTING_STARTED.md)
162
168
 
163
- :brain: The most advanced tutorial to help you with getting up to speed is this one:
164
- <a href="https://neomjs.com/dist/production/apps/portal/#/learn/tutorials.Earthquakes">Earthquakes Tutorial</a>
169
+ :student: Make sure to dive into the [Learning Section](https://neomjs.com/dist/production/apps/portal/#/learn/gettingstarted.Setup)
165
170
 
166
171
  Next steps:
167
- * :star: **Explore exciting Examples here**: <a href="https://neomjs.com/dist/production/apps/portal/#/examples">Neo.mjs Examples</a>
168
- * Many more are included inside the repos <a href="https://github.com/neomjs/neo/tree/dev/apps">apps</a>
169
- & <a href="https://github.com/neomjs/neo/tree/dev/examples">examples</a> folders.
170
- * :blue_book: All Blog Posts are listed here: <a href="https://neomjs.com/dist/production/apps/portal/#/blog">Neo.mjs Blog</a>
171
- </br></br>
172
- ## :handshake: Join the Community
173
172
 
174
- :speech_balloon: Have questions? Join our <a href="https://join.slack.com/t/neomjs/shared_invite/zt-6c50ueeu-3E1~M4T9xkNnb~M_prEEOA">Slack channel</a> and connect with other developers.
173
+ * :star: **Experience stunning Demos & Examples here**: [Neo.mjs Examples Portal](https://neomjs.com/dist/production/apps/portal/#/examples)
174
+ * Many more are included inside the repos [apps](https://github.com/neomjs/neo/tree/dev/apps)
175
+ & [examples](https://github.com/neomjs/neo/tree/dev/examples) folders.
176
+ * :blue_book: All Blog Posts are listed here: [Neo.mjs Blog](https://neomjs.com/dist/production/apps/portal/#/blog)
177
+
178
+ </br></br>
179
+ ## :handshake: Join the Community
175
180
 
176
- :hammer_and_wrench: Want to contribute? Check out our <a href="https://github.com/neomjs/neo/blob/dev/CONTRIBUTING.md">Contributing Guide</a>.
181
+ :speech_balloon: Have questions? Join our [Slack channel](https://join.slack.com/t/neomjs/shared_invite/zt-6c50ueeu-3E1~M4T9xkNnb~M_prEEOA) and connect with other developers.
177
182
 
183
+ :hammer_and_wrench: Want to contribute? Check out our [Contributing Guide](https://github.com/neomjs/neo/blob/dev/CONTRIBUTING.md).
178
184
 
179
185
  </br></br>
180
- Copyright (c) 2015 - today, <a href="https://www.linkedin.com/in/tobiasuhlig/">Tobias Uhlig</a>
186
+ Copyright (c) 2015 - today, [Tobias Uhlig](https://www.linkedin.com/in/tobiasuhlig/)
package/ServiceWorker.mjs CHANGED
@@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase {
20
20
  */
21
21
  singleton: true,
22
22
  /**
23
- * @member {String} version='10.0.0-alpha.1'
23
+ * @member {String} version='10.0.0-alpha.2'
24
24
  */
25
- version: '10.0.0-alpha.1'
25
+ version: '10.0.0-alpha.2'
26
26
  }
27
27
 
28
28
  /**
@@ -16,7 +16,7 @@
16
16
  "@type": "Organization",
17
17
  "name": "Neo.mjs"
18
18
  },
19
- "datePublished": "2025-06-17",
19
+ "datePublished": "2025-06-18",
20
20
  "publisher": {
21
21
  "@type": "Organization",
22
22
  "name": "Neo.mjs"
@@ -107,7 +107,7 @@ class FooterContainer extends Container {
107
107
  }, {
108
108
  module: Component,
109
109
  cls : ['neo-version'],
110
- html : 'v10.0.0-alpha.1'
110
+ html : 'v10.0.0-alpha.2'
111
111
  }]
112
112
  }],
113
113
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name" : "neo.mjs",
3
- "version" : "10.0.0-alpha.1",
4
- "description" : "The webworkers driven UI framework",
3
+ "version" : "10.0.0-alpha.2",
4
+ "description" : "Neo.mjs: The multi-threaded UI framework for building ultra-fast, desktop-like web applications with uncompromised responsiveness, inherent security, and a transpilation-free dev mode.",
5
5
  "type" : "module",
6
6
  "repository" : {
7
7
  "type": "git",
@@ -32,14 +32,42 @@
32
32
  "javascript",
33
33
  "frontend",
34
34
  "framework",
35
+ "performance",
36
+ "ui-responsiveness",
37
+ "no-jank",
38
+ "smooth-ui",
39
+ "scalability",
40
+ "enterprise",
41
+ "multi-threaded",
42
+ "desktop-like",
43
+ "native-like",
44
+ "web-app",
35
45
  "offscreencanvas",
36
46
  "sharedworker",
37
47
  "webworker",
48
+ "virtual-dom",
38
49
  "ecmascript",
39
50
  "css",
40
51
  "json",
41
52
  "react-alternative",
42
- "angular-alternative"
53
+ "angular-alternative",
54
+ "zero-build",
55
+ "no-transpilation",
56
+ "security",
57
+ "xss-resistant",
58
+ "realtime",
59
+ "data-visualization",
60
+ "single-page-application",
61
+ "spa",
62
+ "generative-ai",
63
+ "genai",
64
+ "ai-interfaces",
65
+ "json-blueprints",
66
+ "low-code-ui",
67
+ "multi-window",
68
+ "pwa",
69
+ "progressive-web-app",
70
+ "predictive-caching"
43
71
  ],
44
72
  "author" : "Tobias Uhlig",
45
73
  "license" : "MIT",
@@ -276,12 +276,12 @@ const DefaultConfig = {
276
276
  useVdomWorker: true,
277
277
  /**
278
278
  * buildScripts/injectPackageVersion.mjs will update this value
279
- * @default '10.0.0-alpha.1'
279
+ * @default '10.0.0-alpha.2'
280
280
  * @memberOf! module:Neo
281
281
  * @name config.version
282
282
  * @type String
283
283
  */
284
- version: '10.0.0-alpha.1'
284
+ version: '10.0.0-alpha.2'
285
285
  };
286
286
 
287
287
  Object.assign(DefaultConfig, {
package/src/Main.mjs CHANGED
@@ -487,7 +487,7 @@ class Main extends core.Base {
487
487
 
488
488
  Neo.config[key] = data.value;
489
489
 
490
- key === 'renderCountDeltas' && DomAccess.set({[key]: value})
490
+ key === 'renderCountDeltas' && DeltaUpdates.set({[key]: value})
491
491
  }
492
492
 
493
493
  /**
@@ -17,12 +17,37 @@ class DeltaUpdates extends Base {
17
17
  * @protected
18
18
  */
19
19
  className: 'Neo.main.DeltaUpdates',
20
+ /**
21
+ * @member {Number} countDeltas=0
22
+ * @protected
23
+ */
24
+ countDeltas: 0,
25
+ /**
26
+ * @member {Number} countDeltasPer250ms=0
27
+ * @protected
28
+ */
29
+ countDeltasPer250ms: 0,
30
+ /**
31
+ * @member {Number} countUpdates=0
32
+ * @protected
33
+ */
34
+ countUpdates: 0,
35
+ /**
36
+ * @member {Boolean} renderCountDeltas_=false
37
+ * @protected
38
+ */
39
+ renderCountDeltas_: false,
20
40
  /**
21
41
  * @member {Boolean} singleton=true
22
42
  */
23
43
  singleton: true
24
44
  }
25
45
 
46
+ /**
47
+ * @member {Number} logDeltasIntervalId=0
48
+ * @protected
49
+ */
50
+ logDeltasIntervalId = 0
26
51
  /**
27
52
  * Private property to store the dynamically loaded renderer module.
28
53
  * @member {Neo.main.render.DomApiRenderer|Neo.main.render.DomApiRenderer|null} #renderer=null
@@ -44,8 +69,14 @@ class DeltaUpdates extends Base {
44
69
  construct(config) {
45
70
  super.construct(config);
46
71
 
72
+ let me = this;
73
+
74
+ if (Neo.config.renderCountDeltas) {
75
+ me.renderCountDeltas = true
76
+ }
77
+
47
78
  // Initiate the asynchronous loading of the renderer here.
48
- this.#_readyPromise = (async () => {
79
+ me.#_readyPromise = (async () => {
49
80
  try {
50
81
  let module;
51
82
 
@@ -55,7 +86,7 @@ class DeltaUpdates extends Base {
55
86
  module = await import('./render/DomApiRenderer.mjs')
56
87
  }
57
88
 
58
- this.#renderer = module.default
89
+ me.#renderer = module.default
59
90
  } catch (err) {
60
91
  console.error('DeltaUpdates: Failed to load renderer module:', err);
61
92
  throw err // Re-throw to propagate initialization failures
@@ -63,6 +94,35 @@ class DeltaUpdates extends Base {
63
94
  })()
64
95
  }
65
96
 
97
+ /**
98
+ * Triggered after the renderCountDeltas config got changed
99
+ * @param {Boolean} value
100
+ * @param {Boolean} oldValue
101
+ * @protected
102
+ */
103
+ afterSetRenderCountDeltas(value, oldValue) {
104
+ let me = this,
105
+ {logDeltasIntervalId} = me,
106
+ node;
107
+
108
+ if (value) {
109
+ if (logDeltasIntervalId === 0) {
110
+ me.logDeltasIntervalId = setInterval(() => {
111
+ node = document.getElementById('neo-delta-updates');
112
+
113
+ if (node) {
114
+ node.innerHTML = String(me.countDeltasPer250ms * 4)
115
+ }
116
+
117
+ me.countDeltasPer250ms = 0
118
+ }, 250)
119
+ }
120
+ } else {
121
+ logDeltasIntervalId && clearInterval(logDeltasIntervalId);
122
+ me.logDeltasInterval = 0
123
+ }
124
+ }
125
+
66
126
  /**
67
127
  * @param {HTMLElement} node
68
128
  * @param {String} nodeName
@@ -54,21 +54,6 @@ class DomAccess extends Base {
54
54
  * @protected
55
55
  */
56
56
  className: 'Neo.main.DomAccess',
57
- /**
58
- * @member {Number} countDeltas=0
59
- * @protected
60
- */
61
- countDeltas: 0,
62
- /**
63
- * @member {Number} countDeltasPer250ms=0
64
- * @protected
65
- */
66
- countDeltasPer250ms: 0,
67
- /**
68
- * @member {Number} countUpdates=0
69
- * @protected
70
- */
71
- countUpdates: 0,
72
57
  /**
73
58
  * Remote method access for other workers
74
59
  * @member {Object} remote
@@ -102,11 +87,6 @@ class DomAccess extends Base {
102
87
  'windowScrollTo'
103
88
  ]
104
89
  },
105
- /**
106
- * @member {Boolean} renderCountDeltas_=false
107
- * @protected
108
- */
109
- renderCountDeltas_: false,
110
90
  /**
111
91
  * @member {Boolean} singleton=true
112
92
  * @protected
@@ -114,12 +94,6 @@ class DomAccess extends Base {
114
94
  singleton: true
115
95
  }
116
96
 
117
- /**
118
- * @member {Number} logDeltasIntervalId=0
119
- * @protected
120
- */
121
- logDeltasIntervalId = 0
122
-
123
97
  /**
124
98
  * @returns {HTMLElement}
125
99
  */
@@ -143,10 +117,6 @@ class DomAccess extends Base {
143
117
 
144
118
  let me = this;
145
119
 
146
- if (Neo.config.renderCountDeltas) {
147
- me.renderCountDeltas = true
148
- }
149
-
150
120
  me.initGlobalListeners();
151
121
 
152
122
  // Set up our aligning callback which is called when things change which may
@@ -218,35 +188,6 @@ class DomAccess extends Base {
218
188
  document.head.appendChild(script)
219
189
  }
220
190
 
221
- /**
222
- * Triggered after the renderCountDeltas config got changed
223
- * @param {Boolean} value
224
- * @param {Boolean} oldValue
225
- * @protected
226
- */
227
- afterSetRenderCountDeltas(value, oldValue) {
228
- let me = this,
229
- {logDeltasIntervalId} = me,
230
- node;
231
-
232
- if (value) {
233
- if (logDeltasIntervalId === 0) {
234
- me.logDeltasIntervalId = setInterval(() => {
235
- node = document.getElementById('neo-delta-updates');
236
-
237
- if (node) {
238
- node.innerHTML = String(me.countDeltasPer250ms * 4)
239
- }
240
-
241
- me.countDeltasPer250ms = 0
242
- }, 250)
243
- }
244
- } else {
245
- logDeltasIntervalId && clearInterval(logDeltasIntervalId);
246
- me.logDeltasInterval = 0
247
- }
248
- }
249
-
250
191
  /**
251
192
  * @param {Object} data
252
193
  * @returns {Promise<void>}
@@ -46,14 +46,14 @@ class Helper extends Base {
46
46
  * @returns {Object} deltas
47
47
  * @protected
48
48
  */
49
- compareAttributes(config) {
50
- let {deltas, oldVnode, vnode, vnodeMap} = config,
51
- attributes, delta, value, keys, styles, add, remove;
52
-
49
+ compareAttributes({deltas, oldVnode, vnode, vnodeMap}) {
50
+ // Do not compare attributes for component references
53
51
  if (oldVnode.componentId && (oldVnode.id === vnode.id || oldVnode.componentId === vnode.id)) {
54
52
  return deltas
55
53
  }
56
54
 
55
+ let attributes, delta, value, keys, styles, add, remove;
56
+
57
57
  if (vnode.vtype === 'text' && vnode.innerHTML !== oldVnode.innerHTML) {
58
58
  deltas.default.push({
59
59
  action : 'updateVtext',
@@ -265,7 +265,7 @@ class Helper extends Base {
265
265
  if (me.isMovedNode(childNode, oldVnodeMap)) {
266
266
  me.moveNode({deltas, insertDelta, oldVnodeMap, vnode: childNode, vnodeMap})
267
267
  } else {
268
- me.insertNode({deltas, index: i + insertDelta, oldVnodeMap, vnode: childNode, vnodeMap});
268
+ me.insertNode({deltas, index: i + insertDelta, oldVnodeMap, vnode: childNode, vnodeMap})
269
269
  }
270
270
 
271
271
  if (oldChildNode && vnodeId === vnodeMap.get(oldChildNodeId)?.parentNode.id) {
@@ -380,12 +380,12 @@ class Helper extends Base {
380
380
  }
381
381
  });
382
382
 
383
- // Especially relevant for vtype='text'
383
+ // Relevant for vtype='text'
384
384
  if (Object.keys(node.attributes).length < 1) {
385
385
  delete node.attributes
386
386
  }
387
387
 
388
- // Especially relevant for vtype='text'
388
+ // Relevant for vtype='text'
389
389
  if (Object.keys(node.style).length < 1) {
390
390
  delete node.style
391
391
  }
@@ -396,10 +396,10 @@ class Helper extends Base {
396
396
  /**
397
397
  * Creates a flat map of the tree, containing ids as keys and infos as values
398
398
  * @param {Object} config
399
- * @param {Neo.vdom.VNode} config.vnode
400
- * @param {Neo.vdom.VNode} [config.parentNode=null]
401
399
  * @param {Number} [config.index=0]
402
400
  * @param {Map} [config.map=new Map()]
401
+ * @param {Neo.vdom.VNode} [config.parentNode=null]
402
+ * @param {Neo.vdom.VNode} config.vnode
403
403
  * @returns {Map}
404
404
  * {String} id vnode.id (convenience shortcut)
405
405
  * {Number} index
@@ -407,18 +407,15 @@ class Helper extends Base {
407
407
  * {Neo.vdom.VNode} vnode
408
408
  * @protected
409
409
  */
410
- createVnodeMap(config) {
411
- let {vnode, parentNode=null, index=0, map=new Map()} = config,
412
- id;
413
-
410
+ createVnodeMap({index=0, map=new Map(), parentNode=null, vnode}) {
414
411
  if (vnode) {
415
- id = vnode.id || vnode.componentId;
412
+ let id = vnode.id || vnode.componentId;
416
413
 
417
414
  map.set(id, {id, index, parentNode, vnode});
418
415
 
419
416
  vnode.childNodes?.forEach((childNode, index) => {
420
- this.createVnodeMap({vnode: childNode, parentNode: vnode, index, map})
421
- });
417
+ this.createVnodeMap({index, map, parentNode: vnode, vnode: childNode})
418
+ })
422
419
  }
423
420
 
424
421
  return map
@@ -435,9 +432,8 @@ class Helper extends Base {
435
432
  * @returns {Map}
436
433
  * @protected
437
434
  */
438
- findMovedNodes(config) {
439
- let {movedNodes=new Map(), oldVnodeMap, vnode, vnodeMap} = config,
440
- id = vnode?.id;
435
+ findMovedNodes({movedNodes=new Map(), oldVnodeMap, vnode, vnodeMap}) {
436
+ let id = vnode?.id;
441
437
 
442
438
  if (id) {
443
439
  if (this.isMovedNode(vnode, oldVnodeMap)) {
@@ -454,6 +450,27 @@ class Helper extends Base {
454
450
  return movedNodes
455
451
  }
456
452
 
453
+ /**
454
+ * For delta updates to work, every node inside the live DOM needs a unique ID.
455
+ * Text nodes need to get wrapped into comment nodes, which contain the ID to ensure consistency.
456
+ * As the result, we need a physical index which counts every text node as 3 nodes.
457
+ * @param {Neo.vdom.VNode} parentNode
458
+ * @param {Number} logicalIndex
459
+ * @returns {Number}
460
+ */
461
+ getPhysicalIndex(parentNode, logicalIndex) {
462
+ let physicalIndex = logicalIndex,
463
+ i = 0;
464
+
465
+ for (; i < logicalIndex; i++) {
466
+ if (parentNode.childNodes[i]?.vtype === 'text') {
467
+ physicalIndex += 2 // Accounts for <!--neo-vtext--> wrappers
468
+ }
469
+ }
470
+
471
+ return physicalIndex
472
+ }
473
+
457
474
  /**
458
475
  * Only import for the DOM API based mount adapter.
459
476
  * @returns {Promise<void>}
@@ -493,22 +510,7 @@ class Helper extends Base {
493
510
  movedNodes = me.findMovedNodes({oldVnodeMap, vnode, vnodeMap}),
494
511
  delta = {action: 'insertNode', parentId},
495
512
  hasLeadingTextChildren = false,
496
- physicalIndex = index, // Start with the logical index
497
- i = 0,
498
- siblingVnode;
499
-
500
- // Calculate physicalIndex for DOM insertion and hasLeadingTextChildren flag
501
- // This loop processes the children of the *NEW* parent's VNode in the *current* state (parentNode.childNodes)
502
- // up to the logical insertion point.
503
- for (; i < index; i++) {
504
- siblingVnode = parentNode.childNodes[i];
505
-
506
- // If we encounter a text VNode before the insertion point, adjust physicalIndex
507
- if (siblingVnode?.vtype === 'text') {
508
- physicalIndex += 2; // Each text VNode adds 2 comment nodes to the physical count
509
- hasLeadingTextChildren = true
510
- }
511
- }
513
+ physicalIndex = me.getPhysicalIndex(parentNode, index); // Processes the children of the *NEW* parent's VNode in the *current* state
512
514
 
513
515
  Object.assign(delta, {hasLeadingTextChildren, index: physicalIndex});
514
516
 
@@ -568,21 +570,7 @@ class Helper extends Base {
568
570
  movedParentNode = movedNode.parentNode,
569
571
  {childNodes} = movedParentNode,
570
572
  delta = {action: 'moveNode', id: vnode.id, parentId},
571
- physicalIndex = index, // Start with the logical index
572
- i = 0,
573
- siblingVnode;
574
-
575
- // Calculate physicalIndex for DOM insertion.
576
- // This loop processes the children of the *NEW* parent's VNode in the *current* state (parentNode.childNodes)
577
- // up to the logical insertion point.
578
- for (; i < index; i++) {
579
- siblingVnode = parentNode.childNodes[i];
580
-
581
- if (siblingVnode?.vtype === 'text') {
582
- // Each text VNode adds 2 comment nodes to the physical count
583
- physicalIndex += 2
584
- }
585
- }
573
+ physicalIndex = this.getPhysicalIndex(parentNode, index); // Processes the children of the *NEW* parent's VNode in the *current* state (parentNode.childNodes)
586
574
 
587
575
  Object.assign(delta, {index: physicalIndex + insertDelta});
588
576
  deltas.default.push(delta);