neo.mjs 10.0.1 → 10.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,12 @@
1
+ **Neo.mjs v10.0.2 Release Notes**
2
+
3
+ This release includes several enhancements and fixes since v10.0.1.
4
+
5
+ **Key Updates:**
6
+
7
+ * **`README.md` & Comparison Enhancements**: Significant updates to the `README.md`, including a new section "🚀 Inside v10: A New Era of Frontend Architecture" with links to a five-part blog series. The architectural comparison table "🔍 Architectural Deep Dive: Neo.mjs vs. Main-Thread Frameworks" has been expanded, and detailed comparison documents (`NeoVsAngular.md`, `NeoVsReact.md`, `NeoVsVue.md`) have been updated to provide enhanced insights.
8
+ * **Data Model Enhancements**: Default values for `records` now optionally support functions, enabled by changes in `data.RecordFactory`.
9
+ * **DOM Rendering**: `DomApiRenderer` has been improved for creating void attributes, enhancing rendering accuracy.
10
+ * **Core Component Fix**: Re-added the lost `parentId` default value to `component.Abstract`, which resolves an issue with the tooltip singleton.
11
+ * **Dark Theme**: Improved the styling for ghost buttons
12
+ * **Tree List** Polished drag&drop support for the edge case where stores are not using `id` as the keyProperty.
@@ -0,0 +1,17 @@
1
+ **Neo.mjs v10.1.0 Release Notes**
2
+
3
+ This release includes important fixes and minor improvements, primarily focusing on VDOM optimization, collection performance, and selection model improvements.
4
+
5
+ **Key Updates:**
6
+
7
+ * **VDOM Optimization & `neo-ignore` Refinement (Addresses #7114):**
8
+ The internal handling of the `neo-ignore` flag has been significantly improved. Previously, `neo-ignore` could interfere with the VDOM's structural integrity, potentially leading to rendering anomalies, **especially in complex layouts like grids, where elements might overlap**. Now, a dedicated `neoIgnore: true` flag is added to VDOM nodes, preserving the original `componentId` and `id`. This ensures the VDOM engine can correctly identify and skip sub-trees during diffing, leading to more robust and efficient asymmetric VDOM updates and resolving visual rendering issues **such as those observed in grid components**.
9
+
10
+ * **Collection Performance Enhancements (Addresses #7115):**
11
+ Several methods within `src/collection/Base.mjs` (e.g., `clear`, `findBy`, `pop`, `remove`) have been optimized by replacing `this.getCount()` method calls with direct access to `this.count`. This change streamlines collection operations, leading to improved performance.
12
+
13
+ * **Enhanced Selection Model (Addresses #7116):**
14
+ The `src/selection/Model.mjs` has been updated to provide more comprehensive data during selection changes. A new `getController()` method has been added, and the `selectionChange` event now includes the `records` array, offering richer context for selection handling and better integration with view controllers.
15
+
16
+ * **Minor Fixes & Improvements (Addresses #7117):**
17
+ This release also includes minor cleanups and documentation improvements, such as an updated JSDoc comment in `src/grid/Body.mjs` and the removal of a debug `console.log` statement in `src/main/DeltaUpdates.mjs`.
package/README.md CHANGED
@@ -62,6 +62,24 @@ large-scale, data-intensive, or real-time applications. Neo.mjs offers a fundame
62
62
  moved, and even remounted across browser windows without losing their logic or state. This is key to preventing the "re-rendering madness"
63
63
  common in other frameworks.
64
64
 
65
+ </br></br>
66
+ ## 🚀 Inside v10: A New Era of Frontend Architecture
67
+
68
+ The v10 release marks a significant evolution of the Neo.mjs core, introducing a new functional component model and a revolutionary two-tier reactivity system. We've rebuilt the engine to provide an even more powerful and intuitive developer experience, making it simpler than ever to build complex, performant applications.
69
+
70
+ To understand the depth of these changes and the philosophy behind them, we've published a five-part blog series that dives deep into the architecture of v10:
71
+
72
+ 1. **[A Frontend Love Story: Why the Strategies of Today Won't Build the Apps of Tomorrow](./learn/blog/v10-post1-love-story.md)**
73
+ * *An introduction to the core problems in modern frontend development and the architectural vision of Neo.mjs.*
74
+ 2. **[Deep Dive: Named vs. Anonymous State - A New Era of Component Reactivity](./learn/blog/v10-deep-dive-reactivity.md)**
75
+ * *Explore the powerful two-tier reactivity system that makes the "memoization tax" a thing of the past.*
76
+ 3. **[Beyond Hooks: A New Breed of Functional Components for a Multi-Threaded World](./learn/blog/v10-deep-dive-functional-components.md)**
77
+ * *Discover how functional components in a multi-threaded world eliminate the trade-offs of traditional hooks.*
78
+ 4. **[Deep Dive: The VDOM Revolution - JSON Blueprints & Asymmetric Rendering](./learn/blog/v10-deep-dive-vdom-revolution.md)**
79
+ * *Learn how our off-thread VDOM engine uses simple JSON blueprints for maximum performance and security.*
80
+ 5. **[Deep Dive: The State Provider Revolution](./learn/blog/v10-deep-dive-state-provider.md)**
81
+ * *A look into the powerful, hierarchical state management system that scales effortlessly.*
82
+
65
83
  </br></br>
66
84
  ## 📦 Batteries Included: A Comprehensive Component Library
67
85
 
@@ -84,6 +102,8 @@ That’s Neo.mjs in action — solving problems others can’t touch.
84
102
  * **Persistent Component Instances**: Components maintain their state and logic even when their DOM is removed or moved.
85
103
  No more wasteful re-creations – just surgical, efficient updates.
86
104
 
105
+ * **New in v10: Functional Components & A Modern Hook System**: Embrace a modern, hook-based development style with `defineComponent`, `useConfig`, and `useEvent`. This new paradigm, built on top of our robust class system, offers a familiar and intuitive way to build components while benefiting from the unparalleled performance of our multi-threaded architecture. Best of all, it's free from the "memoization tax" (`useMemo`, `useCallback`) that plagues other frameworks.
106
+
87
107
  * **Reactive State Management**: Leveraging `Neo.state.Provider`, Neo.mjs offers natively integrated, hierarchical state management.
88
108
  Components declare their data needs via a concise `bind` config. These `bind` functions act as powerful inline formulas, allowing
89
109
  Components to automatically react to changes and combine data from multiple state providers within the component hierarchy.
@@ -116,6 +136,10 @@ That’s Neo.mjs in action — solving problems others can’t touch.
116
136
  tree across workers, live-modify component configurations directly in the browser console, and observe real-time UI updates,
117
137
  all without complex tooling setup.
118
138
 
139
+ * **Asymmetric VDOM & JSON Blueprints**: Instead of a complex, class-based VNode tree, your application logic deals with simple, serializable JSON objects. These blueprints are sent to a dedicated VDOM worker for high-performance diffing, ensuring your main thread is never blocked by rendering calculations. This architecture is not only faster but also inherently more secure and easier for AI tools to generate and manipulate.
140
+
141
+ * **Async-Aware Component Lifecycle**: With the `initAsync()` lifecycle method, components can handle asynchronous setup (like fetching data or lazy-loading modules) *before* they are considered "ready." This eliminates entire classes of race conditions and UI flicker, allowing you to build complex, data-dependent components with confidence.
142
+
119
143
  <p align="center">
120
144
  <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.">
121
145
  </p>
@@ -123,20 +147,19 @@ That’s Neo.mjs in action — solving problems others can’t touch.
123
147
  *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.*
124
148
 
125
149
  </br></br>
126
- ## 🔍 Neo.mjs vs. The Rest: Key Differentiators
127
- Wondering how Neo.mjs stacks up against React, Angular, or Vue.js? Here’s the breakdown:
128
-
129
- | Feature | Neo.mjs | React / Angular / Vue.js |
130
- | :------------------------ | :--------------------------------------------- | :------------------------------------------ |
131
- | **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. |
132
- | **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). |
133
- | **Dev Mode Experience** | **No transpilation, instant reloads**: Native ES Modules directly in browser. | Build tools (Webpack, Babel) required for dev; slower reloads. |
134
- | **Component Persistence** | State survives DOM changes; instances move across windows. | Full re-renders common; state often lost on unmount unless managed externally. |
135
- | **Security** | Direct DOM API, inherently XSS-resistant by design. | Relies heavily on careful string sanitization; higher XSS risk if not diligent. |
136
- | **Multi-Window Apps** | Seamless, browser-native multi-window support. | Complex to achieve; hacky or unsupported natively. |
137
- | **Bundle Size** | Zero runtime dependencies for lean apps. | Can be large with many third-party dependencies. |
138
-
139
- **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.
150
+ ## 🔍 Architectural Deep Dive: Neo.mjs vs. Main-Thread Frameworks
151
+ The true power of Neo.mjs lies in its foundational architectural choices, which solve problems that other frameworks can only mitigate. Here’s a more detailed breakdown:
152
+
153
+ | Feature | Neo.mjs Approach | Typical Main-Thread Framework Approach (React, Vue, Angular) | The Neo.mjs Advantage |
154
+ | :--- | :--- | :--- | :--- |
155
+ | **Core Architecture** | **Multi-Threaded by Design**: App logic, VDOM diffing, and rendering are split across a dedicated App Worker, VDOM Worker, and the Main Thread. | **Single-Threaded**: All application logic, state management, rendering, and user interactions compete for the same Main Thread resources. | **Guaranteed UI Responsiveness**. By isolating expensive computations, Neo.mjs ensures the main thread is always free to respond to user input, eliminating UI jank and freezes at an architectural level. |
156
+ | **Reactivity Model** | **Direct & Granular Hybrid**: A powerful two-tier system combines imperative "push" (`afterSet`) and declarative "pull" (`Effect`) reactivity. | **React**: Inverted model (the entire component function re-runs). **Vue/Angular**: Highly optimized, direct "pull" model. | **Performant by Default**. Eliminates the "memoization tax" (`useMemo`, etc.) required in React. More powerful than pure pull systems for orchestrating complex business logic. |
157
+ | **Component Lifecycle** | **Stable & Persistent**: Instances are created once and persist through UI changes. Features a rich lifecycle with `construct`, `initAsync`, and `afterSetMounted`. | **React**: Ephemeral (functional components are re-executed on every render). **Vue/Angular**: More stable, but lack pre-ready async hooks for complex setup. | **Robust & Predictable**. `initAsync` solves async setup (data fetching, module loading) *before* the first render, preventing UI flicker. Persistence enables complex stateful apps and multi-window operations. |
158
+ | **State Management** | **Surgical Subscriptions**: The integrated `StateProvider` allows components to subscribe *only* to the precise state slices they need, completely bypassing intermediate components. | **React**: Context API re-renders all consumers by default, requiring manual optimization. **Vue/Angular**: Optimized state managers (Pinia, NgRx) are still bound by the main thread. | **Scalable & Decoupled**. More performant for global state changes by default. Architecturally cleaner, avoiding props drilling and the performance traps of React's Context. |
159
+ | **DOM Updates** | **Asymmetric & Off-Thread**: Simple, serializable JSON objects (blueprints) are sent to the VDOM worker for diffing. The Main Thread only receives and applies minimal, pre-calculated patches. | VDOM diffing and DOM manipulation are computationally expensive tasks that occur on the main thread, directly competing with user interactions. | **Faster, More Secure, and AI-Friendly**. Off-thread diffing is faster. Using direct DOM APIs instead of `innerHTML` is more secure. Simple JSON blueprints are trivial for AI to generate and manipulate. |
160
+ | **Dev Experience** | **Zero-Builds Development**: Native ES Modules run directly in the browser. No transpilation or bundling is needed for development. | **Build-Heavy**: Relies on tools like Vite, Webpack, or the Angular CLI, which add complexity, require source maps, and introduce delays. | **Unparalleled Simplicity & Debugging Clarity**. What you write is what you debug. Instant feedback and the absence of complex build toolchains lead to a faster, more intuitive workflow. |
161
+
162
+ **The Bottom Line**: Where other frameworks optimize operations on the main thread, Neo.mjs moves them off the main thread entirely. This fundamental difference results in a framework that is not just faster, but architecturally more scalable, robust, and resilient to complexity.
140
163
 
141
164
  </br></br>
142
165
  ## ⚙️ Declarative Class Configuration: Build Faster, Maintain Easier
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.1'
23
+ * @member {String} version='10.1.0'
24
24
  */
25
- version: '10.0.1'
25
+ version: '10.1.0'
26
26
  }
27
27
 
28
28
  /**
@@ -16,7 +16,7 @@
16
16
  "@type": "Organization",
17
17
  "name": "Neo.mjs"
18
18
  },
19
- "datePublished": "2025-07-24",
19
+ "datePublished": "2025-07-27",
20
20
  "publisher": {
21
21
  "@type": "Organization",
22
22
  "name": "Neo.mjs"
@@ -108,7 +108,7 @@ class FooterContainer extends Container {
108
108
  }, {
109
109
  module: Component,
110
110
  cls : ['neo-version'],
111
- text : 'v10.0.1'
111
+ text : 'v10.1.0'
112
112
  }]
113
113
  }],
114
114
  /**
@@ -28,21 +28,21 @@ This is where the two frameworks diverge significantly, each offering unique tra
28
28
  * Communication between workers and the Main Thread happens via asynchronous message passing.
29
29
  * **Benefit:** This architecture keeps the Main Thread almost entirely free and responsive, preventing UI freezes even during heavy computations or complex application logic. It inherently leverages multi-core CPUs for parallel processing, leading to superior UI responsiveness and performance under heavy load.
30
30
 
31
- ### 2. Rendering Mechanism & Change Detection
32
-
33
- * **Angular: Template-Based & Zone.js/Ivy Change Detection**
34
- * Angular uses templates (HTML with Angular-specific syntax) to define UI. These templates are compiled into renderable instructions.
35
- * **Change Detection:** Angular traditionally relies on Zone.js to monkey-patch asynchronous browser APIs, detecting when data changes and triggering a change detection cycle. The Ivy renderer further optimizes this by compiling templates into more efficient instructions.
36
- * **VDOM:** Angular does not use a traditional Virtual DOM in the same way React does. Its Ivy renderer directly updates the DOM based on detected changes.
37
- * **Performance Consideration (Two-Way Binding & Direct DOM Access):** Angular's two-way data binding, while convenient, inherently ties the application's data model directly to the DOM. This often leads to frequent DOM read/write operations. Since **DOM read/write access is significantly slower (often 20x or more) compared to pure JavaScript logic**, this can cause performance bottlenecks and UI jank in complex applications with many bindings, as each change detection cycle can trigger a cascade of expensive DOM manipulations on the Main Thread.
38
- * **DOM Pollution:** Angular often adds numerous internal `data-set` attributes to the real DOM for its own tracking and debugging purposes. While functional, this can lead to a less clean and more verbose DOM structure.
39
- * **Immutability Considerations:** While Angular doesn't enforce immutability, performance optimizations like `OnPush` change detection often benefit significantly from immutable data patterns. This can introduce a cognitive burden for developers to manage data immutably for optimal performance.
40
-
41
- * **Neo.mjs: Off-Thread, Scoped VDOM & Atomic Insertion**
42
- * Neo.mjs uses a Virtual DOM defined by plain JavaScript objects. The diffing process happens in a VDom Worker, keeping the Main Thread free.
31
+ ### 2. Rendering & Reactivity: A Hybrid, Off-Thread Approach
32
+
33
+ * **Angular: Main-Thread Rendering & Zone.js Change Detection**
34
+ * Angular uses templates compiled into renderable instructions. Its change detection relies on **Zone.js** to monkey-patch asynchronous APIs, which broadly detects when an application's state *might* have changed.
35
+ * This triggers a change detection cycle on the main thread. While highly optimized, this can still be a bottleneck, and developers often need to manually implement the `OnPush` strategy to improve performance.
36
+
37
+ * **Neo.mjs: Off-Thread Rendering & A Two-Tier Reactivity System**
38
+ * Neo.mjs uses a Virtual DOM defined by plain JavaScript objects, with the entire diffing process happening in a dedicated **VDom Worker**. This keeps the main thread free from heavy rendering calculations.
43
39
  * **Scoped VDOM (Encapsulation & Performance):** Neo.mjs's VDOM is **scoped by default**. When a parent component renders, its children are represented by simple `{componentId: '...'}` placeholders. This provides two key advantages: 1) **Performance:** A parent's update never processes the complex VDOM of its children, keeping update payloads extremely small. 2) **Encapsulation:** It is architecturally impossible for a parent to accidentally manipulate a child's internal VDOM structure, enforcing clear ownership.
44
40
  * **Atomic Insertion:** For insertions, the Main Thread receives a VNode structure and uses `DomApiRenderer` to **build the entire new DOM subtree in memory**, completely detached from the live document. This fully constructed fragment is then inserted into the live DOM in a **single, atomic operation**.
45
- * **Fine-Grained Reactivity vs. Zone.js:** Instead of Angular's Zone.js, which broadly detects changes, Neo.mjs uses a precise, `Effect`-based system. When a piece of state (`config`) changes, only the `createVdom` functions that *directly depend* on that state are re-executed, ensuring optimal performance by default.
41
+ * **Two-Tier Reactivity vs. Zone.js:** Instead of Zone.js's broad detection, Neo.mjs uses a precise, two-tier system:
42
+ 1. **Classic Components (Imperative "Push"):** For the 100+ components in the core library, changes to reactive configs trigger `afterSet` hooks. These hooks perform surgical, imperative updates directly to the component's VDOM.
43
+ 2. **Functional Components (Declarative "Pull"):** For modern functional components, the `createVdom` function is wrapped in an `Effect`. When its dependencies change, only this function is re-executed to generate a new VDOM structure.
44
+
45
+ This hybrid system provides the architectural robustness needed for a massive component library and the modern developer experience of functional components, all while being performant by default.
46
46
 
47
47
  ### 3. Component Model & State Management
48
48
 
@@ -55,19 +55,16 @@ This is where the two frameworks diverge significantly, each offering unique tra
55
55
  * This functional approach uses hooks like `useConfig()` for state, providing a clean, declarative way to build UI while benefiting from Neo.mjs's underlying fine-grained reactivity.
56
56
  * **State Management:** Features integrated state providers and a unified config system for managing and sharing bindable data across the component tree, often simplifying cross-component communication compared to traditional DI or prop passing.
57
57
 
58
- ### 4. Build Process & Development Workflow
58
+ ### 4. Developer Experience: Prescriptive Tooling vs. Unparalleled Flexibility
59
59
 
60
- * **Angular: Comprehensive CLI & Mandatory Build Process**
61
- * Angular relies heavily on its CLI for scaffolding, development, and building. It has a mandatory and often complex build process (Webpack, TypeScript compilation, Ahead-of-Time (AOT) compilation) even for development.
62
- * **Implication:** This leads to slower development server startup times, requires source maps for debugging transpiled and bundled code, and can introduce debugging challenges due to the abstraction layer between the source code and what runs in the browser. For Angular, a build step is an inherent part of the development workflow.
60
+ * **Angular: The "Straightjacket" - A Prescriptive, Build-Heavy Workflow**
61
+ * Angular is famous for its highly opinionated and prescriptive nature. It dictates specific patterns (e.g., NgModules, decorators, strict DI) and relies heavily on its CLI for a mandatory and often complex build process (Webpack, TypeScript compilation, AOT compilation), even for development.
62
+ * **Implication:** This leads to slower development server startup times, requires source maps for debugging, and can introduce a steep learning curve. While this rigidity can enforce consistency, it often limits flexibility and makes it challenging to deviate from "the Angular way."
63
63
 
64
- * **Neo.mjs: The Revolutionary Zero Builds Development Mode**
65
- * Neo.mjs champions a **"zero builds" instant development mode** as its primary workflow. This means developers create and debug their applications entirely within this instant environment, leveraging native ES Modules, ES6 classes, and dynamic imports directly in the browser.
66
- * **Benefit:** This offers unparalleled speed and debugging clarity. Code changes are reflected instantly without any compilation step. Developers work directly with the real code in the browser's dev tools, eliminating the need for source maps and vastly simplifying debugging. This is a fundamental departure from the build-centric development paradigm of Angular and most other modern frameworks.
67
- * **Deployment Flexibility:** While development is zero-builds, Neo.mjs provides optimized build environments for deployment:
68
- * **`dist/esm`:** Deploys as native ES Modules, preserving the dev mode's file structure for efficient modular loading in modern browsers.
69
- * **`dist/production`:** Generates highly optimized, thread-specific bundles using Webpack for maximum compatibility and minification.
70
- * **Dynamic Module Loading:** Neo.mjs uniquely supports dynamically loading code-based modules (even with arbitrary `import` statements) from different environments at runtime, a powerful feature for plugin architectures or user-generated code that most other frameworks struggle with due to their static build graphs.
64
+ * **Neo.mjs: "Structured Freedom" - A Zero-Builds, Direct Workflow**
65
+ * Neo.mjs champions a **"zero builds" instant development mode**. Developers work directly with native ES Modules in the browser, eliminating the need for transpilation or bundling during development.
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
+ * 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.
71
68
 
72
69
  ### Other Considerations:
73
70
 
@@ -78,13 +75,12 @@ This is where the two frameworks diverge significantly, each offering unique tra
78
75
  * **Ecosystem & Maturity:** Angular has a large, mature ecosystem backed by Google. Neo.mjs has a smaller but dedicated community, with a focus on framework-level solutions and integrated features.
79
76
  * **Dependency Management (Batteries Included):** Angular projects often involve a large `node_modules` directory and can lead to complex dependency trees and version conflicts. Neo.mjs, in contrast, is a "batteries included" framework. It literally has zero real runtime dependencies outside of its own core. This native ES Module approach and integrated framework, significantly reduces this complexity, offering a much leaner and more controlled dependency management experience.
80
77
 
81
- ## Conclusion: Why Neo.mjs Offers Significant Technical Advantages Over Angular
78
+ ## Conclusion: Why Neo.mjs Offers a More Modern and Flexible Architecture
82
79
 
83
- While Angular is a powerful and widely adopted framework, Neo.mjs offers fundamental architectural advantages that can lead to superior technical performance and responsiveness, particularly in demanding applications:
80
+ While Angular is a powerful and widely adopted framework, Neo.mjs offers fundamental architectural advantages that can lead to superior technical performance, responsiveness, and a more streamlined developer experience.
84
81
 
85
82
  * **Unblocked Main Thread & Inherent Performance:** Neo.mjs's unique worker-based architecture fundamentally shifts application logic off the Main Thread. This ensures the UI remains fluid and responsive, even during heavy computations, leading to inherently higher performance ceilings without the need for extensive manual optimizations.
86
- * **Optimized & Precise DOM Updates:** Through off-Main-Thread VDOM diffing, sophisticated batching, and surgical direct DOM API updates, Neo.mjs achieves highly efficient and smooth visual updates, precisely targeting changes and avoiding unnecessary re-renders, often more granularly than Angular's zone-based change detection.
87
- * **Linear Effort for Complexity:** Unlike frameworks where effort can grow exponentially with application complexity, Neo.mjs's unified config system, predictable component lifecycle, and modular design enable a more linear relationship between complexity and development effort, leading to faster development cycles and lower maintenance costs in the long run.
88
- * **Streamlined Development Workflow:** The "zero builds" development mode and native ES Module approach offer a significantly faster and more transparent development experience compared to Angular's mandatory build process.
83
+ * **More Precise and Efficient Reactivity:** By using a surgical, effect-based system instead of broad, zone-based change detection, Neo.mjs is more performant by default and requires less manual tuning from the developer.
84
+ * **Superior Developer Experience:** The "zero builds" development mode offers a significantly faster, simpler, and more transparent development workflow compared to Angular's mandatory and complex build process.
89
85
 
90
- The choice between them depends on the specific application's needs. For applications where guaranteed Main Thread responsiveness, high performance under load, leveraging multi-core processing, and a streamlined development workflow are paramount, Neo.mjs presents a compelling and technically superior alternative.
86
+ The choice between them depends on the specific application's needs. For teams heavily invested in the Angular ecosystem, it remains a robust choice. However, for applications where guaranteed Main Thread responsiveness, high performance under load, and a modern, flexible development workflow are paramount, Neo.mjs presents a compelling and technically superior alternative.
@@ -43,31 +43,98 @@ This is where the two frameworks diverge significantly, each offering unique tra
43
43
  1. **Performance:** A parent's update never processes the complex VDOM of its children, keeping update payloads extremely small and efficient.
44
44
  2. **Encapsulation:** It is architecturally impossible for a parent to accidentally reach into and manipulate a child's internal VDOM structure. This enforces clear ownership and prevents a wide class of bugs.
45
45
 
46
- ### 3. Component Execution Model: Precision vs. Brute Force
47
-
48
- * **React: Cascading Re-Renders & The `memo` Tax**
49
- * When a component's state changes, React re-executes the entire component function. This is just the beginning. By default, it then triggers a cascading re-render of **all its child components**, regardless of whether their own props have changed.
50
- * This brute-force approach creates a significant performance problem known as "unnecessary re-renders." To fight this, developers are forced to pay the `memo` tax: wrapping components in `React.memo()`, manually memoizing functions with `useCallback()`, and objects with `useMemo()`. This adds significant boilerplate, increases complexity, and is a notorious source of bugs, including stale closures and incorrect dependency arrays. This manual optimization becomes a core, and often frustrating, part of the development process.
51
-
52
- * **Neo.mjs: Surgical Effects & Automatic Efficiency**
53
- * Neo.mjs's model is fundamentally more efficient and intelligent. A component's `createVdom` method is wrapped in a `Neo.core.Effect`. This effect automatically and dynamically tracks its dependencies—the specific `config` values it reads.
46
+ ### 3. Reactivity & Execution Model: The 'Inverted' Paradigm vs. Direct Granularity
47
+
48
+ * **React: The Inverted Reactivity Model & The `memo` Tax**
49
+ * React's model is "inverted" because the **entire component function is the unit of reactivity**. When a state change occurs, React re-executes the entire function. This brute-force approach then triggers a cascading re-render of **all its child components**, regardless of whether their own props have changed.
50
+ * This creates a significant performance problem known as "unnecessary re-renders." To fight this, developers are forced to pay the `memo` tax: wrapping components in `React.memo()`, manually memoizing functions with `useCallback()`, and objects with `useMemo()`. This adds significant boilerplate, increases complexity, and is a notorious source of bugs.
51
+
52
+ ```javascript
53
+ // A typical "optimized" React component, demonstrating the memoization tax
54
+ const MyComponent = React.memo(({ onButtonClick, user }) => {
55
+ // This component is wrapped in React.memo to prevent re-renders
56
+ return <button onClick={onButtonClick}>{user.name}</button>;
57
+ });
58
+
59
+ const App = () => {
60
+ const [count, setCount] = useState(0);
61
+
62
+ // We must wrap this in useCallback to prevent MyComponent from re-rendering
63
+ const handleClick = useCallback(() => { /* ... */ }, []);
64
+
65
+ // We must wrap this in useMemo to ensure the object reference is stable
66
+ const user = useMemo(() => ({ name: 'John Doe' }), []);
67
+
68
+ return (
69
+ <div>
70
+ <button onClick={() => setCount(c => c + 1)}>App Clicks: {count}</button>
71
+ <MyComponent onButtonClick={handleClick} user={user} />
72
+ </div>
73
+ );
74
+ };
75
+ ```
76
+
77
+ * **Neo.mjs: Direct & Granular Reactivity (Performant by Default)**
78
+ * Neo.mjs's model is fundamentally more efficient. The **individual config property is the unit of reactivity**.
54
79
  * When a config value changes, only the specific `createVdom` effects that depend on that *exact* piece of state are queued for re-execution. There are no cascading re-renders. If a parent's `createVdom` re-runs, but the configs passed to a child have not changed, the child component's `createVdom` function is **never executed**.
55
- * **Benefit (Zero Manual Optimization):** This fine-grained reactivity completely eliminates the need for manual memoization (`memo`, `useCallback`, `useMemo`). The framework handles dependency tracking automatically and precisely, delivering optimal performance out-of-the-box without the boilerplate and complexity inherent in React. This also sidesteps entire classes of bugs like stale closures, as dependencies are discovered fresh on every run, without requiring manual dependency arrays.
56
-
57
- * **State Management & The End of Props Drilling:** React often relies on its Context API or third-party state management libraries to avoid "props drilling." However, this often introduces performance issues, as any change to a context value re-renders all consuming components by default. Neo.mjs's architecture makes props drilling an obsolete anti-pattern. A deeply nested component can subscribe directly to the precise piece of state it needs from a `StateProvider` via an `Effect`. This creates a direct, performant link between the state and the component that needs it, completely bypassing all intermediate components. This results in a cleaner, more decoupled, and more performant architecture by default.
58
-
59
- ### 4. Scaling Complexity: Linear Effort vs. Exponential Overhead
60
-
61
- A key differentiator between the frameworks is how they handle growing application complexity.
62
-
63
- * **React: Exponential Complexity in Large Applications**
64
- * Consider a complex dashboard with a global state (e.g., a user profile in a Context). When a single value in that state changes (e.g., the user's name), React's default behavior is to re-render **every single component** that consumes that context.
65
- * To prevent the entire UI from lagging, developers must manually optimize each consuming component. This involves wrapping components in `React.memo` and using selector-like functions with `useMemo` to extract only the needed data. As the application grows, the number of these manual optimizations grows, leading to an exponential increase in boilerplate and performance-tuning effort. The burden is on the developer to constantly fight the framework's default behavior.
66
-
67
- * **Neo.mjs: Built-in Efficiency and Linear Effort**
68
- * Neo.mjs's architecture is designed to handle this scenario effortlessly. Multiple state changes are automatically batched into a single, de-duplicated update cycle via the `EffectBatchManager`.
69
- * In the same complex dashboard scenario, if a value in a global `StateProvider` changes, only the `createVdom` effects in components that *directly depend on that specific value* will re-run. All other components remain untouched, with **zero manual optimization required from the developer**.
70
- * This leads to a **linear relationship between complexity and effort**. As you add more components, you don't need to add more performance optimizations. The framework's core design ensures that updates are always surgical and efficient, allowing developers to focus on features instead of fighting the rendering engine. This is a direct result of the sophisticated, multi-layered batching and aggregation built into the framework's core.
80
+ * **Benefit (Zero Manual Optimization):** This fine-grained reactivity completely eliminates the need for manual memoization. The framework is performant by design.
81
+
82
+ ```javascript
83
+ // The Neo.mjs equivalent, performant by default without manual optimization
84
+ import {defineComponent, useConfig} from 'neo.mjs/src/functional/_export.mjs';
85
+
86
+ const MyComponent = defineComponent({
87
+ // The user config is passed in from the parent
88
+ createVdom({user}) {
89
+ // This will only log when the component *actually* re-renders
90
+ console.log('Rendering MyComponent');
91
+ return {tag: 'div', text: user.name};
92
+ }
93
+ });
94
+
95
+ export default defineComponent({
96
+ createVdom() {
97
+ const [count, setCount] = useConfig(0);
98
+
99
+ // No useMemo needed. useConfig provides a stable reference for the user object.
100
+ const [user] = useConfig({name: 'John Doe'});
101
+
102
+ return {
103
+ cn: [{
104
+ tag: 'button',
105
+ onclick: () => setCount(prev => prev + 1),
106
+ text: `App Clicks: ${count}`
107
+ }, {
108
+ module: MyComponent,
109
+ // Pass the stable user object to the child.
110
+ // MyComponent will not re-render when `count` changes.
111
+ user
112
+ }]
113
+ }
114
+ }
115
+ });
116
+ ```
117
+
118
+ ### 4. Component Lifecycle: Ephemeral vs. Stable
119
+
120
+ * **React: Ephemeral Components**
121
+ * In React, a functional component is not a persistent instance but an ephemeral function that is re-executed on every render. This conflates the concepts of component definition, rendering, and lifecycle management into a single, repeatedly-run block of code.
122
+ * Side effects and lifecycle events are managed with hooks like `useEffect`, which run *after* the render has committed to the screen. This can lead to UI flicker (e.g., render a loading state, then fetch data, then re-render the final state) and requires careful management of dependency arrays to prevent infinite loops or stale closures.
123
+
124
+ * **Neo.mjs: Stable & Persistent Instances**
125
+ * In Neo.mjs, both class-based and functional components are **stable, persistent instances** that are created once. This "stable chassis" provides a robust and predictable lifecycle that is separate from the render logic.
126
+ * **`construct()` / `init()`**: These run only once when the instance is created, providing a clear place for one-time setup.
127
+ * **`initAsync()`**: This powerful 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, eliminating UI flicker and race conditions at an architectural level.
128
+ * **`afterSetMounted()`**: This hook fires every time the component is physically mounted or unmounted from the DOM, providing a reliable way to manage DOM-specific event listeners or integrations. This persistence allows components to be moved between containers or even browser windows without being destroyed.
129
+
130
+ ### 5. State Management: Context API vs. Surgical State Providers
131
+
132
+ * **React: The Context Problem**
133
+ * React's primary built-in solution for avoiding "props drilling" is the Context API. However, it has a major performance drawback: when a context value changes, **every single component** that consumes that context re-renders by default, even if it only cares about a small, unchanged part of the context value. This forces developers back into the world of manual memoization to prevent performance degradation.
134
+
135
+ * **Neo.mjs: The State Provider Solution**
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
+ * 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.
71
138
 
72
139
  ### Other Considerations:
73
140
 
@@ -76,12 +143,9 @@ A key differentiator between the frameworks is how they handle growing applicati
76
143
  * **`dist/esm`:** Deploys as native ES Modules, preserving the dev mode's file structure for efficient modular loading in modern browsers.
77
144
  * **`dist/production`:** Generates highly optimized, thread-specific bundles using Webpack for maximum compatibility and minification.
78
145
  * **`dist/development`:** A bundled but unminified environment for debugging production-specific issues or for TypeScript preference, serving as a bridge to traditional build-based workflows.
79
- * **Dynamic Module Loading:** Neo.mjs uniquely supports dynamically loading code-based modules (even with arbitrary `import` statements) from different environments at runtime, a powerful feature for plugin architectures or user-generated code that most other frameworks struggle with.
80
146
 
81
147
  * **JSX vs. Plain Objects:** React uses JSX (requiring a build step for UI definition). Neo.mjs uses plain JavaScript objects for VDOM (no JSX compilation needed for VDOM definition).
82
- * **Side Effects:** Both frameworks advocate for managing side effects outside the pure render function. React uses `useEffect`. Neo.mjs uses separate `Neo.core.Effect` instances or dedicated hooks for side effects.
83
148
  * **Ecosystem & Maturity:** React has a massive, mature ecosystem with abundant libraries, tools, and community support. Neo.mjs has a smaller but dedicated community, with a focus on framework-level solutions and integrated features.
84
- * **Learning Curve:** React's initial learning curve can be gentle, but mastering its performance optimizations (memoization, context, custom hooks) can be complex. Neo.mjs has a steeper initial learning curve due to its worker-based architecture, but once understood, it offers inherent performance benefits.
85
149
  * **Dependency Management (Batteries Included):** React projects often involve a large `node_modules` directory and can lead to complex dependency trees and version conflicts, a common pain point often referred to as "dependency hell." Neo.mjs, in contrast, is a "batteries included" framework. It literally has zero real runtime dependencies outside of its own core. This native ES Module approach and integrated framework significantly reduces this complexity, offering a much leaner and more controlled dependency management experience.
86
150
 
87
151
  ## Conclusion: Why Neo.mjs Offers Significant Technical Advantages Over React
@@ -89,7 +153,7 @@ A key differentiator between the frameworks is how they handle growing applicati
89
153
  For applications where guaranteed Main Thread responsiveness, high performance under load, leveraging multi-core processing, and long-term maintainability are paramount, Neo.mjs presents a compelling and technically superior alternative.
90
154
 
91
155
  * **Unblocked Main Thread & Inherent Performance:** Neo.mjs's unique worker-based architecture fundamentally shifts application logic off the Main Thread. This ensures the UI remains fluid and responsive even during heavy computations, leading to inherently higher performance ceilings without the need for extensive manual optimizations common in React.
92
- * **Optimized & Precise DOM Updates:** Through off-Main-Thread VDOM diffing, sophisticated batching, and surgical direct DOM API updates, Neo.mjs achieves highly efficient and smooth visual updates, precisely targeting changes and avoiding unnecessary re-renders.
93
- * **Linear Effort for Complexity:** Unlike frameworks where effort can grow exponentially with application complexity, Neo.mjs's unified config system, predictable component lifecycle, and modular design enable a more linear relationship between complexity and development effort, leading to faster development cycles and lower maintenance costs in the long run.
156
+ * **Architecturally Superior Reactivity:** By avoiding React's "inverted reactivity model," Neo.mjs eliminates the need for manual memoization, resulting in cleaner code, fewer bugs, and a more intuitive developer experience.
157
+ * **Robust Lifecycle for Complex Apps:** The stable component lifecycle, especially with `initAsync`, provides an architectural solution for asynchronous challenges that React can only handle with post-render side effects, often leading to a less optimal user experience.
94
158
 
95
- The choice between them depends on the specific application's needs. For applications where guaranteed Main Thread responsiveness, high performance under load, leveraging multi-core processing, and long-term maintainability are paramount, Neo.mjs presents a compelling and technically superior alternative.
159
+ The choice between them depends on the specific application's needs. For simple websites or applications where the development team is already heavily invested in the React ecosystem, React remains a viable choice. For applications where performance, scalability, and long-term maintainability are critical, Neo.mjs offers a fundamentally more robust and powerful architecture.
@@ -1,8 +1,8 @@
1
1
  # Neo.mjs vs Vue.js
2
2
 
3
- Neo.mjs is a comprehensive JavaScript ecosystem for building high-performance, multi-threaded web applications. Unlike frameworks like Vue.js, which operate within a single-threaded browser environment, Neo.mjs is a self-contained system with **zero runtime dependencies**. It provides a complete, out-of-the-box solution that includes four distinct development and deployment environments, from a revolutionary zero-builds development mode to thread-optimized production bundles.
3
+ Neo.mjs is a comprehensive JavaScript ecosystem for building high-performance, multi-threaded web applications. Unlike frameworks like Vue.js, which have perfected their architecture for a single-threaded browser environment, Neo.mjs is a self-contained system with **zero runtime dependencies** built on a fundamentally different, multi-threaded paradigm. It provides a complete, out-of-the-box solution that includes four distinct development and deployment environments, from a revolutionary zero-builds development mode to thread-optimized production bundles.
4
4
 
5
- This article provides a focused comparison between the Neo.mjs ecosystem and Vue.js. While both frameworks share a commitment to reactivity and component-based architecture, their underlying philosophies on how to achieve performance and scalability are fundamentally different. We will explore their approaches to **architecture, rendering, and reactivity**, highlighting the trade-offs between Vue's highly optimized, single-threaded model and Neo.mjs's holistic, worker-based paradigm.
5
+ This article provides a focused comparison between the Neo.mjs ecosystem and Vue.js. While both frameworks share a commitment to a superb developer experience and a highly efficient reactive model, their underlying philosophies on how to achieve peak performance and scalability diverge. We will explore their approaches to **architecture, rendering, and reactivity**, highlighting the trade-offs between Vue's best-in-class, single-threaded model and Neo.mjs's holistic, worker-based paradigm.
6
6
 
7
7
  ## Foundational Concepts: A Shared Heritage
8
8
 
@@ -10,62 +10,48 @@ Despite their architectural differences, both frameworks build upon foundational
10
10
 
11
11
  * **Component-Based Architecture:** Both frameworks champion building UIs as a composition of reusable components. Neo.mjs extends this with `Neo.core.Base`, allowing any class-based entity (controllers, models, etc.) to leverage the framework's powerful class system, even without a UI.
12
12
  * **Declarative UI:** Developers describe *what* the UI should look like for a given state, and the framework handles *how* to update the DOM.
13
- * **Reactive Paradigm:** Both are built on reactive principles where UI updates are driven by changes in state. Vue's reactivity system is renowned for its efficiency and ease of use.
14
- * **Functional Components & Modern APIs:** Both support defining components as functions (or with script setup in Vue) and provide APIs (Hooks in Neo.mjs, Composition API in Vue) for managing state and side effects.
13
+ * **Fine-Grained Reactivity:** Both frameworks feature exceptionally efficient reactivity systems that avoid the "unnecessary re-render" problems found in other libraries. They automatically track dependencies and ensure that only the necessary parts of the UI are updated when state changes.
14
+ * **Modern APIs:** Both support modern development patterns with APIs for managing state and side effects (Hooks in Neo.mjs, Composition API in Vue).
15
15
 
16
16
  ## Key Differences: Architectural & Rendering Strategies
17
17
 
18
18
  This is where the two frameworks diverge significantly, each offering unique trade-offs and advantages.
19
19
 
20
- ### 1. Overall Architecture: Main Thread vs. Worker-Based
20
+ ### 1. Overall Architecture: Main-Thread Optimized vs. Multi-Thread Native
21
21
 
22
- * **Vue.js: Main Thread Focused**
22
+ * **Vue.js: A Master of the Main Thread**
23
23
  * Vue applications run entirely on the Main JavaScript Thread. All component logic, state management (e.g., Pinia), VDOM reconciliation, and direct DOM manipulation occur on this single thread.
24
- * **Implication:** Vue is exceptionally well-optimized for the Main Thread. Its reactivity system is designed to minimize unnecessary work. However, it is still fundamentally bound by the single-threaded nature of JavaScript. Very complex computations or large, synchronous state updates can still potentially block the Main Thread, impacting the user experience in data-heavy, high-frequency update scenarios.
24
+ * **Implication:** Vue is exceptionally well-optimized for this environment. Its reactivity system and template compiler are designed to minimize the work done on the main thread. However, it is still fundamentally bound by the single-threaded nature of JavaScript. Very complex computations or large, synchronous state updates can still potentially block the Main Thread, impacting the user experience in data-heavy, high-frequency update scenarios.
25
25
 
26
- * **Neo.mjs: Worker-Based (Main Thread + App Worker + VDom Worker)**
27
- * Neo.mjs's defining feature is its multi-threaded architecture. Application logic (component instances, state, business logic, `vdomEffect`s) runs in a dedicated **App Worker**, separate from the Main Thread. The VDOM diffing occurs in a **VDom Worker**.
28
- * Communication between workers and the Main Thread happens via asynchronous message passing.
29
- * **Benefit:** This architecture keeps the Main Thread almost entirely free and responsive, preventing UI freezes even during heavy computations or complex application logic. It inherently leverages multi-core CPUs for parallel processing, leading to superior UI responsiveness and performance under heavy load.
26
+ * **Neo.mjs: A "Backend-in-the-Browser" Architecture**
27
+ * Neo.mjs's defining feature is its multi-threaded architecture, which treats the client-side application with the same rigor as a backend system.
28
+ * **App Worker (The "Frontend Backend"):** Application logic, component instances, state, and business logic run in a dedicated App Worker.
29
+ * **VDom Worker (The "Rendering Service"):** The VDOM diffing occurs in a separate VDom Worker.
30
+ * **Main Thread (The "Painting Client"):** The Main Thread's primary job is to apply pre-calculated DOM patches.
31
+ * **Benefit:** This architecture keeps the Main Thread almost entirely free and responsive, preventing UI freezes even during heavy computations. It's not just an optimization; it's a paradigm shift that leverages multi-core CPUs for true parallelism.
30
32
 
31
- ### 2. Rendering Mechanism
33
+ ### 2. Reactivity Model: Pure "Pull" vs. a Hybrid "Push/Pull" System
32
34
 
33
- * **Vue.js: Main-Thread VDOM & Compiler-Optimized Updates**
34
- * Vue uses a Virtual DOM and a sophisticated compiler. During the build process, Vue's template compiler analyzes templates and converts them into highly optimized render functions.
35
- * **VDOM Definition:** Primarily uses Single-File Components (SFCs) with HTML-like templates, which are compiled into JavaScript render functions. It also supports JSX.
36
- * **Compiler Intelligence:** The compiler can detect static parts of a template and hoist them out of the render function, so they are created only once. It also applies other optimizations, such as patching flags, to help the runtime VDOM diffing algorithm take fast paths and skip unnecessary checks. This makes Vue's rendering extremely efficient on the Main Thread.
35
+ * **Vue.js: Elegant and Pure "Pull" Reactivity**
36
+ * Vue's reactivity system (using `ref` and `reactive`) is a masterpiece of developer ergonomics. It is a pure "pull" system: your templates or `watchEffect` functions "pull" from reactive sources, and Vue automatically tracks these dependencies to trigger updates.
37
+ * For side effects, developers use `watch` or `watchEffect`, which are powerful tools for observing state changes and reacting to them.
37
38
 
38
- * **Neo.mjs: Off-Thread VDOM & Developer-Friendly Mutability**
39
- * Neo.mjs also uses a Virtual DOM, but its philosophy and implementation are fundamentally different. The VDOM is defined using plain JavaScript objects and arrays—no special template syntax or build step is required for UI definition.
40
- * **Mutability by Design, Immutability in Process:** Neo.mjs embraces developer convenience by allowing **direct, mutable manipulation** of component state (configs) and the VDOM structure within the App Worker. This eliminates the boilerplate and cognitive load of managing immutable updates. The architectural brilliance lies in how it achieves the benefits of immutability: when an update is triggered, Neo.mjs creates a **JSON snapshot** of the relevant VDOM tree. This snapshot is sent to the VDom Worker, making the *update process itself* immutable and predictable for diffing. This provides the best of both worlds: simple, direct mutation for the developer and a safe, immutable structure for the high-performance diffing algorithm in another thread.
41
- * **Off-Main-Thread Diffing:** The entire VDOM diffing process occurs in the dedicated **VDom Worker**, completely freeing the Main Thread from this heavy computation.
42
- * **Scoped VDOM (Encapsulation & Performance):** The VDom Worker sends only the "deltas" (a minimal set of change instructions) back to the Main Thread. For insertions, `DomApiRenderer` **builds the entire new DOM subtree in memory**, completely detached from the live document, and inserts it in a **single, atomic operation**. Furthermore, Neo.mjs's VDOM is **scoped by default**. When a parent component renders, its children are represented by simple `{componentId: '...'}` placeholders. This provides two key advantages:
43
- 1. **Performance:** A parent's update never processes the complex VDOM of its children, keeping update payloads extremely small and efficient.
44
- 2. **Encapsulation:** It is architecturally impossible for a parent to accidentally reach into and manipulate a child's internal VDOM structure. This enforces clear ownership and prevents a wide class of bugs.
39
+ * **Neo.mjs: The Two-Tier Hybrid System**
40
+ * Neo.mjs's reactivity is arguably more powerful because it's a hybrid system designed for architectural flexibility. Every reactive property simultaneously powers two paradigms:
41
+ 1. **The "Pull" System (Declarative):** Used in functional components, the `createVdom()` function is an `Effect` that "pulls" from state dependencies. This is very similar in spirit to Vue's `setup` and is fantastic for expressing the UI as a pure function of its state.
42
+ 2. **The "Push" System (Imperative):** This is the unique part. Every reactive config also has optional `afterSet<PropertyName>()` hooks. This is more than just a `watch`er; it's a powerful, class-based tool for validation, transformation, and orchestrating complex business logic. For example, changing a grid column's `dataIndex` could trigger a store reload, update headers, and recalculate summary rows, all within a clean, predictable `afterSetDataIndex` hook.
45
43
 
46
- ### 3. Component Execution Model: Precision vs. Optimization
44
+ This hybrid approach gives developers the choice between elegant, declarative rendering and powerful, imperative control for any given state change, all within a single, unified model.
47
45
 
48
- * **Vue.js: Fine-Grained Reactivity & Optimized Re-Renders**
49
- * Vue's reactivity system is one of its most celebrated features. When a piece of reactive state (e.g., from `ref` or `reactive`) changes, Vue automatically tracks which components depend on it and triggers updates only for those specific components.
50
- * It avoids the "cascading re-render" problem of React by default. If a parent component re-renders, it will not unnecessarily re-render child components whose props have not changed. This is a significant performance advantage and a core part of Vue's design.
46
+ ### 3. Component Lifecycle: Optimized for Rendering vs. Built for Persistence
51
47
 
52
- * **Neo.mjs: Surgical Effects & Automatic Efficiency**
53
- * Neo.mjs's model achieves a similar outcome through a different mechanism. A component's `createVdom` method is wrapped in a `Neo.core.Effect`. This effect automatically and dynamically tracks its dependencies—the specific `config` values it reads.
54
- * When a config value changes, only the specific `createVdom` effects that depend on that *exact* piece of state are queued for re-execution. There are no cascading re-renders. If a parent's `createVdom` re-runs, but the configs passed to a child have not changed, the child component's `createVdom` function is **never executed**.
55
- * **Benefit (Zero Manual Optimization):** Like Vue, this fine-grained reactivity eliminates the need for manual memoization (`memo`, `useCallback`, `useMemo`) that plagues React development. The framework handles dependency tracking automatically and precisely, delivering optimal performance out-of-the-box.
48
+ * **Vue.js: A Rich, Main-Thread Lifecycle**
49
+ * Vue provides a comprehensive set of lifecycle hooks (`onMounted`, `onUnmounted`, etc.) that give developers fine-grained control over a component's life within the main-thread environment. This is excellent for managing side effects related to the DOM, such as integrating third-party libraries.
56
50
 
57
- ### 4. Scaling Complexity: Linear Effort vs. Main-Thread Limits
58
-
59
- A key differentiator between the frameworks is how they handle growing application complexity.
60
-
61
- * **Vue.js: Optimized for Main-Thread Scalability**
62
- * Vue is designed to scale gracefully on the Main Thread. Its reactivity system and compiler optimizations ensure that as an application grows, performance remains high. State management with Pinia is also highly optimized.
63
- * However, the fundamental limitation remains: all work competes for the same single thread. For extremely data-intensive applications, like real-time financial dashboards or complex graphical editors, the Main Thread can still become a bottleneck, no matter how optimized the framework is.
64
-
65
- * **Neo.mjs: Built-in Efficiency and Linear Effort**
66
- * Neo.mjs's architecture is designed to handle this scenario effortlessly. Multiple state changes are automatically batched into a single, de-duplicated update cycle via the `EffectBatchManager`.
67
- * In a complex dashboard scenario, if a value in a global `StateProvider` changes, only the `createVdom` effects in components that *directly depend on that specific value* will re-run. All other components remain untouched. The crucial difference is that this logic runs in the **App Worker**, and the VDOM diffing runs in the **VDom Worker**, leaving the Main Thread free.
68
- * This leads to a **linear relationship between complexity and effort**. As you add more components, you don't need to add more performance optimizations. The framework's core design ensures that updates are always surgical and efficient, allowing developers to focus on features instead of fighting the rendering engine. This is a direct result of the sophisticated, multi-layered batching and aggregation built into the framework's core.
51
+ * **Neo.mjs: A Stable Lifecycle for Asynchronous and Multi-Window Apps**
52
+ * Neo.mjs's lifecycle is designed to solve problems that are architecturally difficult in a single-threaded world.
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
+ * **`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.
69
55
 
