neo.mjs 6.35.0 → 6.37.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.
Files changed (36) hide show
  1. package/README.md +15 -1
  2. package/apps/ServiceWorker.mjs +2 -2
  3. package/apps/colors/view/ViewportController.mjs +27 -17
  4. package/apps/portal/view/home/FeatureSection.mjs +209 -0
  5. package/apps/portal/view/home/parts/Colors.mjs +34 -84
  6. package/apps/portal/view/home/parts/Features.mjs +1 -1
  7. package/apps/portal/view/home/parts/Helix.mjs +36 -95
  8. package/apps/portal/view/home/parts/How.mjs +31 -48
  9. package/apps/portal/view/learn/ContentView.mjs +53 -20
  10. package/examples/ServiceWorker.mjs +2 -2
  11. package/examples/videoMove/MainContainer.mjs +3 -4
  12. package/examples/videoMove/neo-config.json +2 -1
  13. package/package.json +2 -2
  14. package/resources/data/deck/learnneo/pages/benefits/Multi-Threading.md +221 -0
  15. package/resources/data/deck/learnneo/tree.json +12 -13
  16. package/resources/scss/src/apps/portal/Viewport.scss +19 -0
  17. package/resources/scss/src/apps/portal/home/ContentBox.scss +9 -2
  18. package/resources/scss/src/apps/portal/home/FeatureSection.scss +68 -0
  19. package/resources/scss/src/apps/portal/home/parts/Colors.scss +1 -5
  20. package/resources/scss/src/apps/portal/home/parts/Helix.scss +1 -7
  21. package/resources/scss/src/apps/portal/home/parts/How.scss +0 -22
  22. package/resources/scss/src/apps/portal/learn/ContentView.scss +22 -10
  23. package/resources/scss/src/code/LivePreview.scss +1 -0
  24. package/resources/scss/src/examples/videoMove/MainContainer.scss +31 -0
  25. package/resources/scss/theme-neo-light/design-tokens/Core.scss +1 -0
  26. package/src/DefaultConfig.mjs +2 -2
  27. package/src/Neo.mjs +5 -1
  28. package/src/code/LivePreview.mjs +16 -19
  29. package/src/component/Toast.mjs +8 -8
  30. package/src/component/Video.mjs +22 -28
  31. package/src/component/wrapper/AmChart.mjs +15 -8
  32. package/src/main/addon/AmCharts.mjs +1 -1
  33. package/src/manager/DomEvent.mjs +1 -1
  34. package/src/worker/App.mjs +1 -1
  35. package/src/worker/mixin/RemoteMethodAccess.mjs +1 -3
  36. package/resources/data/deck/learnneo/pages/WhyNeo-Multi-Threaded.md +0 -15
@@ -1,10 +1,10 @@
1
- import BaseContainer from './BaseContainer.mjs';
1
+ import FeatureSection from '../FeatureSection.mjs';
2
2
 
3
3
  /**
4
4
  * @class Portal.view.home.parts.How
5
- * @extends Portal.view.home.parts.BaseContainer
5
+ * @extends Portal.view.home.FeatureSection
6
6
  */
