@nebularstreams/statify 3.0.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 (193) hide show
  1. package/dist/.DS_Store +0 -0
  2. package/dist/website.simple/webfonts/fa-brands-400.woff2 +0 -0
  3. package/dist/website.simple/webfonts/fa-solid-900.eot +0 -0
  4. package/dist/website.simple/webfonts/fa-solid-900.svg +4649 -0
  5. package/dist/website.simple/webfonts/fa-solid-900.ttf +0 -0
  6. package/dist/website.simple/webfonts/fa-solid-900.woff +0 -0
  7. package/dist/website.simple/webfonts/fa-solid-900.woff2 +0 -0
  8. package/dist/website.simple/website.css +32 -0
  9. package/dist/website.simple/website.min.js +6 -0
  10. package/dist/website.software/webfonts/fa-brands-400.woff2 +0 -0
  11. package/dist/website.software/webfonts/fa-solid-900.eot +0 -0
  12. package/dist/website.software/webfonts/fa-solid-900.svg +4649 -0
  13. package/dist/website.software/webfonts/fa-solid-900.ttf +0 -0
  14. package/dist/website.software/webfonts/fa-solid-900.woff +0 -0
  15. package/dist/website.software/webfonts/fa-solid-900.woff2 +0 -0
  16. package/dist/website.software/website.css +32 -0
  17. package/dist/website.software/website.min.js +6 -0
  18. package/dist/website.worlds/webfonts/fa-brands-400.woff2 +0 -0
  19. package/dist/website.worlds/webfonts/fa-solid-900.eot +0 -0
  20. package/dist/website.worlds/webfonts/fa-solid-900.svg +4649 -0
  21. package/dist/website.worlds/webfonts/fa-solid-900.ttf +0 -0
  22. package/dist/website.worlds/webfonts/fa-solid-900.woff +0 -0
  23. package/dist/website.worlds/webfonts/fa-solid-900.woff2 +0 -0
  24. package/dist/website.worlds/website.css +32 -0
  25. package/dist/website.worlds/website.min.js +6 -0
  26. package/package.json +49 -0
  27. package/readme.md +105 -0
  28. package/src/publisher-lib.js +179 -0
  29. package/src/publisher-list.js +91 -0
  30. package/src/publisher.js +38 -0
  31. package/src/util/cdn/nobj.cdn.js +88 -0
  32. package/src/util/htmlroute.js +39 -0
  33. package/src/util/markdown/lib.showdown.icon.js +62 -0
  34. package/src/util/markdown/lib.showdown.yt.js +54 -0
  35. package/src/util/markdown/markdown.js +144 -0
  36. package/src/util/navibar/util.navibar.js +63 -0
  37. package/src/util/sha.js +20 -0
  38. package/src/util/tokenizer.js +234 -0
  39. package/src/util/widgets/NoticeWidget.js +30 -0
  40. package/src/util/widgets/app.widgets.js +70 -0
  41. package/src/util/widgets/util.definewidget.js +14 -0
  42. package/src/widgets/Cookies.js +55 -0
  43. package/src/widgets/Frameset-simple.js +114 -0
  44. package/src/widgets/Frameset.js +124 -0
  45. package/src/widgets/Page.js +356 -0
  46. package/src/widgets/RightSide.js +93 -0
  47. package/src/widgets/Snippets.js +75 -0
  48. package/src/widgets/Tail.js +11 -0
  49. package/src/widgets/Widgets.js +10 -0
  50. package/src/widgets.client/Client.js +22 -0
  51. package/src/widgets.features/FeatureList.js +364 -0
  52. package/src/widgets.features/Youtube.js +23 -0
  53. package/src/widgets.features/bootcol.js +29 -0
  54. package/src/widgets.features/css/feature.css +112 -0
  55. package/src/widgets.features/css/tail.css +4 -0
  56. package/src/widgets.howto/Faq.js +201 -0
  57. package/src/widgets.howto/HowTo.js +419 -0
  58. package/src/widgets.howto/Tiles.js +51 -0
  59. package/src/widgets.howto/script-contractable.js +31 -0
  60. package/src/widgets.markdown/Document.js +84 -0
  61. package/src/widgets.market/MarketItem.js +278 -0
  62. package/src/widgets.market/MarketItem.orig.js +268 -0
  63. package/src/widgets.market/MarketItem.strips.js +276 -0
  64. package/src/widgets.market/StoryLicenseWIdget.js +45 -0
  65. package/src/widgets.market/app.utils.js +238 -0
  66. package/src.website/css/app/animations.css +27 -0
  67. package/src.website/css/app/ismobile.css +7 -0
  68. package/src.website/css/app/style.css +51 -0
  69. package/src.website/css/app/style.fullheight.css +9 -0
  70. package/src.website/css/app/style.mdblocks.css +59 -0
  71. package/src.website/css/app/style.putosafari.css +3 -0
  72. package/src.website/css/bootstrap/bootstrap-tiny-core.css +47 -0
  73. package/src.website/css/bootstrap/bootstrap-tiny-media-columns.css +1276 -0
  74. package/src.website/css/bootstrap/bootstrap-tiny-media-custom.css +59 -0
  75. package/src.website/css/bootstrap/bootstrap-tiny-media-display.css +279 -0
  76. package/src.website/css/bootstrap/bootstrap-tiny-media-flex.css +881 -0
  77. package/src.website/css/bootstrap/bootstrap-tiny-media-fonts.css +39 -0
  78. package/src.website/css/bootstrap/bootstrap-tiny-media-paddings.css +1348 -0
  79. package/src.website/css/css-core/core.ext.css +65 -0
  80. package/src.website/css/css-core/frontpage.css +28 -0
  81. package/src.website/css/css-core/root.css +34 -0
  82. package/src.website/css/css-core/root.fullheight.css +12 -0
  83. package/src.website/css/css-core/ui.dialog.round.css +14 -0
  84. package/src.website/css/css-core/ui.navibar.css +28 -0
  85. package/src.website/css/css-core/ui.notifications.css +12 -0
  86. package/src.website/css/css-featurelist/feature.css +196 -0
  87. package/src.website/css/css-featurelist/tail.css +4 -0
  88. package/src.website/css/css-screens/doc.css +22 -0
  89. package/src.website/css/css-screens/screens-meta.css +3 -0
  90. package/src.website/css/css-unsplash/unsplash.css +32 -0
  91. package/src.website/css/css-videos/youtube.css +28 -0
  92. package/src.website/css/css-wiki/wiki.css +206 -0
  93. package/src.website/css/css-wiki/wiki.sidepane.css +16 -0
  94. package/src.website/css/fontawesome/fa-brands.css +18 -0
  95. package/src.website/css/fontawesome/fa-solid.css +16 -0
  96. package/src.website/css/fontawesome/fa.css +4359 -0
  97. package/src.website/css/skin/mierda.css +15 -0
  98. package/src.website/css/skin/skin.dark.css +85 -0
  99. package/src.website/css/skin/skin.light.css +27 -0
  100. package/src.website/css/skin/vars.font.css +8 -0
  101. package/src.website/css/skin/vars.sizes.css +4 -0
  102. package/src.website/package-lock.json +2160 -0
  103. package/src.website/package.json +28 -0
  104. package/src.website/public/webfonts/fa-brands-400.woff2 +0 -0
  105. package/src.website/public/webfonts/fa-solid-900.eot +0 -0
  106. package/src.website/public/webfonts/fa-solid-900.svg +4649 -0
  107. package/src.website/public/webfonts/fa-solid-900.ttf +0 -0
  108. package/src.website/public/webfonts/fa-solid-900.woff +0 -0
  109. package/src.website/public/webfonts/fa-solid-900.woff2 +0 -0
  110. package/src.website/rollup.simple.mjs +36 -0
  111. package/src.website/rollup.software.mjs +36 -0
  112. package/src.website/rollup.terser.mjs +24 -0
  113. package/src.website/rollup.worlds.mjs +36 -0
  114. package/src.website/src/app/app.style.mjs +36 -0
  115. package/src.website/src/app/index.mjs +118 -0
  116. package/src.website/src/config.js +8 -0
  117. package/src.website/src/core/cdnmodel.mjs +7 -0
  118. package/src.website/src/core/clientruntime.mjs +41 -0
  119. package/src.website/src/core.cookies/analytics.mjs +96 -0
  120. package/src.website/src/core.cookies/cookiemanager.mjs +128 -0
  121. package/src.website/src/core.cookies/css/cookies.css +80 -0
  122. package/src.website/src/core.cookies/css/nocookies.css +48 -0
  123. package/src.website/src/core.extra/css/contractable.css +12 -0
  124. package/src.website/src/core.extra/css/faq.css +42 -0
  125. package/src.website/src/core.extra/css/howto.css +81 -0
  126. package/src.website/src/core.extra/extra.document.mjs +83 -0
  127. package/src.website/src/core.extra/extra.faq.mjs +30 -0
  128. package/src.website/src/core.extra/extra.mjs +62 -0
  129. package/src.website/src/main.simple.mjs +4 -0
  130. package/src.website/src/main.software.mjs +10 -0
  131. package/src.website/src/main.worlds.mjs +9 -0
  132. package/src.website/src/support/ismobile.mjs +4 -0
  133. package/src.website/src/support/nonce.mjs +4 -0
  134. package/src.website/src/support/persistsettings.mjs +41 -0
  135. package/src.website/src/support/qrcode.mjs +1269 -0
  136. package/src.website/src/support/timeutils.mjs +237 -0
  137. package/src.website/src/widgets/index.mjs +22 -0
  138. package/src.website/src.widgets/widgets.market/index.mjs +11 -0
  139. package/src.website/src.widgets/widgets.market/widgets/market/DiscoverWidget.mjs +401 -0
  140. package/src.website/src.widgets/widgets.market/widgets/market/EmptyNew.mjs +28 -0
  141. package/src.website/src.widgets/widgets.market/widgets/market/MarketDetail.mjs +78 -0
  142. package/src.website/src.widgets/widgets.market/widgets/market/MarketItem.mjs +349 -0
  143. package/src.website/src.widgets/widgets.market/widgets/market/MarketWidget.mjs +437 -0
  144. package/src.website/src.widgets/widgets.market/widgets/market/StoryLicenseWidget.mjs +34 -0
  145. package/src.website/src.widgets/widgets.market/widgets/market/css/market.css +394 -0
  146. package/src.website/src.widgets/widgets.market/widgets/market/css/notice.css +6 -0
  147. package/src.website/src.widgets/widgets.market/widgets/navibar/NaviBarWidget.mjs +58 -0
  148. package/src.website/src.widgets/widgets.market/widgets/navibar/css/titlebar.css +68 -0
  149. package/src.website/src.widgets/widgets.market/widgets/navibar/css/ui.navibar.css +28 -0
  150. package/src.website/src.widgets/widgets.software/index.mjs +9 -0
  151. package/src.website/src.widgets/widgets.software/widgets/BootStrapColumnWidget.mjs +17 -0
  152. package/src.website/src.widgets/widgets.software/widgets/DownloadWidget.mjs +210 -0
  153. package/src.website/src.widgets/widgets.software/widgets/LicenseWidget.js +103 -0
  154. package/src.website/src.widgets/widgets.software/widgets/css/download/download.css +110 -0
  155. package/src.website/src.widgets/widgets.software/widgets/css/license/beta.css +28 -0
  156. package/src.website/src.widgets/widgets.software/widgets/css/license/button.css +30 -0
  157. package/src.website/src.widgets/widgets.software/widgets/css/license/front.css +46 -0
  158. package/src.website/src.widgets/widgets.software/widgets/css/license/licenses.css +13 -0
  159. package/src.website/src.widgets/widgets.software/widgets/css/license/overstate.css +15 -0
  160. package/src.website/src.widgets/widgets.software/widgets/css/license/story.css +8 -0
  161. package/src.website/src.widgets/widgets.software/widgets/css/license/toolbar.css +9 -0
  162. package/src.website/src.widgets/widgets.software/widgets/support/version.mjs +88 -0
  163. package/src.website/src.widgets/widgets.worlds/index.mjs +11 -0
  164. package/src.website/src.widgets/widgets.worlds/widgets/LocalBrowser.mjs +386 -0
  165. package/src.website/src.widgets/widgets.worlds/widgets/LocalLauncher.code.mjs +247 -0
  166. package/src.website/src.widgets/widgets.worlds/widgets/LocalLauncher.mjs +239 -0
  167. package/src.website/src.widgets/widgets.worlds/widgets/css/input.css +32 -0
  168. package/src.website/src.widgets/widgets.worlds/widgets/css/localprojects.css +188 -0
  169. package/templates/documentation/assets/docs.css +7 -0
  170. package/templates/documentation/assets/images/right.webp +0 -0
  171. package/templates/documentation/images.json +12 -0
  172. package/templates/documentation/metadata.json +120 -0
  173. package/templates/documentation/pages/cookies.md +25 -0
  174. package/templates/documentation/pages/index.md +13 -0
  175. package/templates/documentation/pages/privacy.md +50 -0
  176. package/templates/documentation/pages/tos.md +49 -0
  177. package/templates/landing/assets/res/carousel/image-1.webp +0 -0
  178. package/templates/landing/assets/res/carousel/image-2.webp +0 -0
  179. package/templates/landing/assets/res/carousel/image-3.webp +0 -0
  180. package/templates/landing/assets/res/custom.css +122 -0
  181. package/templates/landing/assets/res/logos/favicon.ico +0 -0
  182. package/templates/landing/assets/res/logos/favicon.png +0 -0
  183. package/templates/landing/assets/res/logos/logo.medallion.png +0 -0
  184. package/templates/landing/assets/res/logos/logo.white.png +0 -0
  185. package/templates/landing/assets/res/proj/banda-tech-client.webp +0 -0
  186. package/templates/landing/assets/res/proj/banda-tech-server.webp +0 -0
  187. package/templates/landing/assets/res/proj/proj-common-ground.webp +0 -0
  188. package/templates/landing/assets/res/proj/proj-fieldnote.webp +0 -0
  189. package/templates/landing/assets/res/proj/proj-tribes.webp +0 -0
  190. package/templates/landing/images.json +932 -0
  191. package/templates/landing/metadata.json +115 -0
  192. package/templates/landing/pages/index.json +268 -0
  193. package/tool/cli.js +183 -0