70
56
  ### Other Considerations:
71
57
 
@@ -74,19 +60,19 @@ A key differentiator between the frameworks is how they handle growing applicati
74
60
  * **`dist/esm`:** Deploys as native ES Modules, preserving the dev mode's file structure.
75
61
  * **`dist/production`:** Generates highly optimized, thread-specific bundles using Webpack.
76
62
  * **`dist/development`:** A bundled but unminified environment for debugging production-specific issues.
77
- * **Dynamic Module Loading:** Neo.mjs uniquely supports dynamically loading code-based modules (even with arbitrary `import` statements) from different environments at runtime, a powerful feature for plugin architectures.
78
63
 
79
64
  * **Templates vs. Plain Objects:** Vue primarily uses HTML-like templates in SFCs (requiring a build step). Neo.mjs uses plain JavaScript objects for VDOM (no compilation needed for VDOM definition).
80
65
  * **Ecosystem & Maturity:** Vue has a massive, mature ecosystem with a rich collection of libraries, tools (like Vite and Vue Devtools), and extensive community support. Neo.mjs has a smaller but dedicated community, with a focus on framework-level solutions and integrated features.
81
- * **Learning Curve:** Vue is famous for its gentle learning curve and excellent documentation. Neo.mjs has a steeper initial learning curve due to its worker-based architecture, but once understood, it offers inherent performance benefits that don't require manual tuning.
82
66
  * **Dependency Management (Batteries Included):** Vue projects, while often leaner than React, still rely on `node_modules` and a build toolchain. Neo.mjs is a "batteries included" framework with zero real runtime dependencies outside of its own core. This native ES Module approach significantly reduces complexity and dependency management overhead.