7
- class How extends BaseContainer {
7
+ class How extends FeatureSection {
8
8
  static config = {
9
9
  /**
10
10
  * @member {String} className='Portal.view.home.parts.How'
@@ -16,56 +16,39 @@ class How extends BaseContainer {
16
16
  */
17
17
  cls: ['portal-home-parts-how'],
18
18
  /**
19
- * @member {Object} layout={ntype:'hbox',align:'stretch',pack:'center'}
19
+ * @member {Object[]} contentItems
20
20
  */
21
- layout: {ntype: 'hbox', align: 'stretch', pack: 'center'},
22
- /**
23
- * @member {Object[]} items
24
- */
25
- items: [{
21
+ contentItems: [{
26
22
  ntype : 'container',
27
- cls : ['portal-content-text'],
28
- flex : '1',
29
- style : {padding: '2rem'},
30
- layout: {ntype: 'vbox', align: 'center', pack: 'center'},
31
- items : [{
32
- cls : ['neo-h1'],
33
- flex: 'none',
34
- html: 'How?',
35
- tag : 'h1'
36
- }, {
37
- cls : ['neo-h2'],
38
- flex: 'none',
39
- html: 'How Does Neo.mjs Do It?',
40
- tag : 'h2'
41
- }, {
42
- cls : ['neo-h3'],
43
- flex: 'none',
44
- tag : 'p',
45
-
46
- html: [
47
- 'When a Neo.mjs app launches three webworkers are spawned: one that holds app logic, one for tracking delta DOM updates, ',
48
- 'and one for backend calls. Each webworker runs in its own thread, and thus, in its own processor core. ',
49
- 'This means these processes run in parallel: your app logic isn\'t affected by DOM changes or ',
50
- 'by Ajax or socket calls. If you have processor-intensive tasks you can easily run them in their own threads.'
51
- ].join('')
52
- }]
53
- }, {
54
- ntype : 'container',
55
- cls : 'portal-content-wrapper',
56
- flex : '2',
23
+ cls : 'portal-content-container',
57
24
  layout: 'fit',
58
25
  items : [{
59
- ntype : 'container',
60
- cls : 'portal-content-container',
61
- layout: 'fit',
62
- items : [{
63
- cls : 'neo-worker-setup',
64
- tag : 'element-loader',
65
- vdom: {src: '../../resources/images/workers-focus.svg'}
66
- }]
26
+ cls : 'neo-worker-setup',
27
+ tag : 'element-loader',
28
+ vdom: {src: '../../resources/images/workers-focus.svg'}
67
29
  }]
68
- }]
30
+ }],
31
+ /**
32
+ * @member {String} headline='How?'
33
+ */
34
+ headline: 'How?',
35
+ /**
36
+ * @member {String} learnMoreRoute='#/learn/WhyNeo-Speed'
37
+ */
38
+ learnMoreRoute: '#/learn/benefits.Multi-Threading',
39
+ /**
40
+ * @member {String} paragraph
41
+ */
42
+ paragraph: [
43
+ 'When a Neo.mjs app launches 3+ webworkers are spawned: one that holds App logic, one for calculating delta DOM ',
44
+ 'updates, and one for backend calls. Each webworker runs in its own thread, and thus, in its own processor core. ',
45
+ 'This means these processes run in parallel: DOM updates and transitions are not affected by your App logic ',
46
+ 'and always run smooth. Every processor-intensive task runs outside the Main Thread.'
47
+ ].join(''),
48
+ /**
49
+ * @member {String} subHeadline='How does Neo.mjs do it?'
50
+ */
51
+ subHeadline: 'How does Neo.mjs do it?'
69
52
  }
70
53
  }
71
54
 
@@ -3,10 +3,11 @@ import LivePreview from '../../../../src/code/LivePreview.mjs';
3
3
  import {marked} from '../../../../node_modules/marked/lib/marked.esm.js';
4
4
 
5
5
  const
6
- labCloseRegex = /<!--\s*\/lab\s*-->/g,
7
- labOpenRegex = /<!--\s*lab\s*-->/g,
8
- preJsRegex = /<pre\s+data-javascript\s*>([\s\S]*?)<\/pre>/g,
9
- preNeoRegex = /<pre\s+data-neo\s*>([\s\S]*?)<\/pre>/g;
6
+ labCloseRegex = /<!--\s*\/lab\s*-->/g,
7
+ labOpenRegex = /<!--\s*lab\s*-->/g,
8
+ preJsRegex = /<pre\s+data-javascript\s*>([\s\S]*?)<\/pre>/g,
9
+ preNeoRegex = /<pre\s+data-neo\s*>([\s\S]*?)<\/pre>/g,
10
+ preNeoComponentRegex = /<pre\s+data-neo-component\s*>([\s\S]*?)<\/pre>/g;
10
11
 
11
12
  /**
12
13
  * @class Portal.view.learn.ContentView
@@ -108,19 +109,25 @@ class ContentView extends Component {
108
109
  async doFetchContent(record) {
109
110
  let me = this,
110
111
  path = me.getModel().getData('contentPath'),
111
- content, data, html, modifiedHtml, neoDivs;
112
+ content, data, html, modifiedHtml, neoComponents, neoDivs;
112
113
 
113
- path += `/pages/${record.id}.md`;
114
+ path += `/pages/${record.id.replaceAll('.', '/')}.md`;
114
115
 
115
116
  if (record.isLeaf && path) {
116
- data = await fetch(path);
117
- content = await data.text();
118
- content = me.updateContentSectionsStore(content); // also replaces ## with h2 tags
119
- content = `# ${record.name}\n${content}`;
120
- modifiedHtml = await me.highlightPreContent(content);
121
- neoDivs = {};
122
-
123
- // Replace <pre data-neo></neo> with <div id='neo-preview-1'/>
117
+ data = await fetch(path);
118
+ content = await data.text();
119
+ content = me.updateContentSectionsStore(content); // also replaces ## with h2 tags
120
+ content = `# ${record.name}\n${content}`;
121
+ modifiedHtml = await me.highlightPreContent(content);
122
+ neoComponents = {};
123
+ neoDivs = {}
124
+
125
+ // Replace <pre neo-component></pre> with <div id='neo-component-x'/>
126
+ // and create a map keyed by ID, whose value is the javascript
127
+ // from the <pre>
128
+ modifiedHtml = me.extractNeoComponents(modifiedHtml, neoComponents);
129
+
130
+ // Replace <pre data-neo></pre> with <div id='neo-preview-1'/>
124
131
  // and create a map keyed by ID, whose value is the javascript
125
132
  // from the <pre>
126
133
  modifiedHtml = me.extractNeoContent(modifiedHtml, neoDivs);
@@ -132,7 +139,20 @@ class ContentView extends Component {
132
139
 
133
140
  me.html = html;
134
141
 
135
- await me.timeout(50);
142
+ await me.timeout(100);
143
+
144
+ Object.keys(neoComponents).forEach(key => {
145
+ Neo.create({
146
+ appName : me.appName,
147
+ autoMount : true,
148
+ autoRender : true,
149
+ className : 'Neo.component.Base',
150
+ parentComponent: me,
151
+ parentId : key,
152
+ windowId : me.windowId,
153
+ ...neoComponents[key]
154
+ })
155
+ });
136
156
 
137
157
  Object.keys(neoDivs).forEach(key => {
138
158
  // Create LivePreview for each iteration, set value to neoDivs[key]
@@ -161,15 +181,28 @@ class ContentView extends Component {
161
181
  * @param {Object} map
162
182
  * @returns {String}
163
183
  */
164
- extractNeoContent(htmlString, map) {
165
- // 1. Replace <pre data-neo> with <div id='neo-preview-2'/>
184
+ extractNeoComponents(htmlString, map) {
185
+ // 1. Replace <pre data-neo-component> with <div id='neo-learn-content-component-x'/>
166
186
  // and update map with key/value pairs, where the key is the ID and the value is the <pre> contents.
187
+ // Replace the content with tokens, and create a promise to update the corresponding content
188
+ return htmlString.replace(preNeoComponentRegex, (match, preContent) => {
189
+ const key = Neo.core.IdGenerator.getId('learn-content-component');
190
+ map[key] = JSON.parse(preContent);
191
+ return `<div id="${key}"></div>`
192
+ })
193
+ }
167
194
 
168
- let count = 0;
169
-
195
+ /**
196
+ * @param {String} htmlString
197
+ * @param {Object} map
198
+ * @returns {String}
199
+ */
200
+ extractNeoContent(htmlString, map) {
201
+ // 1. Replace <pre data-neo> with <div id='neo-pre-live-preview-x'/>
202
+ // and update map with key/value pairs, where the key is the ID and the value is the <pre> contents.
170
203
  // Replace the content with tokens, and create a promise to update the corresponding content
171
204
  return htmlString.replace(preNeoRegex, (match, preContent) => {
172
- const key = `pre-live-preview-${Neo.core.IdGenerator.getId()}-${count++}`;
205
+ const key = Neo.core.IdGenerator.getId('pre-live-preview');
173
206
  map[key] = preContent;
174
207
  return `<div id="${key}"></div>`
175
208
  })
@@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase {
20
20
  */
21
21
  singleton: true,
22
22
  /**
23
- * @member {String} version='6.35.0'
23
+ * @member {String} version='6.37.0'
24
24
  */
25
- version: '6.35.0'
25
+ version: '6.37.0'
26
26
  }
27
27
 
28
28
  /**
@@ -9,11 +9,12 @@ import Viewport from '../../src/container/Viewport.mjs';
9
9
  class MainContainer extends Viewport {
10
10
  static config = {
11
11
  className: 'Neo.examples.videoMove.MainContainer',
12
+ cls : ['examples-videomove-maincontainer'],
12
13
  layout : {ntype: 'vbox', align: 'stretch'},
13
- style : {padding: '50px'},
14
14
 
15
15
  items: [{
16
16
  ntype : 'container',
17
+ cls : ['video-wrapper'],
17
18
  layout: {ntype: 'hbox', align: 'stretch'},
18
19
 
19
20
  itemDefaults: {
@@ -23,7 +24,6 @@ class MainContainer extends Viewport {
23
24
 
24
25
  items: [{
25
26
  reference: 'container-1',
26
- style : {backgroundColor: 'rgb(139,166,255)', padding: '50px'},
27
27
 
28
28
  items: [{
29
29
  module : Video,
@@ -31,8 +31,7 @@ class MainContainer extends Viewport {
31
31
  url : 'https://video-ssl.itunes.apple.com/itunes-assets/Video125/v4/a0/57/54/a0575426-dd8e-2d25-bdf3-139702870b50/mzvf_786190431362224858.640x464.h264lc.U.p.m4v'
32
32
  }]
33
33
  }, {
34
- reference: 'container-2',
35
- style : {backgroundColor: 'rgb(139,166,255)', marginLeft: '50px', padding: '50px'}
34
+ reference: 'container-2'
36
35
  }]
37
36
  }, {
38
37
  ntype : 'container',
@@ -2,5 +2,6 @@
2
2
  "appPath" : "examples/videoMove/app.mjs",
3
3
  "basePath" : "../../",
4
4
  "environment": "development",
5
- "mainPath" : "./Main.mjs"
5
+ "mainPath" : "./Main.mjs",
6
+ "themes" : ["neo-theme-neo-light"]
6
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neo.mjs",
3
- "version": "6.35.0",
3
+ "version": "6.37.0",
4
4
  "description": "The webworkers driven UI framework",
5
5
  "type": "module",
6
6
  "repository": {
@@ -48,7 +48,7 @@
48
48
  "chalk": "^5.3.0",
49
49
  "clean-webpack-plugin": "^4.0.0",
50
50
  "commander": "^12.1.0",
51
- "cssnano": "^7.0.4",
51
+ "cssnano": "^7.0.5",
52
52
  "envinfo": "^7.13.0",
53
53
  "fs-extra": "^11.2.0",
54
54
  "highlightjs-line-numbers.js": "^2.8.0",
@@ -0,0 +1,221 @@
1
+ ## How many Cores are on a Computer or Smartphone?
2
+ In case you are using a Mac, you can click on the top-left Apple Icon,
3
+ then on "About this Mac" and it will show you something like:
4
+
5
+ > Processor 3,2 GHz 8-Core Intel Xeon W
6
+
7
+ With the Apple silicon series even more: "Apple M2 Ultra" provides 24 CPU cores.
8
+
9
+ An iPhone has 6 CPU cores.
10
+
11
+ TL-BR: Every computer or smartphone has several cores available.
12
+
13
+ This means that you can run multiple threads concurrently.
14
+
15
+ > Would you build a car using just one engine cylinder?
16
+
17
+ If your answer is "Of course not! It would be way slower!",
18
+ then you should read this article carefully.
19
+
20
+ ## How many Cores does a Browser use?
21
+
22
+ On its own, a Browser will just use ***one*** core per tab / window.
23
+
24
+ Meaning: your Angular or React apps look like this:
25
+
26
+ ![Current State of Apps](https://raw.githubusercontent.com/neomjs/pages/main/resources_pub/images/apps-today.png)
27
+
28
+ The more JavaScript tasks are running inside your app, the slower it will get.
29
+ The worst scenario is a complete UI freeze where your one core is at 100%
30
+ and all other cores are completely idle.
31
+
32
+ This is ***not*** scalable at all.
33
+
34
+ _[Side note] In case you are creating simple, small and rather static websites or apps, this setup can be sufficient._
35
+
36
+ ## Web Workers API
37
+
38
+ > Web Workers makes it possible to run a script operation in a background thread separate from the main execution thread
39
+ > of a web application. The advantage of this is that laborious processing can be performed in a separate thread,
40
+ > allowing the main (usually the UI) thread to run without being blocked/slowed down.
41
+
42
+ Source: <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API">MDN: Web Workers API</a>
43
+
44
+ > The W3C and WHATWG envision web workers as long-running scripts that are not interrupted by scripts that respond to
45
+ > clicks or other user interactions. Keeping such workers from being interrupted by user activities should allow
46
+ > Web pages to remain responsive at the same time as they are running long tasks in the background.
47
+ >
48
+ > The simplest use of workers is for performing a computationally expensive task without interrupting the user interface.
49
+
50
+ Source: <a target="_blank" href="https://en.wikipedia.org/wiki/Web_worker">Wikipedia: Web worker</a>
51
+
52
+ So, while JavaScript itself is single-threaded as a language, using workers enables us to use multiple cores
53
+ concurrently and end this scalability nightmare.
54
+
55
+ Let the following quote really sink in:
56
+ > The simplest use of workers is for performing a computationally expensive task without interrupting the user interface.
57
+
58
+ It leads to the question: "What is the most expensive task?"
59
+
60
+ The answer is simple: "An UI Framework or Library itself as well as the apps we build with it."
61
+
62
+ This is leading to the idea: Let us move everything we can out of the main thread,
63
+ so that this one can purely focus on what it is intended to do: manipulating the DOM.
64
+
65
+ In case your apps are no longer running in main, there is nothing left which can slow down or block your UI or create memory leaks.
66
+
67
+ This thought is leading to the following concept:
68
+
69
+ ## The "An Application Worker being the Main Actor" Paradigm
70
+ <a target="_blank" href="https://github.com/surma">Surma</a>
71
+ wrote a very nice article on how the Actor Model can and should get applied to the web
72
+ <a target="_blank" href="https://surma.dev/things/actormodel/">here</a> in 2017.
73
+ At this point, we already had the Neo.mjs setup running, but sadly not open-sourced it yet.
74
+
75
+ > It struck me that the Actor Model could work on the web. The more I thought about it, the more it seems like a natural fit.
76
+
77
+ In case you are not familiar with what an "actor" means, definitely read it first.
78
+
79
+ To resolve this performance bottleneck, we want to get main threads as idle as possible, so that they can fully focus on
80
+ rendering / dynamically manipulating the DOM:
81
+
82
+ ![App Worker Concept](https://raw.githubusercontent.com/neomjs/pages/main/resources_pub/images/app-worker.png)
83
+
84
+ The worst case that could happen now is that your app worker will slow down and this core runs at 100%. However,
85
+ this will not affect your UI (rendering thread → main).
86
+ Probably the best solution for single page apps (SPAs) as well as multi-window apps (MWAs) looks like this:
87
+
88
+ <pre data-neo-component>
89
+ {
90
+ "cls" : "neo-worker-setup",
91
+ "tag" : "element-loader",
92
+ "vdom": {"src": "../../resources/images/workers-focus.svg"}
93
+ }
94
+ </pre>
95
+
96
+ To prevent the app worker from handling too much logic, we can optionally spawn more workers.
97
+ Each thread has its fixed scope. Let us take a quick look into each of them.
98
+
99
+ ### Main Thread
100
+
101
+ The `index.html` file of your Neo.mjs App will by default have an empty body tag and only import the
102
+ `MicroLoader.mjs` file. The loader will fetch your `neo-config.json` and afterwards dynamically import the
103
+ main thread part of the framework. This part is as lightweight as possible: around 40KB in dist/production.
104
+
105
+ * Main will start the workers
106
+ * Main will apply delta-updates to the DOM
107
+ * Main will forward serialised DOM events to the App Worker
108
+ * Main can import Main Thread Addons (e.g. libraries which rely on direct DOM access)
109
+
110
+ The important part: Main is ***not*** aware of Neo.mjs Apps or Components.
111
+ It is purely focussing on mounting and updating DOM nodes to ensure that literally nothing can slow down
112
+ your UI or even freeze it.
113
+
114
+ This concept is called OMT (Off the Main Thread), and you can find quite a bunch of infos on the web.
115
+
116
+ Example-Overview: <a target="_blank" href="https://css-tricks.com/off-the-main-thread/">CSS-Tricks: Off the Main Thread</a>
117
+
118
+ ### Application Worker
119
+
120
+ The most important actor is the App-Worker. After construction, it will lazy-load (dynamically import)
121
+ your main App.
122
+
123
+ * Your App will import your used Components
124
+ * Meaning: your Component instances live within the App-Worker
125
+ * View Models & View Controllers also live here
126
+ * Most parts of the Neo.mjs Framework live here
127
+ * You can directly communicate with other Actors via remote method access (RPC)
128
+
129
+ As a developer, you will probably spend 95% of your time working within this actor.
130
+
131
+ There is a catch: Workers have by design no access to the DOM.
132
+ Meaning: `window` and `window.document` are undefined.
133
+
134
+ This enforces us to use an abstraction layer to describe the DOM (often called virtual DOM or vdom).
135
+ Compared to other implementations like React, the Neo.mjs vdom is super lightweight. It is using a JSON-like
136
+ syntax => just nested objects & arrays, since we need to be able to serialise it.
137
+
138
+ In German, we would call the concept "Kindersicherung" (parental controls), which has the benefit that we
139
+ can ensure that junior developers can not mess up the real DOM with invalid operations.
140
+
141
+ Some libraries like <a target="_blank" href="https://www.solidjs.com/">SOLIDJS</a> are claiming that using
142
+ virtual DOM is a bad thing. They are referring to the React implementation of it, which is very different
143
+ to the Neo.mjs approach. While the SOLIDJS concept to directly modify DOM nodes instead is charming in
144
+ its own way, it does limit you for staying single-threaded. Their Components must live within the main thread.
145
+
146
+ ### Virtual DOM Worker
147
+
148
+ Like the main thread, the vdom-worker is not aware of your Apps or Components.
149
+
150
+ Every Component has a vdom tree (new state) and a vnode tree (current state).
151
+
152
+ Once we applied all our desired changes to the vdom tree, we can start an update-cycle.
153
+ This is a triangle communication:
154
+ 1. The App-Worker will send the vdom & vnode trees to the VDom-Worker
155
+ 2. The VDom-Worker will transform the vdom tree into a vnode tree
156
+ 3. The VDom-Worker will compare the new & old vnode trees to calculate the required delta-updates (diffing)
157
+ 4. The VDom-Worker will send the deltas & the new vnode to the Main Thread
158
+ 5. Main will apply the deltas (piped through `requestAnimationFrame()`) and pass the vnode to the App-Worker
159
+ 6. At this point (async), the next update-cycle can start
160
+
161
+ If you think about it: This solves the problem of requiring an "immutable state tree" out of the box.
162
+ We can modify vdom trees multiple times before starting an update-cycle. Once we do, the vdom gets serialised
163
+ => immutable and sent to the VDom-Worker. We can immediately add new changes to the vdom, which will not interfere with
164
+ the current update-cycle, but get used inside the next cycle.
165
+
166
+ ### Data Worker
167
+
168
+ The main responsibility of the Data-Worker is to communicate with the Backend / Cloud.
169
+ Mostly, but not limited to:
170
+ 1. Ajax Calls
171
+ 2. SocketConnection messages
172
+
173
+ In case you are in need to apply expensive data-transformations before sending / after receiving data,
174
+ these transformations should happen here. Think about the data reader / writer concept.
175
+
176
+ ### Canvas Worker
177
+
178
+ > Can a Worker really not access the DOM?
179
+
180
+ There is one exception, which is called
181
+ <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas">OffscreenCanvas</a>.
182
+
183
+ Meaning: We can transfer the ownership of a Canvas DOM node to a worker.
184
+
185
+ This enables us to work with charting or maps libraries outside the App-Worker & outside the main thread.
186
+
187
+ Here is an example Blog-Post to show you how powerful this concept can be:</br>
188
+ <a target="_blank" href="https://itnext.io/rendering-3d-offscreen-getting-max-performance-using-canvas-workers-88c207cbcdc2?source=friends_link&sk=7ee0851ff6043c4a79248ff5a20a23fc">Rendering 3d offscreen: Getting max performance using canvas workers</a>
189
+
190
+ In the future, we might create an own OffscreenCanvas charting library for Neo.mjs.
191
+
192
+ ### Task Worker
193
+
194
+ In case you have specific expensive tasks, which don't really fit well into the other actors,
195
+ you can optionally move them into the Task-Worker.
196
+
197
+ E.g. calculating Fibonacci numbers would be a good fitting example.
198
+
199
+ ### Service Worker
200
+
201
+ By design, <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API">Service-Workers</a>
202
+ are responsible for caching assets (Images, CSS, JS-bundles)
203
+
204
+ > Service workers essentially act as proxy servers that sit between web applications, the browser,
205
+ > and the network (when available). They are intended, among other things, to enable the creation of
206
+ > effective offline experiences, intercept network requests, and take appropriate action based on whether
207
+ > the network is available, and update assets residing on the server. They will also allow access to push
208
+ > notifications and background sync APIs.
209
+
210
+ While Neo.mjs is not purely focussing on making the very first page load as fast as possible,
211
+ it can make all following page loads happen almost instantly.
212
+
213
+ We can cache the JS bundles which generate the desired markup (HTML) directly on the client.
214
+ There is no need to stream HTML again to the client (server side rendering => SSR),
215
+ since we already have everything in place to recreate the fully hydrated version in the blink of an eye.
216
+
217
+ Via remote method access, the App-Worker can directly communicate with the Service-Worker at run-time.
218
+ This enables us to preload / pre-cache assets on the fly.
219
+
220
+ More infos on this topic:</br>
221
+ <a target="_blank" href="https://itnext.io/predictive-offline-support-for-assets-you-have-not-used-yet-aeeccccd3754?source=friends_link&sk=e946e0f25f508e6a8cec4136400291a3">Predictive offline support for assets you have not used yet</a>
@@ -1,15 +1,15 @@
1
1
  {"data": [
2
2
  {"name": "Welcome!", "parentId": null, "isLeaf": true, "id": "Welcome" },
3
- {"name": "Benefits?", "parentId": null, "isLeaf": false, "id": "WhyNeo"},
3
+ {"name": "Benefits", "parentId": null, "isLeaf": false, "id": "WhyNeo"},
4
4
  {"name": "Introduction ", "parentId": "WhyNeo", "isLeaf": true, "id": "WhyNeo-Intro"},
5
- {"name": "Multi-Threaded", "parentId": "WhyNeo", "isLeaf": true, "id": "WhyNeo-Multi-Threaded"},
5
+ {"name": "Multi-Threading", "parentId": "WhyNeo", "isLeaf": true, "id": "benefits.Multi-Threading"},
6
6
  {"name": "Extreme Speed", "parentId": "WhyNeo", "isLeaf": true, "id": "WhyNeo-Speed"},
7
7
  {"name": "Multi-Window Applications", "parentId": "WhyNeo", "isLeaf": true, "id": "WhyNeo-Multi-Window"},
8
8
  {"name": "Quick Application Development", "parentId": "WhyNeo", "isLeaf": true, "id": "WhyNeo-Quick"},
9
9
  {"name": "Complexity and Effort", "parentId": "WhyNeo", "isLeaf": true, "id": "WhyNeo-Effort"},
10
10
  {"name": "Features and Benefits Summary", "parentId": "WhyNeo", "isLeaf": true, "id": "WhyNeo-Features"},
11
11
  {"name": "Getting Started", "parentId": null, "isLeaf": false, "id": "GettingStarted", "collapsed": true},
12
- {"name": "Setup", "parentId": "GettingStarted", "isLeaf": true, "id": "Setup", "hidden": false},
12
+ {"name": "Setup", "parentId": "GettingStarted", "isLeaf": true, "id": "Setup"},
13
13
  {"name": "Workspaces and Applications", "parentId": "GettingStarted", "isLeaf": true, "id": "2023-10-14T19-25-08-153Z"},
14
14
  {"name": "Describing a View", "parentId": "GettingStarted", "isLeaf": true, "id": "DescribingTheUI"},
15
15
  {"name": "Events", "parentId": "GettingStarted", "isLeaf": true, "id": "Events"},
@@ -20,24 +20,23 @@
20
20
  {"name": "Tutorials", "parentId": null, "isLeaf": false, "expanded": false, "id": "Tutorials", "collapsed": true},
21
21
  {"name": "Rock Scissors Paper", "parentId": "Tutorials", "isLeaf": true, "expanded": false, "id": "RSP", "hidden": true},
22
22
  {"name": "Earthquakes", "parentId": "Tutorials", "isLeaf": true, "expanded": false, "id": "Earthquakes", "collapsed": true},
23
-
24
23
  {"name": "Todo List", "parentId": "Tutorials", "isLeaf": true, "expanded": false, "id": "TodoList"},
25
24
  {"name": "Guides", "parentId": null, "isLeaf": false, "expanded": false, "id": "InDepth", "collapsed": true},
26
25
  {"name": "Config", "parentId": "InDepth", "isLeaf": false, "id": "Config"},
27
- {"name": "Instance Lifecycle", "parentId": "InDepth", "isLeaf": false, "id": "InstanceLifecycle"},
28
- {"name": "User Input (Forms)", "parentId": "InDepth", "isLeaf": false, "id": "Forms"},
26
+ {"name": "Instance Lifecycle", "parentId": "InDepth", "isLeaf": false, "id": "InstanceLifecycle", "hidden": true},
27
+ {"name": "User Input (Forms)", "parentId": "InDepth", "isLeaf": false, "id": "Forms", "hidden": true},
29
28
  {"name": "Component and Container Basics", "parentId": "InDepth", "isLeaf": true, "id": "ComponentsAndContainers"},
30
- {"name": "Layouts", "parentId": "InDepth", "isLeaf": false, "id": "Layouts"},
29
+ {"name": "Layouts", "parentId": "InDepth", "isLeaf": false, "id": "Layouts", "hidden": true},
31
30
  {"name": "View Models", "parentId": "InDepth", "isLeaf": true, "id": "GuideViewModels"},
32
- {"name": "Custom Components", "parentId": "InDepth", "isLeaf": true, "id": "CustomComponents"},
31
+ {"name": "Custom Components", "parentId": "InDepth", "isLeaf": true, "id": "CustomComponents", "hidden": true},
33
32
  {"name": "Events", "parentId": "InDepth", "isLeaf": true, "expanded": false, "id": "GuideEvents"},
34
- {"name": "Tables (Stores)", "parentId": "InDepth", "isLeaf": false, "id": "Tables"},
35
- {"name": "Shared Bindable Data (Component Models)", "parentId": "InDepth", "isLeaf": false, "id": "InDepthComponentModels"},
36
- {"name": "Multi-Window Applications", "parentId": "InDepth", "isLeaf": false, "id": "MultiWindow"},
37
- {"name": "Main Thread Addons", "parentId": "InDepth", "isLeaf": false, "id": "MainThreadAddons"},
33
+ {"name": "Tables (Stores)", "parentId": "InDepth", "isLeaf": false, "id": "Tables", "hidden": true},
34
+ {"name": "Shared Bindable Data (Component Models)", "parentId": "InDepth", "isLeaf": false, "id": "InDepthComponentModels", "hidden": true},
35
+ {"name": "Multi-Window Applications", "parentId": "InDepth", "isLeaf": false, "id": "MultiWindow", "hidden": true},
36
+ {"name": "Main Thread Addons", "parentId": "InDepth", "isLeaf": false, "id": "MainThreadAddons", "hidden": true},
38
37
  {"name": "Introduction", "parentId": "MainThreadAddons", "isLeaf": true, "id": "MainThreadAddonIntro"},
39
38
  {"name": "Example", "parentId": "MainThreadAddons", "isLeaf": true, "id": "MainThreadAddonExample"},
40
- {"name": "Mixins", "parentId": "InDepth", "isLeaf": false, "id": "Mixins"},
39
+ {"name": "Mixins", "parentId": "InDepth", "isLeaf": false, "id": "Mixins", "hidden": true},
41
40
  {"name": "JavaScript Classes", "parentId": null, "isLeaf": false, "id": "JavaScriptClasses", "hidden": true},
42
41
  {"name": "Classes, Properties, and Methods", "parentId": "JavaScriptClasses", "isLeaf": true, "id": "2023-10-07T19-18-28-517Z"},
43
42
  {"name": "Overriding Methods", "parentId": "JavaScriptClasses", "isLeaf": true, "id": "2023-10-08T20-20-07-934Z"},
@@ -35,4 +35,23 @@ body {
35
35
  transform: translateZ(0px);
36
36
  }
37
37
  }
38
+
39
+ // the style will get used inside home & learn
40
+ .neo-worker-setup {
41
+ align-items : center;
42
+ background-color: #17141c;
43
+ display : flex;
44
+ height : 100%;
45
+ justify-content : center;
46
+ padding : 20px;
47
+ width : 100%;
48
+
49
+ --fill-opacity : 0.05;
50
+ --stroke-opacity: 0.05;
51
+
52
+ &:hover {
53
+ --fill-opacity : 1;
54
+ --stroke-opacity: 1;
55
+ }
56
+ }
38
57
  }
@@ -13,9 +13,16 @@
13
13
 
14
14
  .portal-content-box-content {
15
15
  padding-left: 1.3em;
16
+ margin-top: 0.75em;
16
17
  }
17
18
 
18
- .portal-content-box-header {
19
-
19
+ .portal-content-box-headline {
20
+ border-bottom: 1px solid #ececec;
21
+ padding-bottom: 0.75em;
22
+ max-width: 85%;
23
+ margin: 1em auto 0 auto;
24
+ text-align: center;
25
+ letter-spacing: 1.5px!important;
26
+ word-spacing: 1.5px;
20
27
  }
21
28
  }