neo.mjs 10.3.2 → 10.3.3

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.
@@ -0,0 +1,14 @@
1
+ # Neo.mjs v10.3.3 Release Notes
2
+
3
+ This patch release introduces a major new deep-dive article on our rendering architecture and enhances our framework comparison documentation.
4
+
5
+ ## New Content & Documentation
6
+
7
+ - **New Blog Post: The Surgical Update: From JSON Blueprints to Flawless UI**
8
+ - A comprehensive deep dive into the Neo.mjs off-thread rendering engine.
9
+ - Explores key concepts like the secure `DomApiRenderer`, asymmetric update batching, and how our architecture preserves DOM state (e.g., for a playing `<video>`).
10
+ - This is the fourth article in the v10 blog post series.
11
+
12
+ - **Enhanced Framework Comparisons**
13
+ - The comparison documents for React, Vue, and Angular have been updated with a new section: **"Component Mobility: Portals vs. True Persistence."**
14
+ - This section details the architectural advantages of Neo.mjs's persistent component model for moving stateful DOM elements, a common challenge for single-threaded frameworks.
package/README.md CHANGED
@@ -124,7 +124,8 @@ That’s Neo.mjs in action — solving problems others can’t touch.
124
124
  * **Multi-Window & Single-Page Applications (SPAs)***: Beyond traditional SPAs, Neo.mjs excels at complex multi-window applications.
125
125
  Its unique architecture, powered by seamless cross-worker communication (enabled by `Neo.worker.mixin.RemoteMethodAccess`) and
126
126
  extensible Main Thread addons (`Neo.main.addon.*`), enables truly native-like, persistent experiences across browser windows,
127
- all without a native shell.
127
+ all without a native shell. This is made possible by the same efficient delta-based DOM update engine, which can surgically
128
+ move and update components across window boundaries with unparalleled performance.
128
129
 
129
130
  * **No npm Dependency Hell**: Neo.mjs apps run with **zero runtime dependencies**, just a few dev dependencies for tooling.
130
131
  This means smaller bundles, fewer conflicts, and a simpler dependency graph.
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.3.2'
23
+ * @member {String} version='10.3.3'
24
24
  */
25
- version: '10.3.2'
25
+ version: '10.3.3'
26
26
  }
27
27
 