83
67
 
84
- ## Conclusion: Why Neo.mjs Offers Significant Technical Advantages Over Vue.js
68
+ ## Conclusion: Why Neo.mjs Offers a Different Path to Performance
69
+
70
+ For many websites and standard business applications, Vue's performance and developer experience are best-in-class for the main-thread paradigm. It is an exceptional framework.
85
71
 
86
- For applications where guaranteed Main Thread responsiveness, high performance under load, leveraging multi-core processing, and long-term maintainability are paramount, Neo.mjs presents a compelling and technically superior alternative.
72
+ Neo.mjs offers a different solution for a different class of problems. For applications where guaranteed UI fluidity is a non-negotiable business requirement, or for those pushing the boundaries of complexity with data-intensive UIs or multi-window environments, Neo.mjs provides a fundamentally more robust and scalable architecture.
87
73
 
88
- * **Unblocked Main Thread & Inherent Performance:** While Vue is highly optimized for the Main Thread, Neo.mjs's unique worker-based architecture fundamentally removes application logic from the Main Thread entirely. This is not an optimization strategy but a core architectural principle, ensuring the UI remains fluid and responsive even during heavy computations that would challenge any single-threaded framework.
89
- * **True Parallelism:** Neo.mjs doesn't just optimize tasks on the Main Thread; it runs them in parallel on separate threads. This provides a higher performance ceiling for complex, data-intensive applications.
90
- * **Linear Effort for Complexity:** Like Vue, Neo.mjs avoids unnecessary re-renders. However, by offloading this work to workers, it ensures that even complex update cycles have zero impact on the Main Thread's availability, allowing for more scalable and maintainable applications in the long run.
74
+ * **True Parallelism:** Neo.mjs doesn't just optimize tasks on the main thread; it runs them in parallel on separate threads, providing a higher performance ceiling.
75
+ * **Architectural Solutions for Complex Problems:** The hybrid reactivity model and the async-aware, persistent lifecycle are designed to solve complex orchestration and asynchronous challenges at the framework level.
76
+ * **Future-Proofing for Complexity:** By building on a multi-threaded foundation, Neo.mjs is architecturally prepared for the increasingly complex and data-intensive applications of the future.
91
77
 
