@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.
- package/dist/.DS_Store +0 -0
- package/dist/website.simple/webfonts/fa-brands-400.woff2 +0 -0
- package/dist/website.simple/webfonts/fa-solid-900.eot +0 -0
- package/dist/website.simple/webfonts/fa-solid-900.svg +4649 -0
- package/dist/website.simple/webfonts/fa-solid-900.ttf +0 -0
- package/dist/website.simple/webfonts/fa-solid-900.woff +0 -0
- package/dist/website.simple/webfonts/fa-solid-900.woff2 +0 -0
- package/dist/website.simple/website.css +32 -0
- package/dist/website.simple/website.min.js +6 -0
- package/dist/website.software/webfonts/fa-brands-400.woff2 +0 -0
- package/dist/website.software/webfonts/fa-solid-900.eot +0 -0
- package/dist/website.software/webfonts/fa-solid-900.svg +4649 -0
- package/dist/website.software/webfonts/fa-solid-900.ttf +0 -0
- package/dist/website.software/webfonts/fa-solid-900.woff +0 -0
- package/dist/website.software/webfonts/fa-solid-900.woff2 +0 -0
- package/dist/website.software/website.css +32 -0
- package/dist/website.software/website.min.js +6 -0
- package/dist/website.worlds/webfonts/fa-brands-400.woff2 +0 -0
- package/dist/website.worlds/webfonts/fa-solid-900.eot +0 -0
- package/dist/website.worlds/webfonts/fa-solid-900.svg +4649 -0
- package/dist/website.worlds/webfonts/fa-solid-900.ttf +0 -0
- package/dist/website.worlds/webfonts/fa-solid-900.woff +0 -0
- package/dist/website.worlds/webfonts/fa-solid-900.woff2 +0 -0
- package/dist/website.worlds/website.css +32 -0
- package/dist/website.worlds/website.min.js +6 -0
- package/package.json +49 -0
- package/readme.md +105 -0
- package/src/publisher-lib.js +179 -0
- package/src/publisher-list.js +91 -0
- package/src/publisher.js +38 -0
- package/src/util/cdn/nobj.cdn.js +88 -0
- package/src/util/htmlroute.js +39 -0
- package/src/util/markdown/lib.showdown.icon.js +62 -0
- package/src/util/markdown/lib.showdown.yt.js +54 -0
- package/src/util/markdown/markdown.js +144 -0
- package/src/util/navibar/util.navibar.js +63 -0
- package/src/util/sha.js +20 -0
- package/src/util/tokenizer.js +234 -0
- package/src/util/widgets/NoticeWidget.js +30 -0
- package/src/util/widgets/app.widgets.js +70 -0
- package/src/util/widgets/util.definewidget.js +14 -0
- package/src/widgets/Cookies.js +55 -0
- package/src/widgets/Frameset-simple.js +114 -0
- package/src/widgets/Frameset.js +124 -0
- package/src/widgets/Page.js +356 -0
- package/src/widgets/RightSide.js +93 -0
- package/src/widgets/Snippets.js +75 -0
- package/src/widgets/Tail.js +11 -0
- package/src/widgets/Widgets.js +10 -0
- package/src/widgets.client/Client.js +22 -0
- package/src/widgets.features/FeatureList.js +364 -0
- package/src/widgets.features/Youtube.js +23 -0
- package/src/widgets.features/bootcol.js +29 -0
- package/src/widgets.features/css/feature.css +112 -0
- package/src/widgets.features/css/tail.css +4 -0
- package/src/widgets.howto/Faq.js +201 -0
- package/src/widgets.howto/HowTo.js +419 -0
- package/src/widgets.howto/Tiles.js +51 -0
- package/src/widgets.howto/script-contractable.js +31 -0
- package/src/widgets.markdown/Document.js +84 -0
- package/src/widgets.market/MarketItem.js +278 -0
- package/src/widgets.market/MarketItem.orig.js +268 -0
- package/src/widgets.market/MarketItem.strips.js +276 -0
- package/src/widgets.market/StoryLicenseWIdget.js +45 -0
- package/src/widgets.market/app.utils.js +238 -0
- package/src.website/css/app/animations.css +27 -0
- package/src.website/css/app/ismobile.css +7 -0
- package/src.website/css/app/style.css +51 -0
- package/src.website/css/app/style.fullheight.css +9 -0
- package/src.website/css/app/style.mdblocks.css +59 -0
- package/src.website/css/app/style.putosafari.css +3 -0
- package/src.website/css/bootstrap/bootstrap-tiny-core.css +47 -0
- package/src.website/css/bootstrap/bootstrap-tiny-media-columns.css +1276 -0
- package/src.website/css/bootstrap/bootstrap-tiny-media-custom.css +59 -0
- package/src.website/css/bootstrap/bootstrap-tiny-media-display.css +279 -0
- package/src.website/css/bootstrap/bootstrap-tiny-media-flex.css +881 -0
- package/src.website/css/bootstrap/bootstrap-tiny-media-fonts.css +39 -0
- package/src.website/css/bootstrap/bootstrap-tiny-media-paddings.css +1348 -0
- package/src.website/css/css-core/core.ext.css +65 -0
- package/src.website/css/css-core/frontpage.css +28 -0
- package/src.website/css/css-core/root.css +34 -0
- package/src.website/css/css-core/root.fullheight.css +12 -0
- package/src.website/css/css-core/ui.dialog.round.css +14 -0
- package/src.website/css/css-core/ui.navibar.css +28 -0
- package/src.website/css/css-core/ui.notifications.css +12 -0
- package/src.website/css/css-featurelist/feature.css +196 -0
- package/src.website/css/css-featurelist/tail.css +4 -0
- package/src.website/css/css-screens/doc.css +22 -0
- package/src.website/css/css-screens/screens-meta.css +3 -0
- package/src.website/css/css-unsplash/unsplash.css +32 -0
- package/src.website/css/css-videos/youtube.css +28 -0
- package/src.website/css/css-wiki/wiki.css +206 -0
- package/src.website/css/css-wiki/wiki.sidepane.css +16 -0
- package/src.website/css/fontawesome/fa-brands.css +18 -0
- package/src.website/css/fontawesome/fa-solid.css +16 -0
- package/src.website/css/fontawesome/fa.css +4359 -0
- package/src.website/css/skin/mierda.css +15 -0
- package/src.website/css/skin/skin.dark.css +85 -0
- package/src.website/css/skin/skin.light.css +27 -0
- package/src.website/css/skin/vars.font.css +8 -0
- package/src.website/css/skin/vars.sizes.css +4 -0
- package/src.website/package-lock.json +2160 -0
- package/src.website/package.json +28 -0
- package/src.website/public/webfonts/fa-brands-400.woff2 +0 -0
- package/src.website/public/webfonts/fa-solid-900.eot +0 -0
- package/src.website/public/webfonts/fa-solid-900.svg +4649 -0
- package/src.website/public/webfonts/fa-solid-900.ttf +0 -0
- package/src.website/public/webfonts/fa-solid-900.woff +0 -0
- package/src.website/public/webfonts/fa-solid-900.woff2 +0 -0
- package/src.website/rollup.simple.mjs +36 -0
- package/src.website/rollup.software.mjs +36 -0
- package/src.website/rollup.terser.mjs +24 -0
- package/src.website/rollup.worlds.mjs +36 -0
- package/src.website/src/app/app.style.mjs +36 -0
- package/src.website/src/app/index.mjs +118 -0
- package/src.website/src/config.js +8 -0
- package/src.website/src/core/cdnmodel.mjs +7 -0
- package/src.website/src/core/clientruntime.mjs +41 -0
- package/src.website/src/core.cookies/analytics.mjs +96 -0
- package/src.website/src/core.cookies/cookiemanager.mjs +128 -0
- package/src.website/src/core.cookies/css/cookies.css +80 -0
- package/src.website/src/core.cookies/css/nocookies.css +48 -0
- package/src.website/src/core.extra/css/contractable.css +12 -0
- package/src.website/src/core.extra/css/faq.css +42 -0
- package/src.website/src/core.extra/css/howto.css +81 -0
- package/src.website/src/core.extra/extra.document.mjs +83 -0
- package/src.website/src/core.extra/extra.faq.mjs +30 -0
- package/src.website/src/core.extra/extra.mjs +62 -0
- package/src.website/src/main.simple.mjs +4 -0
- package/src.website/src/main.software.mjs +10 -0
- package/src.website/src/main.worlds.mjs +9 -0
- package/src.website/src/support/ismobile.mjs +4 -0
- package/src.website/src/support/nonce.mjs +4 -0
- package/src.website/src/support/persistsettings.mjs +41 -0
- package/src.website/src/support/qrcode.mjs +1269 -0
- package/src.website/src/support/timeutils.mjs +237 -0
- package/src.website/src/widgets/index.mjs +22 -0
- package/src.website/src.widgets/widgets.market/index.mjs +11 -0
- package/src.website/src.widgets/widgets.market/widgets/market/DiscoverWidget.mjs +401 -0
- package/src.website/src.widgets/widgets.market/widgets/market/EmptyNew.mjs +28 -0
- package/src.website/src.widgets/widgets.market/widgets/market/MarketDetail.mjs +78 -0
- package/src.website/src.widgets/widgets.market/widgets/market/MarketItem.mjs +349 -0
- package/src.website/src.widgets/widgets.market/widgets/market/MarketWidget.mjs +437 -0
- package/src.website/src.widgets/widgets.market/widgets/market/StoryLicenseWidget.mjs +34 -0
- package/src.website/src.widgets/widgets.market/widgets/market/css/market.css +394 -0
- package/src.website/src.widgets/widgets.market/widgets/market/css/notice.css +6 -0
- package/src.website/src.widgets/widgets.market/widgets/navibar/NaviBarWidget.mjs +58 -0
- package/src.website/src.widgets/widgets.market/widgets/navibar/css/titlebar.css +68 -0
- package/src.website/src.widgets/widgets.market/widgets/navibar/css/ui.navibar.css +28 -0
- package/src.website/src.widgets/widgets.software/index.mjs +9 -0
- package/src.website/src.widgets/widgets.software/widgets/BootStrapColumnWidget.mjs +17 -0
- package/src.website/src.widgets/widgets.software/widgets/DownloadWidget.mjs +210 -0
- package/src.website/src.widgets/widgets.software/widgets/LicenseWidget.js +103 -0
- package/src.website/src.widgets/widgets.software/widgets/css/download/download.css +110 -0
- package/src.website/src.widgets/widgets.software/widgets/css/license/beta.css +28 -0
- package/src.website/src.widgets/widgets.software/widgets/css/license/button.css +30 -0
- package/src.website/src.widgets/widgets.software/widgets/css/license/front.css +46 -0
- package/src.website/src.widgets/widgets.software/widgets/css/license/licenses.css +13 -0
- package/src.website/src.widgets/widgets.software/widgets/css/license/overstate.css +15 -0
- package/src.website/src.widgets/widgets.software/widgets/css/license/story.css +8 -0
- package/src.website/src.widgets/widgets.software/widgets/css/license/toolbar.css +9 -0
- package/src.website/src.widgets/widgets.software/widgets/support/version.mjs +88 -0
- package/src.website/src.widgets/widgets.worlds/index.mjs +11 -0
- package/src.website/src.widgets/widgets.worlds/widgets/LocalBrowser.mjs +386 -0
- package/src.website/src.widgets/widgets.worlds/widgets/LocalLauncher.code.mjs +247 -0
- package/src.website/src.widgets/widgets.worlds/widgets/LocalLauncher.mjs +239 -0
- package/src.website/src.widgets/widgets.worlds/widgets/css/input.css +32 -0
- package/src.website/src.widgets/widgets.worlds/widgets/css/localprojects.css +188 -0
- package/templates/documentation/assets/docs.css +7 -0
- package/templates/documentation/assets/images/right.webp +0 -0
- package/templates/documentation/images.json +12 -0
- package/templates/documentation/metadata.json +120 -0
- package/templates/documentation/pages/cookies.md +25 -0
- package/templates/documentation/pages/index.md +13 -0
- package/templates/documentation/pages/privacy.md +50 -0
- package/templates/documentation/pages/tos.md +49 -0
- package/templates/landing/assets/res/carousel/image-1.webp +0 -0
- package/templates/landing/assets/res/carousel/image-2.webp +0 -0
- package/templates/landing/assets/res/carousel/image-3.webp +0 -0
- package/templates/landing/assets/res/custom.css +122 -0
- package/templates/landing/assets/res/logos/favicon.ico +0 -0
- package/templates/landing/assets/res/logos/favicon.png +0 -0
- package/templates/landing/assets/res/logos/logo.medallion.png +0 -0
- package/templates/landing/assets/res/logos/logo.white.png +0 -0
- package/templates/landing/assets/res/proj/banda-tech-client.webp +0 -0
- package/templates/landing/assets/res/proj/banda-tech-server.webp +0 -0
- package/templates/landing/assets/res/proj/proj-common-ground.webp +0 -0
- package/templates/landing/assets/res/proj/proj-fieldnote.webp +0 -0
- package/templates/landing/assets/res/proj/proj-tribes.webp +0 -0
- package/templates/landing/images.json +932 -0
- package/templates/landing/metadata.json +115 -0
- package/templates/landing/pages/index.json +268 -0
- 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;
|