28
28
  /**
@@ -16,7 +16,7 @@
16
16
  "@type": "Organization",
17
17
  "name": "Neo.mjs"
18
18
  },
19
- "datePublished": "2025-08-03",
19
+ "datePublished": "2025-08-04",
20
20
  "publisher": {
21
21
  "@type": "Organization",
22
22
  "name": "Neo.mjs"
@@ -1,4 +1,16 @@
1
1
  [{
2
+ "author" : "Tobias Uhlig",
3
+ "authorImage" : "author_TobiasUhlig.jpeg",
4
+ "date" : "Aug 04, 2025",
5
+ "id" : 68,
6
+ "image" : "VdomRevolution.png",
7
+ "name" : "The Surgical Update: From JSON Blueprints to Flawless UI",
8
+ "provider" : "Medium",
9
+ "publisher" : "ITNEXT",
10
+ "selectedInto": [],
11
+ "type" : "Blog Post",
12
+ "url" : "https://itnext.io/the-surgical-update-from-json-blueprints-to-flawless-ui-fcba31575f44?source=friends_link&sk=d2084a1ed9d5fc512859320895550080"
13
+ }, {
2
14
  "author" : "Tobias Uhlig",
3
15
  "authorImage" : "author_TobiasUhlig.jpeg",
4
16
  "date" : "Jul 28, 2025",
@@ -108,7 +108,7 @@ class FooterContainer extends Container {
108
108
  }, {
109
109
  module: Component,
110
110
  cls : ['neo-version'],
111
- text : 'v10.3.2'
111
+ text : 'v10.3.3'
112
112
  }]
113
113
  }],
114
114
  /**
@@ -1,56 +1,137 @@
1
- # The VDOM Revolution: How We Render UIs from a Web Worker
1
+ # The Surgical Update: From JSON Blueprints to Flawless UI
2
2
 
3
- The Virtual DOM is a cornerstone of modern frontend development. But what happens when you take this concept and move it
4
- off the main thread entirely, into a Web Worker? It's a compelling idea—it promises a world where even the most complex
5
- UI rendering and diffing can never block user interactions.
3
+ **A deep dive into our off-thread rendering engine, which uses asymmetric batching and a secure `DomApiRenderer` to
4
+ eliminate jank at an architectural level.**
6
5
 
7
- But this architectural shift introduces a new set of fascinating engineering challenges. How do you efficiently
8
- communicate UI changes from a worker to the main thread? And what's the best language to describe a UI when it's being
9
- built by a machine, for a machine?
6
+ In a world where some frameworks declare the VDOM "pure overhead," we've made a radical bet: we've doubled down on the
7
+ VDOM and moved it entirely off the main thread. This isn't a defense of the traditional VDOM;
8
+ it's a redefinition of its purpose.
9
+
10
+ The debate between VDOM-based and "VDOM-less" frameworks misses the point. Both are fundamentally limited by the same
11
+ bottleneck: the single main thread where your application logic, state management, and rendering all compete for resources.
12
+ The result is inevitable—every complex calculation is a potential source of UI jank.
13
+
14
+ The recent introduction of the React Compiler is a brilliant validation of this very problem. By automating memoization,
15
+ the React team acknowledges that managing performance on the main thread is a major burden. But automated memoization is
16
+ still memoization—an extra layer of overhead added to your application, just hidden by a tool. It’s the most advanced
17
+ solution possible for a single-threaded architecture.
18
+
19
+ Neo.mjs offers a different path. Our architecture is designed to make the cost of updates so low that performance
20
+ memoization becomes unnecessary. We don't automate the tax; we eliminate it.
21
+
22
+ Neo.mjs solves this by changing the battlefield. For any true off-main-thread architecture, a VDOM isn't a choice—it's a
23
+ **necessity**. Since a Web Worker cannot directly access the DOM, it needs a serializable, abstract representation of
24
+ the UI to send to the main thread. That representation *is* a Virtual DOM. We treat it not as a rendering engine, but
25
+ as the essential **cross-thread communication protocol**. This same protocol is the foundation for true multi-window
26
+ applications, allowing a single App Worker to seamlessly synchronize the UI across multiple browser windows—a feat
27
+ impossible for single-threaded frameworks.
28
+
29
+ This architectural shift is the real revolution. It forces us to answer fascinating new questions: How do you best
30
+ communicate UI changes between threads? And, most critically, what is the ideal language for describing a UI when it's
31
+ being built not by a human, but by a machine? The answer is simple, structured data—a language that AI understands natively.
32
+ This is the foundation for the next generation of applications.
10
33
 
11
34
  This article explores the solutions to those problems, focusing on two key concepts:
12
- 1. **JSON Blueprints:** Why using structured data is a more powerful way to define complex UIs than traditional HTML.
35
+ 1. **Three Philosophies of UI Definition:** How Neo.mjs offers multiple layers of abstraction for building UIs, from
36
+ high-level declarative trees to low-level VDOM blueprints.
13
37
  2. **Asymmetric Rendering:** How using different, specialized strategies for creating new UI vs. updating existing UI
14
- 3. leads to a more performant and secure system.
38
+ leads to a more performant and secure system.
15
39
 
16
40
  *(Part 4 of 5 in the v10 blog series. Details at the bottom.)*
17
41
 
18
42
  ---
19
43
 
20
- ## Part 1: The Blueprint - Why JSON is the Language of the Future UI
44
+ ## Part 1: The Three Philosophies of UI Definition
45
+
46
+ Before diving into *how* the VDOM works, it's crucial to understand *what* we're building. Neo.mjs offers three distinct
47
+ approaches to defining your UI, each with its own strengths.
48
+
49
+ ### 1. The OOP Way: Abstracting the DOM Away
50
+
51
+ The most powerful and abstract way to build complex applications in Neo.mjs is the Object-Oriented approach. You compose
52
+ your UI by creating `Container` classes and declaratively defining their child `items` as a configuration object.
53
+
54
+ ```javascript
55
+ // Example of a declarative component tree
56
+ import Container from '../../../../src/container/Base.mjs';
57
+ import Toolbar from '../../../../src/toolbar/Base.mjs';
58
+ import Button from '../../../../src/button/Base.mjs';
59
+
60
+ class MyComponent extends Container {
61
+ static config = {
62
+ className: 'MyComponent',
63
+ layout : {ntype: 'vbox', align: 'stretch'},
64
+ items : [{
65
+ module: Toolbar,
66
+ items : [{module: Button, text: 'Button 1'}]
67
+ }, {
68
+ ntype: 'component',
69
+ text : 'Content Area'
70
+ }]
71
+ }
72
+ }
73
+ ```
74
+
75
+ With this method, you are not thinking about HTML, divs, or even the VDOM. You are describing your application in terms
76
+ of its logical components and their relationships. This is the classic approach for building robust, enterprise-scale
77
+ applications where separation of concerns and maintainability are paramount.
78
+
79
+ From a Micro-Frontends perspective: The **named** configs here are the contract (API) available from the outside.
80
+ Any run-time value change is reactive and will update the UI.
81
+
82
+ ### 2. The Functional Way: Direct VDOM Control with JSON Blueprints
83
+
84
+ For functional components, or when you need to dynamically generate UI structures, you can drop down a level of
85
+ abstraction and work directly with **JSON Blueprints**. This is the "native language" of the Neo.mjs rendering engine.
21
86
 
22
- The web industry has spent years optimizing the delivery of HTML. For content-heavy sites, Server-Side Rendering (SSR)
23
- and streaming HTML is a brilliant solution. But for complex, stateful applications—the kind needed for AI cockpits, IDEs,
24
- and enterprise dashboards—is sending pre-rendered HTML the ultimate endgame?
87
+ The component's `render()` method returns a structured JSON object that describes the VDOM tree.
25
88
 
26
- We've seen this movie before. In the world of APIs, the verbose, heavyweight XML standard was supplanted by the lighter,
27
- simpler, and more machine-friendly JSON. We believe the same evolution is inevitable for defining complex UIs.
89
+ We've seen this movie before. In the world of APIs, the verbose, heavyweight, and human-readable XML standard was
90
+ inevitably supplanted by the lighter, simpler, and more machine-friendly JSON. We believe the same evolution is happening
91
+ for defining complex UIs. While HTML is the language of the document, structured data is the language of the application.
28
92
 
29
- Instead of the server laboring to render and stream HTML, Neo.mjs is built on the principle of **JSON Blueprints**.
30
- The server's job is to provide a compact, structured description of the component tree—its configuration, state, and
31
- relationships. Think of it as sending the architectural plans, not pre-fabricated walls.
93
+ This approach has profound advantages:
32
94
 
33
- This approach has profound advantages, especially for the AI-driven applications of tomorrow:
95
+ * **Extreme Data Efficiency:** A JSON blueprint is drastically smaller than its equivalent rendered HTML.
96
+ * **AI's Native Language:** This is the most critical advantage for the next generation of applications. An LLM's
97
+ natural output is structured text. Asking it to generate a valid JSON object that conforms to a component's API is
98
+ a far more reliable and constrained task than asking it to generate nuanced HTML.
99
+ * **Ultimate Control:** It gives you precise, programmatic control over the generated VDOM.
34
100
 
35
- * **Extreme Data Efficiency:** A JSON blueprint is drastically smaller than its equivalent rendered HTML, minimizing data transfer.
36
- * **Server De-Loading:** This offloads rendering stress from the server, freeing it for core application logic and intensive AI computations.
37
- * **AI's Native Language:** This is the most critical advantage for the next generation of applications.
38
- An LLM's natural output is structured text. Asking it to generate a valid JSON object that conforms to a component's
39
- configuration is a far more reliable and constrained task than asking it to generate nuanced HTML with embedded logic
40
- and styles. The component's config becomes a clean, well-defined API for the AI to target, making UI generation less
41
- error-prone and more predictable.
42
- * **True Separation of Concerns:** The server provides the "what" (the UI blueprint); the client's worker-based engine
43
- expertly handles the "how" (rendering, interactivity, and state management).
101
+ This philosophy is the engine behind our AI-powered **Neo Studio** (super early spoiler),, which generates these JSON
102
+ blueprints from natural language prompts.
44
103
 
45
- This philosophy—that structured JSON is the future of UI definition—is not just a theoretical concept for us. It
46
- is the core engine behind a new tool we are developing: **Neo Studio**. It's a multi-window, browser-based IDE
47
- where we're integrating AI to generate component blueprints from natural language. The AI doesn't write JSX; it
48
- generates the clean, efficient JSON that the framework then renders into a live UI. It's the first step towards
49
- the vision of scaffolding entire applications this way.
104
+ ![Screenshot of the Neo Studio UI, showcasing a generated component from a prompt](https://raw.githubusercontent.com/neomjs/pages/main/resources_pub/website/blog/NeoStudio.png "Neo Studio Spoiler")
50
105
 
51
- `[Screenshot of the Neo Studio UI, showcasing a generated component from a prompt]`
106
+ ### 3. The Onboarding Ramp: Developer-Friendly HTML Templates
52
107
 
53
- JSON blueprints are the language. Now let's look at the engine that translates them into a live application.
108
+ While JSON is the framework's native tongue, we recognize the universal fluency of HTML. To lower the barrier to entry
109
+ and provide a familiar authoring experience, the recent [v10.3.0 release](../../.github/RELEASE_NOTES/v10.3.0.md)
110
+ introduced an intuitive, HTML-like syntax built on standard JavaScript Tagged Template Literals.
111
+
112
+ ```javascript
113
+ // Inside a component's render() method:
114
+ return html`
115
+ <div class="my-container">
116
+ <p>${config.myText}</p>
117
+ <${Button} text="Click Me" handler="${this.onButtonClick}" />
118
+ </div>
119
+ `;
120
+ ```
121
+
122
+ This is the **beginner mode**—an easy on-ramp for developers new to the framework. Crucially, this is **not JSX**. It's
123
+ built on our core principle of a **zero-builds development experience**, running directly in the browser.
124
+
125
+ To achieve this without sacrificing performance, we use a dual-mode architecture:
126
+ 1. **Development:** Templates are parsed live in the browser.
127
+ 2. **Production:** A build-time process transforms the templates directly into the same optimized **JSON VDOM blueprints**
128
+ used by the functional approach. The parser is completely removed, resulting in zero runtime overhead.
129
+
130
+ This gives developers a choice: start with the familiar comfort of HTML templates, or use the raw power of JSON
131
+ blueprints. For complex functional components, we still recommend using JSON directly for maximum clarity and performance.
132
+ But for simpler components or for developers who prefer it, the template syntax is a powerful and welcome alternative.
133
+
134
+ All three philosophies feed into the same revolutionary rendering engine. Now let's look at that engine.
54
135
 
55
136
  ---
56
137
 
@@ -69,6 +150,7 @@ the DOM in the Main Thread.
69
150
 
70
151
  On the Main Thread, the `Neo.main.DeltaUpdates` manager acts as a central orchestrator. It receives a stream of commands
71
152
  from the VDOM worker and uses the right tool for every job.
153
+ Deltas are pushed into `requestAnimationFrame()`.
72
154
 
73
155
  #### For Creating New DOM: The `DomApiRenderer`
74
156
 
@@ -76,12 +158,41 @@ Whenever a new piece of UI needs to be created, the VDOM worker sends an `insert
76
158
  initial page load. It applies any time you dynamically add a new component to a container or, in a capability that
77
159
  showcases the power of the multi-threaded architecture, move an entire component tree into a **new browser window**.
78
160
 
79
- For all these creation tasks, our pipeline uses the `DomApiRenderer`. This renderer is not only fast but also
80
- **secure by default**. It never parses HTML strings, instead building the DOM programmatically with safe APIs like
81
- `document.createElement()` and `element.textContent`. This completely eradicates the risk of XSS attacks that plague
82
- `innerHTML`-based rendering, providing a crucial safety net for UIs where an LLM might generate content or even structure.
161
+ For all these creation tasks, our pipeline uses the [DomApiRenderer](../../src/main/render/DomApiRenderer.mjs).
162
+ This renderer is not only fast but also **secure by default**. It achieves this by treating all content as plain text
163
+ unless explicitly told otherwise. When processing a VDOM blueprint, it uses the safe `node.textContent` property for any
164
+ `text` configs. This simple default required a framework-wide effort to ensure our entire component library uses `text`
165
+ instead of `html`, fundamentally eliminating the risk of XSS attacks that plague `innerHTML`-based rendering. While
166
+ developers can still use the `html` property at their own risk for trusted content, the framework's secure-by-default
167
+ posture provides a crucial safety net, especially for UIs where an LLM might generate content or even structure.
168
+ This zero-trust approach to rendering means that even if a malicious or malformed string were to be injected into a
169
+ component's data, it is physically incapable of being executed as code in the browser.
170
+
171
+ The renderer builds the entire new UI tree on a `DocumentFragment` that is detached from the live DOM, preventing layout
172
+ thrashing. Only when the entire structure is complete is it appended to the document in a single, efficient operation.
173
+
174
+ ```javascript
175
+ // A simplified look at the DomApiRenderer's core logic
176
+ // Note: This runs on the Main Thread
177
+ const createFragment = (vnode) => {
178
+ const fragment = document.createDocumentFragment();
179
+
180
+ // Recursively build the entire tree off-screen
181
+ vnode.childNodes?.forEach(child => {
182
+ const el = document.createElement(child.tag);
183
+ // ... logic for setting attributes, styles, etc.
184
+ fragment.appendChild(el);
185
+ });
186
+
187
+ return fragment;
188
+ };
189
+
190
+ // Later, in a single operation:
191
+ parentElement.appendChild(createFragment(vnode));
192
+ ```
83
193
 
84
- Enabling this superior rendering engine is as simple as setting a flag in your project's configuration:
194
+ Enabling this superior rendering engine is as simple as setting a flag in your project's configuration
195
+ (Default value in v10):
85
196
 
86
197
  ```json
87
198
  {
@@ -90,6 +201,22 @@ Enabling this superior rendering engine is as simple as setting a flag in your p
90
201
  }
91
202
  ```
92
203
 
204
+ But its real genius lies in how it handles complex insertions. This isn't just about performance; it's about **preserving
205
+ the state of live DOM nodes**.
206
+
207
+ Consider a `<video>` element that is currently playing. In many frameworks, moving that video component to a different
208
+ part of the UI would destroy the old DOM node and create a new one, causing the video to jarringly restart from the
209
+ beginning. This is because the rendering engine only knows how to create new things, not how to relocate existing ones.
210
+
211
+ Neo.mjs avoids this entirely. Our architecture understands that the component instance is a persistent entity. When you
212
+ move it, the [DomApiVnodeCreator](../../src/vdom/util/DomApiVnodeCreator.mjs) sees that the video component's DOM
213
+ already exists. Instead of generating a VDOM blueprint to recreate it, it **prunes that entire branch** from the
214
+ `insertNode` delta. The VDOM worker then issues a separate, highly efficient `moveNode` delta.
215
+
216
+ The result on the main thread is a single, clean DOM operation: the existing `<video>` element is simply moved to its
217
+ new location, **continuing to play without interruption**. This is the power of the pruned graph: it's an optimization
218
+ that not only boosts performance but preserves the integrity and state of your UI.
219
+
93
220
  #### For Modifying Existing DOM: Surgical Updates
94
221
 
95
222
  When you change a property on an existing component—like its text, style, or attributes—the VDOM worker sends different
@@ -119,17 +246,107 @@ However, this had a limitation. The `updateDepth` was an "all or nothing" switch
119
246
  Consider a toolbar with ten buttons. If the toolbar's own structure needed to change *and* just one of those ten buttons
120
247
  also needed to update, the v9 model wasn't ideal.
121
248
 
122
- This is the exact challenge that **v10's Asymmetric Blueprints** were designed to solve.
249
+ This is the exact challenge that **v10's Asymmetric Blueprints** were designed to solve. The magic lies in a
250
+ sophisticated **pre-processing and negotiation phase** that happens entirely within the App Worker, *before* anything
251
+ is sent to the VDOM worker.
252
+
253
+ Here's how it works, with a more realistic example. Imagine a dashboard container with four cards. A user action causes
254
+ the container's background to change, and simultaneously, the content of the second and fourth cards needs to update.
255
+
256
+ **1. Negotiation & Aggregation:**
257
+ The `VDomUpdate` manager is notified of three separate changes: one for the container and one for each of the two cards.
258
+ Instead of sending three separate update requests across the worker boundary (which would be inefficient), it aggregates
259
+ them. It initiates a single update on the top-most component in the hierarchy (the container) and passes the IDs of the
260
+ other changed components (`card-2`, `card-4`) into the `TreeBuilder` via the `mergedChildIds` parameter.
261
+
262
+ **2. The Asymmetric Build:**
263
+ The [TreeBuilder](../../src/util/vdom/TreeBuilder.mjs) is called on the container. It knows it needs to generate the
264
+ container's own VDOM, but instead of expanding all children, it follows a simple rule: "Expand only the children whose
265
+ IDs are in the `mergedChildIds` set. For all others, create a lightweight placeholder."
266
+
267
+ Here is the power of this approach in action.
268
+
269
+ **Before: The Container's Own VDOM Blueprint**
270
+ This is the simple structure the container holds before the update. It just references its children.
271
+ ```javascript
272
+ // The container's vdom property just lists its children
273
+ {
274
+ id: 'dashboard-container-1',
275
+ tag: 'div',
276
+ cn: [
277
+ { componentId: 'card-1' },
278
+ { componentId: 'card-2' },
279
+ { componentId: 'card-3' },
280
+ { componentId: 'card-4' }
281
+ ]
282
+ }
283
+ ```
284
+
285
+ **After: The Optimized Blueprint Sent to the VDOM Worker**
286
+ The `TreeBuilder` produces a highly specific, asymmetric blueprint. It's a mix of detailed VDOM for the changed items
287
+ and placeholders for the unchanged ones.
123
288
 
124
- The new `VDomUpdate` manager and `TreeBuilder` utility work together to create a far more intelligent update payload.
125
- When the toolbar and one button need to change, the manager calculates the precise scope. The `TreeBuilder` then
126
- generates a partial VDOM blueprint that includes:
127
- 1. The full VDOM for the toolbar itself.
128
- 2. The full VDOM for the *one* button that is changing.
129
- 3. Lightweight `{componentId: 'neo-ignore'}` placeholders for the other nine buttons.
289
+ ```javascript
290
+ // The TreeBuilder intelligently expands only what's necessary
291
+ {
292
+ id: 'dashboard-container-1',
293
+ tag: 'div',
294
+ style: { background: '#f0f0f0' }, // The container's own change
295
+ cn: [
296
+ // Card 1 is untouched, so it becomes a placeholder
297
+ { componentId: 'card-1', neoIgnore: true },
298
+
299
+ // Card 2 changed, so its full VDOM is included
300
+ {
301
+ id: 'card-2',
302
+ tag: 'section',
303
+ cn: [
304
+ { tag: 'h2', text: 'Card 2 Header' },
305
+ { tag: 'p', text: 'This is the NEW updated content.' }
306
+ ]
307
+ },
308
+
309
+ // Card 3 is untouched
310
+ { componentId: 'card-3', neoIgnore: true },
311
+
312
+ // Card 4 also changed
313
+ {
314
+ id: 'card-4',
315
+ tag: 'section',
316
+ cn: [
317
+ { tag: 'h2', text: 'Card 4 Header' },
318
+ { tag: 'p', text: 'Another card with new text.' }
319
+ ]
320
+ }
321
+ ]
322
+ }
323
+ ```
324
+
325
+ **The Result: A Revolution in Efficiency and Correctness**
326
+
327
+ The VDOM worker receives this pre-optimized blueprint. When it sees a node with `neoIgnore: true`, it **completely skips
328
+ diffing that entire branch of the UI**, saving significant computation time.
130
329
 
131
- The VDOM worker receives this highly optimized, asymmetric blueprint. When it sees a `neo-ignore` node,
132
- it completely skips diffing that entire branch of the UI.
330
+ But the placeholders serve a second, equally critical purpose: **they preserve the structural integrity of the child
331
+ list**. By keeping the original order and count of siblings intact, they ensure the diffing engine can correctly
332
+ calculate where to insert a new node or move an existing one. Without them, the engine would see a list of two items
333
+ instead of four and incorrectly create deltas to delete the other two cards. The placeholders allow the engine to
334
+ distinguish between a simple update and a structural change, producing a minimal and—most importantly—*accurate* set of
335
+ deltas to send to the main thread.
336
+
337
+ This is the essence of the Asymmetric VDOM. It's not just about batching updates; it's about creating the smartest,
338
+ most minimal blueprint possible *before* the expensive diffing process even begins.
339
+
340
+ #### Inside the VDOM Worker: The Diffing Engine
341
+ Once the optimized blueprint arrives at the VDOM worker, the second half of the revolution begins. Here, the plain JSON
342
+ blueprint is inflated into a tree of `VNode` instances. This is a key architectural point: the `VNode` is a very
343
+ lightweight wrapper class that exists *only* inside the VDOM worker. It performs no complex logic, but normalizes the
344
+ raw JSON blueprint, ensuring every node has a consistent structure (e.g., an `id` and a `childNodes` array) for the
345
+ diffing engine to process reliably.
346
+
347
+ The `vdom.Helper` singleton then acts as the core diffing engine. It compares the new `VNode` tree to the previous one
348
+ and, instead of generating HTML, produces an array of **deltas**—highly specific, low-level instructions for the main
349
+ thread to execute.
133
350
 
134
351
  It’s the ultimate optimization: instead of sending the entire blueprint for a skyscraper just to fix a window,
135
352
  we now send the floor plan for the lobby *and* the specific blueprint for that one window on the 50th floor,
@@ -140,22 +357,30 @@ and the framework automatically creates the most efficient update possible.
140
357
 
141
358
  ## Conclusion: An Engine Built for Tomorrow
142
359
 
143
- The VDOM Revolution in Neo.mjs isn't just a performance enhancement; it's a paradigm shift.
360
+ The VDOM Revolution in Neo.mjs isn't just a performance enhancement; it's a paradigm shift that fundamentally changes
361
+ what's possible on the web.
144
362
 
145
363
  By combining the declarative power of **JSON Blueprints** with the intelligent efficiency of **Asymmetric Rendering**,
146
- we've created an architecture that is:
364
+ we've created an architecture that delivers on the promise of a truly non-blocking UI. For you, the developer,
365
+ this means you can finally build applications that are:
147
366
 
148
- - **Faster:** Blazing-fast initial renders and surgically precise updates keep the UI fluid at all times.
149
- - **Smarter:** The multi-threaded design allows for intensive AI logic to run in the background without ever freezing
150
- the user experience—a critical feature for AI-native apps.
151
- - **Future-Proof:** An engine where AI is not an afterthought, but a first-class citizen. It provides the perfect,
152
- secure, and efficient foundation for building applications *with* AI, where LLMs can generate, manipulate, and render
153
- complex UIs by speaking their native language: structured data.
367
+ - **Fast by Default:** Blazing-fast initial renders and surgically precise updates keep the UI fluid at all times,
368
+ without you having to think about it.
369
+ - **Intelligent & Unrestricted:** The multi-threaded design allows for intensive AI logic, complex calculations,
370
+ or heavy data processing to run in the background without ever freezing the user experience.
371
+ - **Future-Proof & AI-Native:** An engine where AI is not an afterthought, but a first-class citizen.
372
+ It provides the perfect, secure, and efficient foundation for building applications *with* AI, where LLMs can generate,
373
+ manipulate, and render complex UIs by speaking their native language: structured data.
154
374
 
155
375
  This is what it means to build a framework not just for the web of today, but for the applications of tomorrow.
156
376
 
157
- In our final article, we'll bring all three revolutions—Reactivity, Functional Components, and the VDOM—together and
158
- invite you to fall in love with frontend development all over again.
377
+ ### What's Next?
378
+
379
+ - **See it in Action:** Explore our [Online Examples](https://neomjs.com/dist/esm/apps/portal/) to experience the fluid UI for yourself.
380
+ - **Dive into the Code:** The entire framework is open source. [Check out the repo on GitHub](https://github.com/neomjs/neo) and see how it works.
381
+ - **Join the Community:** Have questions? Join our [Slack Channel](https://join.slack.com/t/neomjs/shared_invite/zt-6c50ueeu-3E1~M4T9xkNnb~M_prEEOA) and connect with the team and other developers.
382
+
383
+ In our final article, we'll bring all three revolutions—Reactivity, Functional Components, and the VDOM—together and show you why it's time to fall in love with frontend development all over again.
159
384
 
160
385
  ---
161
386
 
@@ -66,6 +66,21 @@ This is where the two frameworks diverge significantly, each offering unique tra
66
66
  * **Benefit:** This offers unparalleled speed and debugging clarity. Code changes are reflected instantly. Developers work directly with the real code in the browser's dev tools, eliminating the need for source maps and vastly simplifying debugging.
67
67
  * While Neo.mjs provides a robust architecture, it offers significant flexibility within that structure (e.g., plain JS for VDOM, choice of functional or class components), allowing developers more freedom without sacrificing consistency.
68
68
 
69
+ ### 5. Component Mobility: Portals vs. True Persistence
70
+
71
+ A critical architectural difference emerges when dealing with moving components around the UI, especially those with internal state (like a playing `<video>` or a complex third-party widget).
72
+
73
+ * **Angular: The CDK Portal**
74
+ * Angular uses its Component Dev Kit (CDK) `cdkPortal` and `cdkPortalOutlet` to solve a common CSS layout problem: rendering a component's DOM subtree in a different physical location in the DOM (e.g., a modal at the end of `<body>`). This is a **rendering trick**.
75
+ * The component's logical position in the Angular tree remains the same, but its DOM is "teleported" elsewhere.
76
+ * **The Limitation:** If the component that *defines* the portal is destroyed (e.g., via `*ngIf`), its state is destroyed. The portal's content is completely recreated from scratch. A playing video would jarringly restart.
77
+
78
+ * **Neo.mjs: True Mobility by Design**
79
+ * This is not a special feature in Neo.mjs; it is a **natural consequence of the architecture**.
80
+ * Because component instances are stable and persistent, moving a component is a controlled data operation. A developer programmatically modifies the `items` arrays of the relevant containers, then calls `update()` on the **closest common ancestor**. This signals the framework to perform a single, efficient reconciliation that correctly identifies the component move. While calling `update()` on a higher-level ancestor would also work, targeting the closest one is a best practice that minimizes the scope of the update, showcasing the framework's focus on performance and developer control. This explicit, batch-friendly approach is a core architectural feature, not a hack.
81
+ * The framework recognizes that the component's DOM node already exists. It issues a single, efficient `moveNode` command to the Main Thread.
82
+ * **The Benefit:** The existing DOM node, with all its internal state, is simply unplugged from its old parent and plugged into the new one. A playing video continues to play, uninterrupted. This enables a level of UI fluidity and state preservation that is architecturally impossible in a single-threaded model where component identity is tied to its place in the template.
83
+
69
84
  ### Other Considerations:
70
85
 
71
86
  * **Framework Rigidity vs. Flexible Structure:** Angular is often perceived as a "straightjacket" due to its highly opinionated and prescriptive nature, dictating specific patterns (e.g., NgModules, decorators, strict DI) that can limit flexibility and make it challenging to deviate from "the Angular way." Neo.mjs, while also a comprehensive framework, offers a different kind of opinionation. Its core architectural choices (worker-based, unified config system, `Neo.core.Base` for any class-based entity) provide a robust structure, but within that structure, it offers significant flexibility (e.g., plain JS for VDOM, choice of functional or class components, integrated features reducing external dependencies), allowing developers more freedom without sacrificing consistency.
@@ -136,6 +136,21 @@ This is where the two frameworks diverge significantly, each offering unique tra
136
136
  * Neo.mjs's architecture makes props drilling an obsolete anti-pattern. The integrated `state.Provider` allows a deeply nested component to subscribe *only* to the precise slice of state it needs via its `bind` config or a `useConfig` hook.
137
137
  * This creates a direct, performant, and surgical link between the state and the component that needs it, completely bypassing all intermediate components. It is more akin to a selector in a dedicated state management library, but it's a native, architectural feature of the framework.
138
138
 
139
+ ### 6. Component Mobility: Portals vs. True Persistence
140
+
141
+ A critical architectural difference emerges when dealing with moving components around the UI, especially those with internal state (like a playing `<video>` or a complex third-party widget).
142
+
143
+ * **React: The Portal "Trick"**
144
+ * React uses `ReactDOM.createPortal()` to solve a common CSS layout problem: rendering a component's DOM subtree in a different physical location in the DOM (e.g., a modal at the end of `<body>`). This is a **rendering trick**.
145
+ * The component's logical position in the React tree remains the same, but its DOM is "teleported" elsewhere.
146
+ * **The Limitation:** If the component that *defines* the portal is unmounted and remounted in a new location, its state is destroyed. The portal's content is completely recreated from scratch. A playing video would jarringly restart.
147
+
148
+ * **Neo.mjs: True Mobility by Design**
149
+ * This is not a special feature in Neo.mjs; it is a **natural consequence of the architecture**.
150
+ * Because component instances are stable and persistent, moving a component is a controlled data operation. A developer programmatically modifies the `items` arrays of the relevant containers, then calls `update()` on the **closest common ancestor**. This signals the framework to perform a single, efficient reconciliation that correctly identifies the component move. While calling `update()` on a higher-level ancestor would also work, targeting the closest one is a best practice that minimizes the scope of the update, showcasing the framework's focus on performance and developer control. This explicit, batch-friendly approach is a core architectural feature, not a hack.
151
+ * The framework recognizes that the component's DOM node already exists. It issues a single, efficient `moveNode` command to the Main Thread.
152
+ * **The Benefit:** The existing DOM node, with all its internal state, is simply unplugged from its old parent and plugged into the new one. A playing video continues to play, uninterrupted. This enables a level of UI fluidity and state preservation that is architecturally impossible in a single-threaded, ephemeral component model.
153
+
139
154
  ### Other Considerations:
140
155
 
141
156
  * **Development & Deployment Environments:** Neo.mjs offers four distinct environments, providing unparalleled flexibility:
@@ -53,6 +53,21 @@ This is where the two frameworks diverge significantly, each offering unique tra
53
53
  * **`initAsync()`**: This hook runs *after* construction but *before* the component is considered "ready." It allows for asynchronous setup (like data fetching or lazy-loading modules) to complete *before* the first render. This eliminates entire classes of UI flicker and race conditions at an architectural level.
54
54
  * **`afterSetMounted()` & True Persistence**: A Neo.mjs component is a stable, persistent instance. It can be unmounted from the DOM (`mounted: false`) and later remounted without being destroyed. `afterSetMounted` fires reliably for both state changes. This persistence is the key to enabling true multi-window applications, where a component instance can be moved from one browser window to another while retaining its full state and logic.
55
55
 
56
+ ### 4. Component Mobility: Portals vs. True Persistence
57
+
58
+ A critical architectural difference emerges when dealing with moving components around the UI, especially those with internal state (like a playing `<video>` or a complex third-party widget).
59
+
60
+ * **Vue.js: The `<Teleport>` Component**
61
+ * Vue uses its built-in `<Teleport>` component to solve a common CSS layout problem: rendering a component's DOM subtree in a different physical location in the DOM (e.g., a modal at the end of `<body>`). This is a **rendering trick**.
62
+ * The component's logical position in the Vue tree remains the same, but its DOM is "teleported" elsewhere.
63
+ * **The Limitation:** If the component that *contains* the `<Teleport>` tag is unmounted and remounted in a new location (e.g., via `v-if`), its state is destroyed. The teleported content is completely recreated from scratch. A playing video would jarringly restart.
64
+
65
+ * **Neo.mjs: True Mobility by Design**
66
+ * This is not a special feature in Neo.mjs; it is a **natural consequence of the architecture**.
67
+ * Because component instances are stable and persistent, moving a component is a controlled data operation. A developer programmatically modifies the `items` arrays of the relevant containers, then calls `update()` on the **closest common ancestor**. This signals the framework to perform a single, efficient reconciliation that correctly identifies the component move. While calling `update()` on a higher-level ancestor would also work, targeting the closest one is a best practice that minimizes the scope of the update, showcasing the framework's focus on performance and developer control. This explicit, batch-friendly approach is a core architectural feature, not a hack.
68
+ * The framework recognizes that the component's DOM node already exists. It issues a single, efficient `moveNode` command to the Main Thread.
69
+ * **The Benefit:** The existing DOM node, with all its internal state, is simply unplugged from its old parent and plugged into the new one. A playing video continues to play, uninterrupted. This enables a level of UI fluidity and state preservation that is architecturally impossible in a single-threaded model where component identity is tied to its place in the template.
70
+
56
71
  ### Other Considerations:
57
72
 
58
73
  * **Development & Deployment Environments:** Neo.mjs offers four distinct environments, providing unparalleled flexibility:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neo.mjs",
3
- "version": "10.3.2",
3
+ "version": "10.3.3",
4
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": {
@@ -83,7 +83,7 @@
83
83
  "acorn": "^8.15.0",
84
84
  "astring": "^1.9.0",
85
85
  "autoprefixer": "^10.4.21",
86
- "chalk": "^5.4.1",
86
+ "chalk": "^5.5.0",
87
87
  "clean-webpack-plugin": "^4.0.0",
88
88
  "commander": "^14.0.0",
89
89
  "cssnano": "^7.1.0",
@@ -93,7 +93,7 @@
93
93
  "highlightjs-line-numbers.js": "^2.9.0",
94
94
  "html-minifier-terser": "^7.2.0",
95
95
  "inquirer": "^12.9.0",
96
- "marked": "^16.1.1",
96
+ "marked": "^16.1.2",
97
97
  "monaco-editor": "0.50.0",
98
98
  "neo-jsdoc": "1.0.1",
99
99
  "neo-jsdoc-x": "1.0.5",
@@ -299,12 +299,12 @@ const DefaultConfig = {
299
299
  useVdomWorker: true,
300
300
  /**
301
301
  * buildScripts/injectPackageVersion.mjs will update this value
302
- * @default '10.3.2'
302
+ * @default '10.3.3'
303
303
  * @memberOf! module:Neo
304
304
  * @name config.version
305
305
  * @type String
306
306
  */
307
- version: '10.3.2'
307
+ version: '10.3.3'
308
308
  };
309
309
 
310
310
  Object.assign(DefaultConfig, {