92
- The choice between them depends on the specific application's needs. For many content-driven sites and standard business applications, Vue's performance and developer experience are excellent. For applications pushing the boundaries of complexity and performance, where guaranteed UI fluidity is a critical requirement, Neo.mjs offers a fundamentally more robust and scalable architecture.
78
+ The choice depends on the project's goals. If you are building a highly interactive SPA and hitting the limits of the main thread, or if you are envisioning a true multi-window desktop-like experience on the web, Neo.mjs offers a compelling and powerful alternative.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name" : "neo.mjs",
3
- "version" : "10.0.1",
3
+ "version" : "10.1.0",
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" : {
@@ -48,28 +48,28 @@ $text-color : #bbb;
48
48
  --button-use-gradients : true;
49
49
 
50
50
  // {module: Button, ui: --ghost}
51
- --button-ghost-background-color : var(--button-text-color);
51
+ --button-ghost-background-color : transparent;
52
52
  --button-ghost-background-color-active : #373a3c;
53
53
  --button-ghost-background-color-disabled : inherit;
54
- --button-ghost-background-color-hover : var(--button-text-color);
54
+ --button-ghost-background-color-hover : transparent;
55
55
  --button-ghost-background-image : none;
56
56
  --button-ghost-badge-background-color : var(--button-badge-color);