@@ -0,0 +1,386 @@
1
+ import m from "mithril";
2
+ import {defineWidget, InputWidget, ButtonNew, Empty, NoticeWidget} from "@nebularstreams/libmui";
3
+
4
+ import TimeUtils from "../../../src/support/timeutils.mjs"
5
+
6
+ import "./css/localprojects.css"
7
+ import "./css/input.css";
8
+
9
+ const
10
+ DEFAULT_HEADER_CONFIG = {
11
+ icon: "plug",
12
+ text: "Connect to Nebular World Editor to test your worlds",
13
+ size: 2,
14
+ class: "configbox"
15
+ },
16
+ FEATURECLASS = "col-12 col-md-6 col-lg-4 col-xxl-3";
17
+
18
+ let
19
+ CREDENTIALS_AVAILABLE = false,
20
+ EDITOR_AVAILABLE = false,
21
+
22
+ PORT = 8888,
23
+ key = 0,
24
+ DATA = [0, 0, 0, 0];
25
+
26
+ async function init() {
27
+
28
+ EDITOR_AVAILABLE = false;
29
+
30
+ const
31
+ ip = window.localStorage.getItem("editorIp"),
32
+ port = window.localStorage.getItem("editorPort") || "8888";
33
+
34
+ if (ip) {
35
+
36
+ let available = true;
37
+
38
+ ip.split(".")
39
+ .forEach((n, index) => {
40
+ const g = parseInt(n, 10);
41
+ if (g >= 0 && g < 255) DATA[index] = g; else available = false;
42
+ });
43
+
44
+ PORT = parseInt(port, 10) || 8888;
45
+
46
+ CREDENTIALS_AVAILABLE = available;
47
+ } else {
48
+ CREDENTIALS_AVAILABLE = false;
49
+ }
50
+
51
+ }
52
+
53
+ function persistAddress() {
54
+ if (DATA && PORT) {
55
+ window.localStorage.setItem("editorIp", DATA.join("."));
56
+ window.localStorage.setItem("editorPort", PORT);
57
+ }
58
+ }
59
+
60
+ function fetchWithTimeout(url, options = {}, timeout = 5000) {
61
+ // Create a timeout promise
62
+ const timeoutPromise = new Promise((_, reject) =>
63
+ setTimeout(() => reject(new Error("Request timed out")), timeout)
64
+ );
65
+
66
+ // Use Promise.race to enforce timeout
67
+ return Promise.race([
68
+ fetch(url, options), // Original fetch request
69
+ timeoutPromise // Timeout rejection
70
+ ]);
71
+ }
72
+
73
+ async function connect() {
74
+
75
+ if ((DATA[0] < 10 || DATA[0] > 192) || (DATA[3] === 0 || DATA[3] > 253)) {
76
+ EDITOR_AVAILABLE = false;
77
+ } else {
78
+ try {
79
+
80
+ const
81
+ address = `https://${DATA.join(".")}:${PORT}/world/list.json`,
82
+ response = await fetchWithTimeout(address, {}, 2000),
83
+ data = response.ok && await response.json();
84
+
85
+ if (Array.isArray(data)) {
86
+ persistAddress();
87
+ EDITOR_AVAILABLE = true;
88
+ } else {
89
+ EDITOR_AVAILABLE = false;
90
+ }
91
+ } catch (e) {
92
+ EDITOR_AVAILABLE = false;
93
+ }
94
+ }
95
+
96
+ }
97
+
98
+ function cool() {
99
+ return DATA[0] >= 0 && DATA[1] >= 0 && DATA[2] >= 0 && DATA[3] >= 0 && PORT >= 8000;
100
+ }
101
+
102
+ const KeyWrapper = defineWidget(
103
+ ({children}) => [children]
104
+ ),
105
+ ConfigScreen = defineWidget(
106
+ ({attrs}) =>
107
+ m(".flexcol.w-max.h-100.createscreen", attrs.state?.loading ? [
108
+ m(Empty, {icon: "sync.fa-spin", size: 2})
109
+ ] : [
110
+ m(".header", attrs.header),
111
+ m(".subheader", (attrs.subheaders || []).map((td) => m(".subline", td))),
112
+ m(ConfigDialog, attrs),
113
+ m(".instructions", (attrs.instructions || []).map((td) => m(".instruction", td)))
114
+ ])
115
+ ),
116
+ ConfigDialog = defineWidget(
117
+ ({attrs}) => {
118
+ const state = attrs.state;
119
+ return m(
120
+ NoticeWidget,
121
+ attrs.headerDialog,
122
+ [
123
+ m(".flexrow.gap-2.ipinput", [
124
+ m(InputWidget, {
125
+ type: "number",
126
+ min: 0,
127
+ max: 255,
128
+ hint: "192",
129
+ value: DATA[0],
130
+ oninput: () => {
131
+ },
132
+ onchange: (val) => {
133
+ DATA[0] = val;
134
+ }
135
+ }),
136
+ m(InputWidget, {
137
+ type: "number",
138
+ min: 0,
139
+ max: 255,
140
+ hint: "0",
141
+ value: DATA[1],
142
+ oninput: () => {
143
+ },
144
+ onchange: (val) => {
145
+ DATA[1] = val;
146
+ }
147
+ }),
148
+ m(InputWidget, {
149
+ type: "number",
150
+ min: 0,
151
+ max: 255,
152
+ hint: "0",
153
+ value: DATA[2],
154
+ oninput: () => {
155
+ },
156
+ onchange: (val) => {
157
+ DATA[2] = val;
158
+ }
159
+ }),
160
+ m(InputWidget, {
161
+ type: "number",
162
+ min: 0,
163
+ max: 255,
164
+ hint: "0",
165
+ value: DATA[3],
166
+ oninput: () => {
167
+ },
168
+ onchange: (val) => {
169
+ DATA[3] = val;
170
+ }
171
+ }),
172
+ m(InputWidget, {
173
+ type: "number",
174
+ min: 9000,
175
+ max: 9999,
176
+ hint: "9999",
177
+ value: PORT,
178
+ oninput: () => {
179
+ },
180
+ onchange: (val) => {
181
+ PORT = val;
182
+ }
183
+ })
184
+ ]),
185
+ ...cool()
186
+ ? [
187
+ m(ButtonNew, {
188
+ class: "accentbutton connectbutton",
189
+ text: "connect to Nebular World Editor",
190
+ onClick: () => {
191
+
192
+ state.error = null;
193
+ state.loading = true;
194
+ m.redraw();
195
+
196
+ connect()
197
+ .finally(() => {
198
+ state.loading = false;
199
+ m.redraw();
200
+ });
201
+ }
202
+ }
203
+ )
204
+ ]
205
+ : [],
206
+ ...state.error
207
+ ? [
208
+ m(NoticeWidget, {
209
+ icon: "explamation-triangle",
210
+ text: [
211
+ "Cannot connect to Nebular World Editor",
212
+ "Please make sure that your computer is in the same network as your VT Glasses, that Nebular World Application is started and that you have correctly entered the IP Address and Port above.",
213
+ "Don't have Nebular World Editor installed? You then need to download it here!"
214
+ ]
215
+ }, [
216
+ m(ButtonNew, {
217
+ icon: "download",
218
+ class: "accentbutton",
219
+ text: "Download Nebular World Editor",
220
+ onClick: () => m.route.set("download")
221
+ })
222
+ ])
223
+ ] : []
224
+ ]
225
+ );
226
+
227
+ }
228
+ ),
229
+ PlatformEntityMasterList = defineWidget(
230
+ ({attrs, state}) =>
231
+ m(
232
+ "ul.flexrow.wrap.grows.scrolly.h-100",
233
+ {
234
+ "aria-label": attrs.label,
235
+ key: "k" + key,
236
+ class: attrs.class
237
+ },
238
+ state.data
239
+ ? [...attrs.header ? [m(NoticeWidget, attrs.header)] : []]
240
+ .concat((state.data || [])
241
+ .filter((a) => a.isDirectory)
242
+ .sort(attrs.sorter || ((a, b) => a.date < b.date ? 1 : -1))
243
+ .map((item) => m(attrs.TemplateItem, {
244
+ item,
245
+ state,
246
+ featured: false,
247
+ class: "fadein pad-2 " + (attrs.params?.featureClass || FEATURECLASS),
248
+ innerClass: attrs.params?.innerClass || "growgrid graytrx shadow-1",
249
+ titleClass: attrs.params?.titleClass || "",
250
+ noTitleIcon: attrs.params?.noTitleIcon,
251
+ noTitleChannel: attrs.params?.noTitleChannel,
252
+ itemClass: attrs.params?.itemClass || "",
253
+ imageClass: attrs.params?.imageClass || "",
254
+ shortPath: !!attrs.params?.shortPath,
255
+ localhost: attrs.localhost,
256
+ onClick: () => {
257
+ window.clearTimeout(state.timer);
258
+ state.timer = window.setTimeout(
259
+ () => attrs.onSelect(item, attrs.params?.htmlRoot),
260
+ 200
261
+ );
262
+ }
263
+ }))
264
+ )
265
+ : m(Empty, state.loading
266
+ ? {
267
+ icon: "sync.fa-spin",
268
+ size: 4
269
+ }
270
+ : {
271
+ icon: attrs.icon,
272
+ size: 4,
273
+ message: attrs.message
274
+ })),
275
+
276
+ ({attrs, state}) => {
277
+ state.loading = true;
278
+ attrs.request()
279
+ .then((data) => {
280
+ state.data = data;
281
+ state.empty = !data.length;
282
+ })
283
+ .catch((e) => {
284
+ state.error = e;
285
+ })
286
+ .finally(() => {
287
+ state.loading = false;
288
+ m.redraw();
289
+ });
290
+ });
291
+
292
+ const
293
+ ProjectItem = defineWidget(
294
+ ({attrs}) => m(
295
+ ".project.flexcol.pad-1.fadein",
296
+ {class: attrs.class},
297
+ m(".inner.w-max.flexcol", {
298
+ onclick: (e) => e.target.nodeName !== "BUTTON" && attrs.onClick(attrs.item),
299
+ ...attrs.item && {style: {backgroundImage: "url(" + attrs.localhost + "/world/" + attrs.item.name + "/meta/logo.jpg)"}}
300
+ }
301
+ ),
302
+ m(".lineinfo.flexrow", attrs.item ? [
303
+ m(".title", attrs.item.meta?.title || attrs.item.name || "Untitled"),
304
+ m(".grows"),
305
+ m(".date", TimeUtils.getTimeInterval(attrs.item.date))
306
+ ] : [])
307
+ )
308
+ );
309
+
310
+ const LocalBrowserWidget = defineWidget(
311
+ ({attrs, state}) => {
312
+
313
+ return state.uninit
314
+ ? m("ul.flexrow.wrap.grows.h-100", m(Empty, {
315
+ icon: "sync.fa-spin",
316
+ size: 3
317
+ }))
318
+ // user supplied credentials
319
+ : CREDENTIALS_AVAILABLE
320
+ // that have been successful
321
+ ? EDITOR_AVAILABLE
322
+ ? m(KeyWrapper, [
323
+ m(
324
+ PlatformEntityMasterList,
325
+ {
326
+ key,
327
+ class: "projectlist",
328
+ label: "List of worlds",
329
+ type: "project",
330
+ icon: "globe",
331
+ params: attrs.static,
332
+ message: ["Local worlds are not available at the moment", "Please try again later"].join("\n"),
333
+ request: async () => {
334
+ const response = await fetch(state.localhost + "/world/list.json");
335
+ if (response.ok) {
336
+ return response.json();
337
+ } else {
338
+ throw new Error("Not connected");
339
+ }
340
+ },
341
+ onSelect: (item) => {
342
+ // window.location.href = `/${item.type}/${item.id}`;
343
+ window.location.href = `/${item.type}/${item.id}?local=1`;
344
+ },
345
+ TemplateItem: ProjectItem,
346
+ localhost: state.localhost
347
+ }
348
+ )])
349
+ : m(ConfigScreen, {
350
+ state,
351
+ header: attrs.static.header || "Bring Your Ideas To Life!",
352
+ subheaders: attrs.static.subheaders || ["Step into the next level by creating your own immersive world.", "Connect to the Nebular World Editor to start building your adventure!"],
353
+ headerDialog: attrs.static.headerDialog || DEFAULT_HEADER_CONFIG,
354
+ instructions: attrs.static.instructions || [
355
+ "How to Connect",
356
+ "Open the Nebular World Editor on your computer",
357
+ "Locate the IP address displayed in the editor",
358
+ "Enter the IP address and port number below",
359
+ "Click Connect to display your local projects."
360
+ ]
361
+ })
362
+ : m(ConfigScreen, {
363
+ state,
364
+ header: attrs.static.header || "Bring Your Ideas To Life!",
365
+ subheaders: attrs.static.subheaders || ["Step into the next level by creating your own immersive world.", "Connect to the Nebular World Editor to start building your adventure!"],
366
+ headerDialog: attrs.static.headerDialog || DEFAULT_HEADER_CONFIG,
367
+ instructions: attrs.static.instructions || [
368
+ "How to Connect",
369
+ "Open the Nebular World Editor on your computer",
370
+ "Locate the IP address displayed in the editor",
371
+ "Enter the IP address and port number below",
372
+ "Click Connect to display your local projects."
373
+ ]
374
+ });
375
+ },
376
+ ({state}) => {
377
+ state.uninit = true;
378
+ init(state)
379
+ .then(() => {
380
+ state.uninit = false;
381
+ m.redraw();
382
+ });
383
+ }
384
+ );
385
+
386
+ export default LocalBrowserWidget;
@@ -0,0 +1,247 @@
1
+ import m from "mithril";
2
+ import {defineWidget, InputWidget, ButtonNew, Empty, NoticeWidget} from "@nebularstreams/libmui";
3
+
4
+ const
5
+
6
+ PORT = 8888,
7
+
8
+ ipDecoder = (() => {
9
+
10
+ // Alphabet for encoding (letters only)
11
+ const
12
+ alphabet = "23456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
13
+ base = alphabet.length; // Base-52 encoding
14
+
15
+ return (encoded) => {
16
+ let intIp = 0;
17
+
18
+ // Decode the base-52 string into a 32-bit integer
19
+ for (let char of encoded) {
20
+ const value = alphabet.indexOf(char);
21
+ if (value === -1) {
22
+ return null;
23
+ }
24
+ intIp = intIp * base + value;
25
+ }
26
+
27
+ // Convert the 32-bit integer back into an IPv4 address
28
+ return [
29
+ (intIp >> 24) & 0xff,
30
+ (intIp >> 16) & 0xff,
31
+ (intIp >> 8) & 0xff,
32
+ intIp & 0xff
33
+ ].join(".");
34
+ };
35
+ })(),
36
+
37
+ DEFAULT_HEADER_CONFIG = {
38
+ icon: "plug",
39
+ text: "Connect to Nebular World Editor to test your worlds",
40
+ size: 2,
41
+ class: "configbox"
42
+ };
43
+
44
+ let
45
+ CREDENTIALS_AVAILABLE = false,
46
+ EDITOR_AVAILABLE = false,
47
+
48
+ CODE = "",
49
+ DATA = [];
50
+
51
+ async function init() {
52
+
53
+ EDITOR_AVAILABLE = false;
54
+
55
+ const
56
+ code = window.localStorage.getItem("editorCode");
57
+
58
+ if (code) {
59
+
60
+ let available = true;
61
+
62
+ const ip = ipDecoder(code);
63
+
64
+ ip.split(".")
65
+ .forEach((n, index) => {
66
+ const g = parseInt(n, 10);
67
+ if (g >= 0 && g < 255) DATA[index] = g; else available = false;
68
+ });
69
+
70
+ CREDENTIALS_AVAILABLE = available;
71
+ } else {
72
+ CREDENTIALS_AVAILABLE = false;
73
+ }
74
+
75
+ }
76
+
77
+ function persistAddress() {
78
+ if (CODE) {
79
+ window.localStorage.setItem("editorCode", CODE);
80
+ CREDENTIALS_AVAILABLE = true;
81
+ }
82
+ }
83
+
84
+ function fetchWithTimeout(url, options = {}, timeout = 5000) {
85
+ // Create a timeout promise
86
+ const timeoutPromise = new Promise((_, reject) =>
87
+ setTimeout(() => reject(new Error("Request timed out")), timeout)
88
+ );
89
+
90
+ // Use Promise.race to enforce timeout
91
+ return Promise.race([
92
+ fetch(url, options), // Original fetch request
93
+ timeoutPromise // Timeout rejection
94
+ ]);
95
+ }
96
+
97
+ async function connect(state) {
98
+
99
+ if (cool()) {
100
+
101
+ const ipAddress = ipDecoder(CODE);
102
+
103
+ try {
104
+
105
+ state.portalUrl = `https://${ipAddress}:${PORT}`;
106
+
107
+ const t = Date.now();
108
+ state.loading = true;
109
+ m.redraw();
110
+
111
+ fetchWithTimeout(state.portalUrl, {}, 1000)
112
+ .catch((e) => {
113
+ console.error("LAUNCHER", "Fetch Error", e);
114
+ })
115
+ .finally(() => {
116
+
117
+ state.loading = false;
118
+ m.redraw();
119
+
120
+ if (Date.now() - t < 500) {
121
+ persistAddress();
122
+ EDITOR_AVAILABLE = true;
123
+ window.open(state.portalUrl, "editor");
124
+ }
125
+ });
126
+ } catch (e) {
127
+ EDITOR_AVAILABLE = false;
128
+ }
129
+ } else {
130
+ EDITOR_AVAILABLE = false;
131
+ }
132
+
133
+ }
134
+
135
+ function cool() {
136
+ return CODE.length >= 5;
137
+ }
138
+
139
+ const
140
+ ConfigScreen = defineWidget(
141
+ ({ attrs }) =>
142
+ m(".flexcol.w-max.h-100.createscreen", attrs.state?.loading ? [
143
+ m(Empty, { icon: "sync.fa-spin", size: 2 })
144
+ ] : [
145
+ m(".header", attrs.header),
146
+ m(".subheader", (attrs.subheaders || []).map((td) => m(".subline", td))),
147
+ m(ConfigDialog, attrs),
148
+ m(".instructions", (attrs.instructions || []).map((td) => m(".instruction", td)))
149
+ ])
150
+ ),
151
+ ConfigDialog = defineWidget(
152
+ ({ attrs }) => {
153
+ const state = attrs.state;
154
+ return m(
155
+ NoticeWidget,
156
+ attrs.headerDialog,
157
+ [
158
+ m(".flexrow.gap-2.ipinput", [
159
+ m(InputWidget, {
160
+ type: "text",
161
+ maxlength: 8,
162
+ hint: "--------",
163
+ value: CODE,
164
+ oninput: () => {},
165
+ onchange: (val) => {CODE = val;}
166
+ })
167
+ ]),
168
+ ...cool()
169
+ ? [
170
+ m(ButtonNew, {
171
+ class: "accentbutton connectbutton",
172
+ text: "connect to Nebular World Editor",
173
+ onClick: () => {
174
+
175
+ state.error = null;
176
+ state.loading = true;
177
+ m.redraw();
178
+
179
+ connect(state)
180
+ .finally(() => {
181
+ state.loading = false;
182
+ m.redraw();
183
+ });
184
+ }
185
+ }
186
+ )
187
+ ]
188
+ : [],
189
+ ...state.error
190
+ ? [
191
+ m(NoticeWidget, {
192
+ icon: "explamation-triangle",
193
+ text: [
194
+ "Cannot connect to Nebular World Editor",
195
+ "Please make sure that your computer is in the same network as your VR Glasses, that Nebular World Application is started and that you have correctly entered the IP Address and Port above.",
196
+ "Don't have Nebular World Editor installed? You then need to download it here!"
197
+ ]
198
+ }, [
199
+ m(ButtonNew, {
200
+ icon: "download",
201
+ class: "accentbutton",
202
+ text: "Download Nebular World Editor",
203
+ onClick: () => m.route.set("download")
204
+ })
205
+ ])
206
+ ] : []
207
+ ]
208
+ );
209
+
210
+ }
211
+ );
212
+
213
+ const
214
+ LocalLauncher = defineWidget(
215
+ ({ attrs, state }) => {
216
+
217
+ return state.uninit
218
+ ? m("ul.flexrow.wrap.grows.h-100", m(Empty, {
219
+ icon: "sync.fa-spin",
220
+ size: 3
221
+ }))
222
+ // user supplied credentials
223
+ : m(ConfigScreen, {
224
+ state,
225
+ header: attrs.static.header || "Bring Your Ideas To Life!",
226
+ subheaders: attrs.static.subheaders || ["Step into the next level by creating your own immersive world.", "Connect to the Nebular World Editor to start building your adventure!"],
227
+ headerDialog: attrs.static.headerDialog || DEFAULT_HEADER_CONFIG,
228
+ instructions: attrs.static.instructions || [
229
+ "How to Connect",
230
+ "Open the Nebular World Editor on your computer",
231
+ "Locate the connection code displayed in the Settings Screen",
232
+ "Enter the code in the box below",
233
+ "Click the Connect Button to display your local projects."
234
+ ]
235
+ });
236
+ },
237
+ ({ state }) => {
238
+ state.uninit = true;
239
+ init(state)
240
+ .then(() => {
241
+ state.uninit = false;
242
+ m.redraw();
243
+ });
244
+ }
245
+ );
246
+
247
+ export default LocalLauncher;