57
57
  --button-ghost-badge-color : var(--button-badge-background-color);
58
- --button-ghost-border : 1px #{$border-style} var(--button-background-color);
58
+ --button-ghost-border : none;
59
59
  --button-ghost-border-active : 1px #{$border-style} #282828;
60
60
  --button-ghost-border-disabled : inherit;
61
- --button-ghost-border-hover : 1px #{$border-style} #282828;
61
+ --button-ghost-border-hover : 1px #{$border-style} #64B5F6;
62
62
  --button-ghost-border-pressed : 1px #{$border-style} #5d83a7;
63
- --button-ghost-glyph-color : var(--button-background-color);
64
- --button-ghost-glyph-color-active : var(--button-background-color);
63
+ --button-ghost-glyph-color : var(--button-text-color);
64
+ --button-ghost-glyph-color-active : var(--button-text-color);
65
65
  --button-ghost-glyph-color-disabled : inherit;
66
- --button-ghost-glyph-color-hover : var(--button-background-color);
66
+ --button-ghost-glyph-color-hover : var(--button-text-color);
67
67
  --button-ghost-opacity-disabled : var(--neo-disabled-opacity);
68
68
  --button-ghost-ripple-background-color : inherit;
69
- --button-ghost-text-color : var(--button-background-color);
70
- --button-ghost-text-color-active : var(--button-background-color);
69
+ --button-ghost-text-color : var(--button-text-color);
70
+ --button-ghost-text-color-active : var(--button-text-color);
71
71
  --button-ghost-text-color-disabled : inherit;
72
- --button-ghost-text-color-hover : var(--button-background-color);
72
+ --button-ghost-text-color-hover : var(--button-text-color);
73
73
 
74
74
  // {module: Button, ui: --secondary}
75
75
  --button-secondary-background-color : var(--button-text-color);
@@ -299,12 +299,12 @@ const DefaultConfig = {
299
299
  useVdomWorker: true,
300
300
  /**
301
301
  * buildScripts/injectPackageVersion.mjs will update this value
302
- * @default '10.0.1'
302
+ * @default '10.1.0'
303
303
  * @memberOf! module:Neo
304
304
  * @name config.version
305
305
  * @type String
306
306
  */
307
- version: '10.0.1'
307
+ version: '10.1.0'
308
308
  };
309
309
 
310
310
  Object.assign(DefaultConfig, {
@@ -412,7 +412,7 @@ class Collection extends Base {
412
412
  * Removes all items and clears the map
413
413
  */
414
414
  clear() {
415
- this.splice(0, this.getCount())
415
+ this.splice(0, this.count)
416
416
  }
417
417
 
418
418
  /**
@@ -429,7 +429,7 @@ class Collection extends Base {
429
429
  clearSilent() {
430
430
  let me = this;
431
431
 
432
- me._items.splice(0, me.getCount());
432
+ me._items.splice(0, me.count);
433
433
  me.map.clear()
434
434
  }
435
435
 
@@ -817,10 +817,10 @@ class Collection extends Base {
817
817
  * @param {Object} fn.item The current collection item
818
818
  * @param {Object} scope=this The scope in which the passed function gets executed
819
819
  * @param {Number} start=0 The start index
820
- * @param {Number} end=this.getCount() The end index (up to, last value excluded)
820
+ * @param {Number} end=this.count The end index (up to, last value excluded)
821
821
  * @returns {Array} Returns an empty Array in case no items are found
822
822
  */
823
- findBy(fn, scope=this, start=0, end=this.getCount()) {
823
+ findBy(fn, scope=this, start=0, end=this.count) {
824
824
  let me = this,
825
825
  items = [],
826
826
  i = start;
@@ -1041,7 +1041,7 @@ class Collection extends Base {
1041
1041
  * @returns {Object}
1042
1042
  */
1043
1043
  last() {
1044
- return this._items[this.getCount() -1]
1044
+ return this._items[this.count -1]
1045
1045
  }
1046
1046
 
1047
1047
  /**
@@ -1102,7 +1102,7 @@ class Collection extends Base {
1102
1102
  * @returns {Object} The removed element from the collection; undefined if the collection is empty.
1103
1103
  */
1104
1104
  pop() {
1105
- let mutation = this.splice(this.getCount() -1, 1);
1105
+ let mutation = this.splice(this.count -1, 1);
1106
1106
  return mutation.removedItems[0]
1107
1107
  }
1108
1108
 
@@ -1122,7 +1122,7 @@ class Collection extends Base {
1122
1122
  */
1123
1123
  remove(key) {
1124
1124
  this.splice(0, Array.isArray(key) ? key : [key]);
1125
- return this.getCount()
1125
+ return this.count
1126
1126
  }
1127
1127
 
1128
1128
  /**
@@ -1132,7 +1132,7 @@ class Collection extends Base {
1132
1132
  */
1133
1133
  removeAt(index) {
1134
1134
  this.splice(index, 1);
1135
- return this.getCount()
1135
+ return this.count
1136
1136
  }
1137
1137
 
1138
1138
  /**
@@ -1310,7 +1310,7 @@ class Collection extends Base {
1310
1310
  */
1311
1311
  unshift(item) {
1312
1312
  this.splice(0, 0, item);
1313
- return this.getCount()
1313
+ return this.count
1314
1314
  }
1315
1315
  }
1316
1316
 
@@ -87,11 +87,11 @@ class Abstract extends Base {
87
87
  */
88
88
  parentComponent_: null,
89
89
  /**
90
- * @member {String|null} parentId_=null
91
- * @protected
90
+ * The parent component id or document.body
91
+ * @member {String} parentId_='document.body'
92
92
  * @reactive
93
93
  */
94
- parentId_: null,
94
+ parentId_: 'document.body',
95
95
  /**
96
96
  * Optionally add a state.Provider to share state data with child components
97
97
  * @member {Object|null} stateProvider_=null
@@ -32,7 +32,7 @@ class RecordFactory extends Base {
32
32
  }
33
33
 
34
34
  /**
35
- * Assigns model based default values to a data object
35
+ * Assigns model-based default values to a data object
36
36
  * @param {Object} data
37
37
  * @param {Neo.data.Model} model
38
38
  * @returns {Object}
@@ -40,11 +40,13 @@ class RecordFactory extends Base {
40
40
  assignDefaultValues(data, model) {
41
41
  model.fieldsMap.forEach((field, fieldName) => {
42
42
  if (Object.hasOwn(field, 'defaultValue')) {
43
+ const defaultValue = Neo.isFunction(field.defaultValue) ? field.defaultValue() : field.defaultValue;
44
+
43
45
  // We could always use Neo.assignToNs() => the check is just for improving the performance
44
46
  if (model.hasNestedFields) {
45
- Neo.assignToNs(fieldName, field.defaultValue, data, false)
47
+ Neo.assignToNs(fieldName, defaultValue, data, false)
46
48
  } else if (data[fieldName] === undefined) {
47
- data[fieldName] = field.defaultValue
49
+ data[fieldName] = defaultValue
48
50
  }
49
51
  }
50
52
  });
@@ -73,7 +73,7 @@ class DragZone extends BaseDragZone {
73
73
  let {owner} = this;
74
74
 
75
75
  if (!(this.leafNodesOnly && !record.isLeaf)) {
76
- return owner.getVdomChild(owner.getItemId(record.id), owner.vdom)
76
+ return owner.getVdomChild(owner.getItemId(record[owner.getKeyProperty()]), owner.vdom)
77
77
  }
78
78
 
79
79
  return null
package/src/grid/Body.mjs CHANGED
@@ -954,7 +954,7 @@ class GridBody extends Component {
954
954
  /**
955
955
  * @param {Object} data
956
956
  * @param {Object[]} data.fields Each field object contains the keys: name, oldValue, value
957
- * @param {Neo.data.Model} data.model The model instance of the changed record
957
+ * @param {Neo.data.Model} data.model The model instance of the changed record
958
958
  * @param {Object} data.record
959
959
  */
960
960
  onStoreRecordChange({fields, record}) {
@@ -370,10 +370,6 @@ class DeltaUpdates extends Base {
370
370
  let me = this,
371
371
  node = DomAccess.getElementOrBody(delta.id);
372
372
 
373
- if (!node) {
374
- console.log('node not found', delta.id);
375
- }
376
-
377
373
  if (node) {
378
374
  Object.entries(delta).forEach(([prop, value]) => {
379
375
  switch (prop) {
@@ -53,7 +53,7 @@ const DomApiRenderer = {
53
53
  // Apply Attributes
54
54
  Object.entries(vnode.attributes).forEach(([key, value]) => {
55
55
  if (voidAttributes.has(key)) {
56
- domNode[key] = (value === 'true' || value === true)
56
+ domNode.toggleAttribute(key, value === 'true' || value === true)
57
57
  } else if (key === 'value') {
58
58
  domNode.value = value
59
59
  } else if (value !== null && value !== undefined) {
@@ -157,6 +157,14 @@ class Model extends Base {
157
157
  super.destroy(...args)
158
158
  }
159
159
 
160
+ /**
161
+ * Important for mapping listeners to view controllers
162
+ * @returns {Neo.controller.Component|null}
163
+ */
164
+ getController() {
165
+ return this.view.getController()
166
+ }
167
+
160
168
  /**
161
169
  * @returns {Array} this.items
162
170
  */
@@ -226,8 +234,9 @@ class Model extends Base {
226
234
  items = [items]
227
235
  }
228
236
 
229
- let me = this,
230
- {view} = me;
237
+ let me = this,
238
+ {view} = me,
239
+ records = [...items]; // Potential records
231
240
 
232
241
  // We hold vdom ids for now, so all incoming selections must be converted.
233
242
  items = items.map(item => item.isRecord ? view.getItemId(item) : Neo.isObject(item) ? item.id : item);
@@ -256,6 +265,7 @@ class Model extends Base {
256
265
  view.onSelect?.(items);
257
266
 
258
267
  me.fire('selectionChange', {
268
+ records,
259
269
  selection: itemCollection
260
270
  })
261
271
  }
package/src/tree/List.mjs CHANGED
@@ -64,9 +64,9 @@ class Tree extends Base {
64
64
  * @member {Object} _vdom
65
65
  */
66
66
  _vdom:
67
- {cn: [
68
- {tag: 'ul', cls: ['neo-list-container', 'neo-list'], tabIndex: -1, cn: []}
69
- ]}
67
+ {cn: [
68
+ {tag: 'ul', cls: ['neo-list-container', 'neo-list'], tabIndex: -1, cn: []}
69
+ ]}
70
70
  }
71
71
 
72
72
  /**
@@ -188,12 +188,15 @@ class Tree extends Base {
188
188
  {folderCls, itemCls} = me,
189
189
  cls = [itemCls],
190
190
  contentCls = [itemCls + '-content'],
191
+ keyProperty = me.getKeyProperty(),
191
192
  itemVdom;
192
193
 
193
194
  if (record.iconCls) {
194
- contentCls.push(
195
- Array.isArray(record.iconCls) ? record.iconCls : record.iconCls.split(' ')
196
- )
195
+ if (Array.isArray(record.iconCls)) {
196
+ contentCls.push(...record.iconCls)
197
+ } else {
198
+ contentCls.push(record.iconCls)
199
+ }
197
200
  }
198
201
 
199
202
  if (record.isLeaf) {
@@ -209,7 +212,7 @@ class Tree extends Base {
209
212
  itemVdom = {
210
213
  tag: 'li',
211
214
  cls,
212
- id : me.getItemId(record.id),
215
+ id : me.getItemId(record[keyProperty]),
213
216
  cn : [{
214
217
  tag : 'span',
215
218
  cls : contentCls,
@@ -416,16 +419,17 @@ class Tree extends Base {
416
419
  * @param {Object} data
417
420
  */
418
421
  onItemClick(node, data) {
419
- let me = this,
420
- {items} = me.store,
421
- i = 0,
422
- len = items.length,
423
- path = data.path.map(e => e.id),
422
+ let me = this,
423
+ {items} = me.store,
424
+ i = 0,
425
+ len = items.length,
426
+ keyProperty = me.getKeyProperty(),
427
+ path = data.path.map(e => e.id),
424
428
  item, record, tmpItem, vnodeId;
425
429
 
426
430
  for (; i < len; i++) {
427
431
  tmpItem = items[i];
428
- vnodeId = me.getItemId(tmpItem.id);
432
+ vnodeId = me.getItemId(tmpItem[keyProperty]);
429
433
 
430
434
  if (path.includes(vnodeId)) {
431
435
  record = tmpItem;
@@ -51,7 +51,7 @@ class TreeBuilder extends Base {
51
51
  if (currentItem.componentId) {
52
52
  // Prune the branch only if we are at the boundary AND the child is not part of a merged update
53
53
  if (depth === 1 && !mergedChildIds?.has(currentItem.componentId)) {
54
- output[childKey].push({componentId: 'neo-ignore', id: item.id || item.componentId});
54
+ output[childKey].push({...currentItem, neoIgnore: true});
55
55
  return // Stop processing this branch
56
56
  }
57
57
  // Expand the branch if it's part of a merged update, or if the depth requires it
@@ -255,7 +255,8 @@ class Helper extends Base {
255
255
  // Case 2: Both nodes are placeholders for the same component
256
256
  (childNode.componentId && childNode.componentId === oldChildNode.componentId)
257
257
  )) {
258
- if (childNode.componentId === 'neo-ignore') {
258
+ if (childNode.neoIgnore) {
259
+ delete childNode.neoIgnore;
259
260
  continue
260
261
  }
261
262