@kvyverse/world-runtime 0.6.3 → 0.6.8
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/README.md +2 -2
- package/dist/{ChatModule-2tiWS_Yr.js → ChatModule-BFNVVLA8.js} +62 -37
- package/dist/{SightInteractableArea-DrqyB41W.js → SightInteractableArea-BK8IXNIl.js} +6 -6
- package/dist/addons/AddonsRuntime.d.ts +3 -4
- package/dist/behaviours/player/SightInteractableArea.d.ts +2 -2
- package/dist/{chat.module-descriptor-BTsep-Kq.js → chat.module-descriptor-CGeOaq16.js} +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +38 -8
- package/dist/modules/chat/ChatDom.d.ts +16 -10
- package/dist/modules/contracts/platform.module-api.d.ts +60 -0
- package/dist/modules/index.d.ts +6 -0
- package/dist/modules/platform/NullPlatformModule.d.ts +16 -0
- package/dist/modules/registry.d.ts +4 -0
- package/dist/version.gen.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -102,8 +102,8 @@ world.stores.set("difficulty", "hard");
|
|
|
102
102
|
and the world format version, and refuses to load on an incompatible runtime.
|
|
103
103
|
Re-export the world after a major runtime update.
|
|
104
104
|
- **Not everything crosses over.** Platform features (multiplayer server, user
|
|
105
|
-
state,
|
|
106
|
-
export report lists whatever your particular world loses.
|
|
105
|
+
state, switching the page to another world, the editor's debug overlay) stay in
|
|
106
|
+
Kvyverse; the export report lists whatever your particular world loses.
|
|
107
107
|
|
|
108
108
|
## License
|
|
109
109
|
|
|
@@ -1,28 +1,29 @@
|
|
|
1
|
-
import { t as CHAT_COMMAND_KEY } from "./chat.module-descriptor-
|
|
1
|
+
import { t as CHAT_COMMAND_KEY } from "./chat.module-descriptor-CGeOaq16.js";
|
|
2
2
|
import { ContextModule, ThreeContextEvents } from "three-start";
|
|
3
3
|
import van from "vanjs-core";
|
|
4
4
|
//#region packages/world-runtime/src/modules/chat/chat.css?inline
|
|
5
|
-
var chat_default = "/* Neutral base for the chat input: only what it breaks without on real devices.\n Looks are left to the world. */\n\n.kvy-chat {\n position: absolute;\n left: 0;\n bottom: 0;\n width: 100%;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n justify-content: flex-end;\n /* On-screen keyboard: ChatDom writes the overlap height here. */\n padding-bottom: var(--kvy-chat-keyboard-inset, 0px);\n /* Click-through like the HUD around it: the root stretches over the whole feed,\n so catching events here would kill clicks on the world behind the messages.\n Only the input takes events back. */\n pointer-events: none;\n}\n\n.kvy-chat__form {\n display: flex;\n
|
|
5
|
+
var chat_default = "/* Neutral base for the chat input: only what it breaks without on real devices.\n Looks are left to the world. */\n\n.kvy-chat {\n position: absolute;\n left: 0;\n bottom: 0;\n width: 100%;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n justify-content: flex-end;\n /* On-screen keyboard: ChatDom writes the overlap height here. */\n padding-bottom: var(--kvy-chat-keyboard-inset, 0px);\n /* Click-through like the HUD around it: the root stretches over the whole feed,\n so catching events here would kill clicks on the world behind the messages.\n Only the input takes events back. */\n pointer-events: none;\n}\n\n.kvy-chat__form {\n display: flex;\n}\n\n.kvy-chat__input {\n flex: 1 1 auto;\n min-width: 0;\n /* 16px — below that iOS Safari zooms the page on focus. */\n font: inherit;\n font-size: 16px;\n line-height: 1.25;\n color: inherit;\n background: transparent;\n border: 0;\n outline: none;\n padding: 4px 6px;\n -webkit-appearance: none;\n appearance: none;\n border-radius: 0;\n /* The one interactive part of the chat. */\n pointer-events: auto;\n /* No 300ms delay and no double-tap zoom on the input. */\n touch-action: manipulation;\n}\n\n/* The message feed is a TTL queue, not a scroll container. It sticks to the\n bottom and extra lines are cut from the top — no scrollbar here by design. */\n.kvy-chat__feed {\n display: flex;\n flex-direction: column;\n justify-content: flex-end;\n max-height: 40vh;\n overflow: hidden;\n /* Belt and braces: the root is already click-through, but a world restyling it\n must not accidentally make messages catch clicks. */\n pointer-events: none;\n}\n\n.kvy-chat__line {\n opacity: 1;\n transition: opacity 300ms ease;\n overflow-wrap: anywhere;\n}\n\n/* The line has outlived its time: it fades but stays in the DOM — opening the\n chat shows it again instead of rebuilding the feed. */\n.kvy-chat__line--faded {\n opacity: 0;\n}\n\n/* Toggle mode: the input is hidden until entered (Enter on the canvas).\n Opacity, not visibility/display: an element hidden by those cannot be focused\n programmatically, leaving nothing to open the chat with. */\n.kvy-chat--idle-hidden .kvy-chat__input {\n opacity: 0;\n pointer-events: none;\n}\n";
|
|
6
6
|
//#endregion
|
|
7
7
|
//#region packages/world-runtime/src/modules/chat/ChatDom.ts
|
|
8
|
-
var {
|
|
8
|
+
var { div, input } = van.tags;
|
|
9
9
|
var STYLE_ID = "kvyverse-chat-styles";
|
|
10
|
+
/** How long a send waits for the keyboard to commit the typed word (ms). */
|
|
11
|
+
var COMPOSITION_COMMIT_TIMEOUT = 150;
|
|
10
12
|
/**
|
|
11
13
|
* Chat DOM: the message feed plus the input field.
|
|
12
14
|
*
|
|
13
|
-
* Built to survive real devices:
|
|
14
|
-
*
|
|
15
|
-
* keyboard via `visualViewport`. Details —
|
|
16
|
-
* `./README.md`.
|
|
15
|
+
* Built to survive real devices: sending on the Enter keydown (no `<form>`: a
|
|
16
|
+
* sandboxed host may block form submission), an IME composition guard, and
|
|
17
|
+
* lifting above the on-screen keyboard via `visualViewport`. Details — `./README.md`.
|
|
17
18
|
*/
|
|
18
19
|
var ChatDom = class ChatDom {
|
|
19
20
|
root;
|
|
20
21
|
feed;
|
|
21
|
-
form;
|
|
22
22
|
input;
|
|
23
23
|
_options;
|
|
24
24
|
_lines = [];
|
|
25
|
-
|
|
25
|
+
/** Timer of a send waiting for the keyboard to commit its composition. */
|
|
26
|
+
_pendingSubmit = 0;
|
|
26
27
|
_viewportRaf = 0;
|
|
27
28
|
_mounted = false;
|
|
28
29
|
constructor(options = {}) {
|
|
@@ -40,16 +41,8 @@ var ChatDom = class ChatDom {
|
|
|
40
41
|
this.input.spellcheck = false;
|
|
41
42
|
this.input.setAttribute("autocorrect", "off");
|
|
42
43
|
this.input.setAttribute("autocapitalize", "sentences");
|
|
43
|
-
this.form = form({
|
|
44
|
-
class: "kvy-chat__form",
|
|
45
|
-
novalidate: true
|
|
46
|
-
}, this.input, button({
|
|
47
|
-
class: "kvy-chat__submit",
|
|
48
|
-
type: "submit",
|
|
49
|
-
tabindex: "-1"
|
|
50
|
-
}, "Send"));
|
|
51
44
|
this.feed = div({ class: "kvy-chat__feed" });
|
|
52
|
-
this.root = div({ class: "kvy-chat" }, this.feed, this.
|
|
45
|
+
this.root = div({ class: "kvy-chat" }, this.feed, div({ class: "kvy-chat__form" }, this.input));
|
|
53
46
|
}
|
|
54
47
|
/** The focus policy is only known at runtime (mobile/desktop, canvas). */
|
|
55
48
|
setFocusPolicy(policy) {
|
|
@@ -185,10 +178,8 @@ var ChatDom = class ChatDom {
|
|
|
185
178
|
line.el.classList.toggle("kvy-chat__line--faded", faded);
|
|
186
179
|
}
|
|
187
180
|
_bind() {
|
|
188
|
-
this.form.addEventListener("submit", this._onSubmit);
|
|
189
|
-
this.input.addEventListener("compositionstart", this._onCompositionStart);
|
|
190
|
-
this.input.addEventListener("compositionend", this._onCompositionEnd);
|
|
191
181
|
this.input.addEventListener("beforeinput", this._onBeforeInput);
|
|
182
|
+
this.input.addEventListener("compositionend", this._onCompositionEnd);
|
|
192
183
|
this.input.addEventListener("keydown", this._onKeyDown);
|
|
193
184
|
this.input.addEventListener("keyup", this._stopPropagation);
|
|
194
185
|
this.input.addEventListener("focus", this._onFocus);
|
|
@@ -200,10 +191,9 @@ var ChatDom = class ChatDom {
|
|
|
200
191
|
}
|
|
201
192
|
}
|
|
202
193
|
_unbind() {
|
|
203
|
-
this.
|
|
204
|
-
this.input.removeEventListener("compositionstart", this._onCompositionStart);
|
|
205
|
-
this.input.removeEventListener("compositionend", this._onCompositionEnd);
|
|
194
|
+
this._cancelPendingSubmit();
|
|
206
195
|
this.input.removeEventListener("beforeinput", this._onBeforeInput);
|
|
196
|
+
this.input.removeEventListener("compositionend", this._onCompositionEnd);
|
|
207
197
|
this.input.removeEventListener("keydown", this._onKeyDown);
|
|
208
198
|
this.input.removeEventListener("keyup", this._stopPropagation);
|
|
209
199
|
this.input.removeEventListener("focus", this._onFocus);
|
|
@@ -218,9 +208,7 @@ var ChatDom = class ChatDom {
|
|
|
218
208
|
this._viewportRaf = 0;
|
|
219
209
|
}
|
|
220
210
|
}
|
|
221
|
-
|
|
222
|
-
e.preventDefault();
|
|
223
|
-
if (this._isComposing) return;
|
|
211
|
+
_submit() {
|
|
224
212
|
const text = this.input.value.trim();
|
|
225
213
|
this.clear();
|
|
226
214
|
if (!text) return;
|
|
@@ -231,25 +219,54 @@ var ChatDom = class ChatDom {
|
|
|
231
219
|
}
|
|
232
220
|
this.blur();
|
|
233
221
|
this._options.returnFocusTo?.focus({ preventScroll: true });
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Sends now or, while the keyboard still holds the typed word as a composition
|
|
225
|
+
* (Android), once it commits the word: clearing the field under a live
|
|
226
|
+
* composition can bring the word back into it.
|
|
227
|
+
*/
|
|
228
|
+
_requestSubmit(isComposing) {
|
|
229
|
+
if (!isComposing) {
|
|
230
|
+
this._cancelPendingSubmit();
|
|
231
|
+
this._submit();
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (this._pendingSubmit) return;
|
|
235
|
+
this._pendingSubmit = window.setTimeout(this._flushSubmit, COMPOSITION_COMMIT_TIMEOUT);
|
|
236
|
+
}
|
|
237
|
+
_cancelPendingSubmit() {
|
|
238
|
+
if (!this._pendingSubmit) return;
|
|
239
|
+
clearTimeout(this._pendingSubmit);
|
|
240
|
+
this._pendingSubmit = 0;
|
|
241
|
+
}
|
|
242
|
+
_flushSubmit = () => {
|
|
243
|
+
this._pendingSubmit = 0;
|
|
244
|
+
this._submit();
|
|
237
245
|
};
|
|
238
246
|
_onCompositionEnd = () => {
|
|
239
|
-
this.
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
_onBeforeInput = (e) => {
|
|
243
|
-
if (e.inputType !== "insertLineBreak") return;
|
|
244
|
-
e.preventDefault();
|
|
245
|
-
this.form.requestSubmit();
|
|
247
|
+
if (!this._pendingSubmit) return;
|
|
248
|
+
clearTimeout(this._pendingSubmit);
|
|
249
|
+
this._pendingSubmit = window.setTimeout(this._flushSubmit, 0);
|
|
246
250
|
};
|
|
247
251
|
_onKeyDown = (e) => {
|
|
248
252
|
e.stopPropagation();
|
|
253
|
+
if (isImeProcessedKey(e)) return;
|
|
254
|
+
if (e.key === "Enter") {
|
|
255
|
+
e.preventDefault();
|
|
256
|
+
this._requestSubmit(e.isComposing);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
249
259
|
if (e.key !== "Escape") return;
|
|
250
260
|
this.blur();
|
|
251
261
|
this._options.returnFocusTo?.focus({ preventScroll: true });
|
|
252
262
|
};
|
|
263
|
+
/** Fallback for keyboards that insert a line break instead of sending Enter. */
|
|
264
|
+
_onBeforeInput = (e) => {
|
|
265
|
+
const { inputType, data, isComposing } = e;
|
|
266
|
+
if (!(inputType === "insertLineBreak" || inputType === "insertParagraph" || inputType === "insertText" && data === "\n")) return;
|
|
267
|
+
e.preventDefault();
|
|
268
|
+
this._requestSubmit(isComposing);
|
|
269
|
+
};
|
|
253
270
|
_stopPropagation = (e) => {
|
|
254
271
|
e.stopPropagation();
|
|
255
272
|
};
|
|
@@ -298,6 +315,14 @@ var ChatDom = class ChatDom {
|
|
|
298
315
|
document.head.append(style);
|
|
299
316
|
}
|
|
300
317
|
};
|
|
318
|
+
/**
|
|
319
|
+
* The key went to the IME, not the page: every engine marks it with `keyCode 229`
|
|
320
|
+
* (Safari even after `compositionend`). Not `isComposing`: on Android the typed word
|
|
321
|
+
* is always a composition, and an Enter that reaches the page there means "send".
|
|
322
|
+
*/
|
|
323
|
+
function isImeProcessedKey(e) {
|
|
324
|
+
return e.keyCode === 229;
|
|
325
|
+
}
|
|
301
326
|
//#endregion
|
|
302
327
|
//#region packages/world-runtime/src/modules/chat/ChatModule.ts
|
|
303
328
|
/**
|
|
@@ -18,7 +18,7 @@ var SightInteractableArea = class SightInteractableArea extends Object3DBehaviou
|
|
|
18
18
|
this.object.getWorldPosition(this._initWorldPos);
|
|
19
19
|
}
|
|
20
20
|
onDisable() {
|
|
21
|
-
this.
|
|
21
|
+
this._hidePrompt();
|
|
22
22
|
}
|
|
23
23
|
setRadius(r) {
|
|
24
24
|
this.radius = r;
|
|
@@ -38,10 +38,10 @@ var SightInteractableArea = class SightInteractableArea extends Object3DBehaviou
|
|
|
38
38
|
if (this._active === active) return;
|
|
39
39
|
this._active = active;
|
|
40
40
|
this.emit("activechanged", active);
|
|
41
|
-
if (active) this.
|
|
42
|
-
else this.
|
|
41
|
+
if (active) this._showPrompt();
|
|
42
|
+
else this._hidePrompt();
|
|
43
43
|
}
|
|
44
|
-
|
|
44
|
+
_showPrompt() {
|
|
45
45
|
const modules = this.modules;
|
|
46
46
|
SightInteractableArea._CURRENT = this.object.id;
|
|
47
47
|
modules.html.setCrosshair(this.hint);
|
|
@@ -50,7 +50,7 @@ var SightInteractableArea = class SightInteractableArea extends Object3DBehaviou
|
|
|
50
50
|
modules.html.crosshair.addEventListener("pointerdown", this._interact);
|
|
51
51
|
} else this.ctx.renderer.domElement.addEventListener("mousedown", this._interact);
|
|
52
52
|
}
|
|
53
|
-
|
|
53
|
+
_hidePrompt() {
|
|
54
54
|
const modules = this.modules;
|
|
55
55
|
if (SightInteractableArea._CURRENT === this.object.id) {
|
|
56
56
|
SightInteractableArea._CURRENT = null;
|
|
@@ -66,7 +66,7 @@ var SightInteractableArea = class SightInteractableArea extends Object3DBehaviou
|
|
|
66
66
|
const current = SightInteractableArea._CURRENT;
|
|
67
67
|
if (!(current === this.object.id || current === null)) return;
|
|
68
68
|
this.interactEnabled = false;
|
|
69
|
-
this.
|
|
69
|
+
this._hidePrompt();
|
|
70
70
|
this._active = false;
|
|
71
71
|
this.emit("interaction");
|
|
72
72
|
console.log("interaction with ", this.object.name);
|
|
@@ -9,10 +9,9 @@ import type { AddonsConfig, WorldImports } from "./types";
|
|
|
9
9
|
* by a module/behaviour through `requiredLibs`. Scripts reach all of them by
|
|
10
10
|
* `require("<key>")`.
|
|
11
11
|
*
|
|
12
|
-
* This is the `addons` ctx module, but it
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* runtime owner (see `WorldImports`).
|
|
12
|
+
* This is the `addons` ctx module, but it is loaded before the ctx exists: a
|
|
13
|
+
* module may need its lib (physics needs rapier) the moment the ctx is built.
|
|
14
|
+
* How to import optional packages is up to the runtime owner (see `WorldImports`).
|
|
16
15
|
*/
|
|
17
16
|
export declare class AddonsRuntime extends ContextModule implements AddonsModuleApi {
|
|
18
17
|
private readonly _imports;
|
|
@@ -17,7 +17,7 @@ export default class SightInteractableArea extends Object3DBehaviour<{
|
|
|
17
17
|
onDisable(): void;
|
|
18
18
|
setRadius(r: number): void;
|
|
19
19
|
onBeforeRender(): void;
|
|
20
|
-
private
|
|
21
|
-
private
|
|
20
|
+
private _showPrompt;
|
|
21
|
+
private _hidePrompt;
|
|
22
22
|
private _interact;
|
|
23
23
|
}
|
|
@@ -49,7 +49,7 @@ var chatModuleDescriptor = {
|
|
|
49
49
|
normalize: (raw) => normalizeChat(raw),
|
|
50
50
|
defaultConfig: () => ({ ...DEFAULTS }),
|
|
51
51
|
create: async (config) => {
|
|
52
|
-
const { ChatModule } = await import("./ChatModule-
|
|
52
|
+
const { ChatModule } = await import("./ChatModule-BFNVVLA8.js");
|
|
53
53
|
return new ChatModule(config);
|
|
54
54
|
},
|
|
55
55
|
schema: {
|
package/dist/index.d.ts
CHANGED
|
@@ -7,8 +7,9 @@ export { WorldEvent } from "./world-events";
|
|
|
7
7
|
export { lazyProxy } from "./utils/lazy";
|
|
8
8
|
export { WorldStores } from "./world-stores";
|
|
9
9
|
export type { WorldStore } from "./world-stores";
|
|
10
|
-
export { createWorldModules, EnvModule, HtmlHudModule, InputSystemModule, isMobileDevice, SoundsModule, UtilsModule, } from "./modules";
|
|
10
|
+
export { createWorldModules, EnvModule, HtmlHudModule, InputSystemModule, isMobileDevice, NullPlatformModule, SoundsModule, UtilsModule, } from "./modules";
|
|
11
11
|
export type { EnvMode, WorldModules } from "./modules";
|
|
12
|
+
export type { PlatformModuleApi, PlatformPlayerApi, PlatformPlayerStorageApi, WorldName, WorldNamesRegister, WorldOpenParams, } from "./modules/contracts/platform.module-api";
|
|
12
13
|
export { DEFAULT_RESOURCE_IDS, DefaultResourceId, getDefaultResource, isDefaultResourceId, } from "./resources/default-resources";
|
|
13
14
|
export type { DefaultResourceIdValue, DefaultResourceTypes, } from "./resources/default-resources";
|
|
14
15
|
export { AssetLoaders, configureAssetLoaders, LOADING_MANAGER } from "./resources/loaders";
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as OPTIONAL_MODULE_IDS, i as normalizeChat, n as CHAT_INPUT_MODES, o as OptionalModuleId, r as chatModuleDescriptor } from "./chat.module-descriptor-
|
|
1
|
+
import { a as OPTIONAL_MODULE_IDS, i as normalizeChat, n as CHAT_INPUT_MODES, o as OptionalModuleId, r as chatModuleDescriptor } from "./chat.module-descriptor-CGeOaq16.js";
|
|
2
2
|
import { a as LibId, c as libByRequireKey, i as LIB_IDS, n as LIBS, o as OPTIONAL_LIB_DESCRIPTORS, r as LIB_DESCRIPTORS, s as OPTIONAL_LIB_IDS, t as BUILTIN_LIB_DESCRIPTORS } from "./lib-registry-CPwXvdYs.js";
|
|
3
3
|
import { n as getRadiusScale, t as getHalfheightRadiusScale } from "./get-scale-from-obj-B7JlVPni.js";
|
|
4
4
|
import * as THREE from "three/webgpu";
|
|
@@ -875,6 +875,36 @@ function isUiKeyboardTarget(target) {
|
|
|
875
875
|
}
|
|
876
876
|
}
|
|
877
877
|
//#endregion
|
|
878
|
+
//#region packages/world-runtime/src/modules/platform/NullPlatformModule.ts
|
|
879
|
+
var NO_PARAMS = Object.freeze({});
|
|
880
|
+
var NULL_PLAYER = {
|
|
881
|
+
username: null,
|
|
882
|
+
storage: {
|
|
883
|
+
get: () => Promise.resolve(null),
|
|
884
|
+
save: () => Promise.resolve()
|
|
885
|
+
}
|
|
886
|
+
};
|
|
887
|
+
/**
|
|
888
|
+
* `world.platform` без платформы: self-hosted экспорт, мир без моста. Все вызовы
|
|
889
|
+
* безопасны — чтения пустые, записи ничего не делают. Мир на платформе получает
|
|
890
|
+
* вместо него реализацию хоста, переданную в `createWorldModules({ platform })`.
|
|
891
|
+
*/
|
|
892
|
+
var NullPlatformModule = class extends ContextModule {
|
|
893
|
+
available = false;
|
|
894
|
+
player = NULL_PLAYER;
|
|
895
|
+
openedWith = NO_PARAMS;
|
|
896
|
+
referrer = null;
|
|
897
|
+
openWorld() {
|
|
898
|
+
return Promise.resolve(false);
|
|
899
|
+
}
|
|
900
|
+
resolveWorld() {
|
|
901
|
+
return Promise.resolve(null);
|
|
902
|
+
}
|
|
903
|
+
openLink() {
|
|
904
|
+
return Promise.resolve(false);
|
|
905
|
+
}
|
|
906
|
+
};
|
|
907
|
+
//#endregion
|
|
878
908
|
//#region packages/world-runtime/src/modules/SoundsModule.ts
|
|
879
909
|
var SoundsModule = class extends ContextModule {
|
|
880
910
|
global = Howler;
|
|
@@ -913,6 +943,7 @@ async function createWorldModules(mode = "prod", options) {
|
|
|
913
943
|
env: new EnvModule(mode),
|
|
914
944
|
html: new HtmlHudModule(),
|
|
915
945
|
input: new InputSystemModule(),
|
|
946
|
+
platform: options.platform ?? new NullPlatformModule(),
|
|
916
947
|
sounds: new SoundsModule(),
|
|
917
948
|
utils: new UtilsModule()
|
|
918
949
|
};
|
|
@@ -963,7 +994,7 @@ var entries = [
|
|
|
963
994
|
{
|
|
964
995
|
uuid: "sightinteractablearea",
|
|
965
996
|
name: "SightInteractableArea",
|
|
966
|
-
load: () => loadDefault(import("./SightInteractableArea-
|
|
997
|
+
load: () => loadDefault(import("./SightInteractableArea-BK8IXNIl.js"))
|
|
967
998
|
},
|
|
968
999
|
{
|
|
969
1000
|
uuid: "meshinstansing",
|
|
@@ -1155,10 +1186,9 @@ function collectRequiredLibs(addons, usedBuiltinUuids = []) {
|
|
|
1155
1186
|
* by a module/behaviour through `requiredLibs`. Scripts reach all of them by
|
|
1156
1187
|
* `require("<key>")`.
|
|
1157
1188
|
*
|
|
1158
|
-
* This is the `addons` ctx module, but it
|
|
1159
|
-
*
|
|
1160
|
-
*
|
|
1161
|
-
* runtime owner (see `WorldImports`).
|
|
1189
|
+
* This is the `addons` ctx module, but it is loaded before the ctx exists: a
|
|
1190
|
+
* module may need its lib (physics needs rapier) the moment the ctx is built.
|
|
1191
|
+
* How to import optional packages is up to the runtime owner (see `WorldImports`).
|
|
1162
1192
|
*/
|
|
1163
1193
|
var AddonsRuntime = class extends ContextModule {
|
|
1164
1194
|
_imports;
|
|
@@ -1578,7 +1608,7 @@ function isAbsoluteUrl(url) {
|
|
|
1578
1608
|
}
|
|
1579
1609
|
//#endregion
|
|
1580
1610
|
//#region packages/world-runtime/src/version.gen.ts
|
|
1581
|
-
var RUNTIME_VERSION = "0.6.
|
|
1611
|
+
var RUNTIME_VERSION = "0.6.8";
|
|
1582
1612
|
//#endregion
|
|
1583
1613
|
//#region packages/world-runtime/src/version.ts
|
|
1584
1614
|
/**
|
|
@@ -2109,4 +2139,4 @@ var TONE_MAPPING_NAMES = [
|
|
|
2109
2139
|
"Neutral"
|
|
2110
2140
|
];
|
|
2111
2141
|
//#endregion
|
|
2112
|
-
export { AddonsRuntime, AssetLoaders, AudioSource, BLOOM_MODES, BUILTIN_BEHAVIOURS, BUILTIN_BEHAVIOUR_LIST, BUILTIN_LIB_DESCRIPTORS, CHAT_INPUT_MODES, DEFAULT_RESOURCE_IDS, DefaultResourceId, EXTERNAL_TYPES, EnvModule, FontSource, HtmlHudModule, InputSystemModule, LIBS, LIB_DESCRIPTORS, LIB_IDS, LOADING_MANAGER, LibId, OPTIONAL_LIB_DESCRIPTORS, OPTIONAL_LIB_IDS, OPTIONAL_MODULES, OPTIONAL_MODULE_IDS, OptionalModuleId, PHYSICS_ENGINES, RUNTIME_VERSION, SHADOW_MAP_TYPES, SceneBloom, SoundsModule, TONE_MAPPING_NAMES, TslNode, UNIFORM_DEFAULTS, UNIFORM_TYPES, UtilsModule, VideoSource, WORLD_FORMAT_VERSION, World, WorldEvent, WorldStores, applyRendererConfig, applyTextureDefaults, collectModelSubResources, collectRequiredLibs, configureAssetLoaders, createAudio, createFont, createTslNode, createUniformNode, createVideo, createWorldModules, defineWorld, enableSceneEmissiveMrt, fontFormatFromMime, getBuiltinBehaviour, getDefaultResource, getHalfheightRadiusScale, getOptionalModuleDescriptor, getRadiusScale, isDefaultResourceId, isMobileDevice, isUniformType, lazyProxy, libByRequireKey, loadGeometry, loadHdr, loadModel, loadTexture, normalizeChat, normalizePhysics, parseGeometryJson, parseHdr, parseModel, rapierPhysicsPatch, setColor, uniformType };
|
|
2142
|
+
export { AddonsRuntime, AssetLoaders, AudioSource, BLOOM_MODES, BUILTIN_BEHAVIOURS, BUILTIN_BEHAVIOUR_LIST, BUILTIN_LIB_DESCRIPTORS, CHAT_INPUT_MODES, DEFAULT_RESOURCE_IDS, DefaultResourceId, EXTERNAL_TYPES, EnvModule, FontSource, HtmlHudModule, InputSystemModule, LIBS, LIB_DESCRIPTORS, LIB_IDS, LOADING_MANAGER, LibId, NullPlatformModule, OPTIONAL_LIB_DESCRIPTORS, OPTIONAL_LIB_IDS, OPTIONAL_MODULES, OPTIONAL_MODULE_IDS, OptionalModuleId, PHYSICS_ENGINES, RUNTIME_VERSION, SHADOW_MAP_TYPES, SceneBloom, SoundsModule, TONE_MAPPING_NAMES, TslNode, UNIFORM_DEFAULTS, UNIFORM_TYPES, UtilsModule, VideoSource, WORLD_FORMAT_VERSION, World, WorldEvent, WorldStores, applyRendererConfig, applyTextureDefaults, collectModelSubResources, collectRequiredLibs, configureAssetLoaders, createAudio, createFont, createTslNode, createUniformNode, createVideo, createWorldModules, defineWorld, enableSceneEmissiveMrt, fontFormatFromMime, getBuiltinBehaviour, getDefaultResource, getHalfheightRadiusScale, getOptionalModuleDescriptor, getRadiusScale, isDefaultResourceId, isMobileDevice, isUniformType, lazyProxy, libByRequireKey, loadGeometry, loadHdr, loadModel, loadTexture, normalizeChat, normalizePhysics, parseGeometryJson, parseHdr, parseModel, rapierPhysicsPatch, setColor, uniformType };
|
|
@@ -28,19 +28,18 @@ export interface ChatDomOptions {
|
|
|
28
28
|
/**
|
|
29
29
|
* Chat DOM: the message feed plus the input field.
|
|
30
30
|
*
|
|
31
|
-
* Built to survive real devices:
|
|
32
|
-
*
|
|
33
|
-
* keyboard via `visualViewport`. Details —
|
|
34
|
-
* `./README.md`.
|
|
31
|
+
* Built to survive real devices: sending on the Enter keydown (no `<form>`: a
|
|
32
|
+
* sandboxed host may block form submission), an IME composition guard, and
|
|
33
|
+
* lifting above the on-screen keyboard via `visualViewport`. Details — `./README.md`.
|
|
35
34
|
*/
|
|
36
35
|
export declare class ChatDom {
|
|
37
36
|
readonly root: HTMLDivElement;
|
|
38
37
|
readonly feed: HTMLDivElement;
|
|
39
|
-
readonly form: HTMLFormElement;
|
|
40
38
|
readonly input: HTMLInputElement;
|
|
41
39
|
private readonly _options;
|
|
42
40
|
private _lines;
|
|
43
|
-
|
|
41
|
+
/** Timer of a send waiting for the keyboard to commit its composition. */
|
|
42
|
+
private _pendingSubmit;
|
|
44
43
|
private _viewportRaf;
|
|
45
44
|
private _mounted;
|
|
46
45
|
constructor(options?: ChatDomOptions);
|
|
@@ -77,12 +76,19 @@ export declare class ChatDom {
|
|
|
77
76
|
private _setFaded;
|
|
78
77
|
private _bind;
|
|
79
78
|
private _unbind;
|
|
80
|
-
private
|
|
81
|
-
|
|
79
|
+
private _submit;
|
|
80
|
+
/**
|
|
81
|
+
* Sends now or, while the keyboard still holds the typed word as a composition
|
|
82
|
+
* (Android), once it commits the word: clearing the field under a live
|
|
83
|
+
* composition can bring the word back into it.
|
|
84
|
+
*/
|
|
85
|
+
private _requestSubmit;
|
|
86
|
+
private _cancelPendingSubmit;
|
|
87
|
+
private readonly _flushSubmit;
|
|
82
88
|
private readonly _onCompositionEnd;
|
|
83
|
-
/** Some Android keyboards send no Enter keydown, only `insertLineBreak`. */
|
|
84
|
-
private readonly _onBeforeInput;
|
|
85
89
|
private readonly _onKeyDown;
|
|
90
|
+
/** Fallback for keyboards that insert a line break instead of sending Enter. */
|
|
91
|
+
private readonly _onBeforeInput;
|
|
86
92
|
private readonly _stopPropagation;
|
|
87
93
|
private readonly _onFocus;
|
|
88
94
|
private readonly _onBlur;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public API of the `platform` module — the world's link to the Kvyverse
|
|
3
|
+
* platform. Off the platform (self-hosted export) every call is a safe no-op:
|
|
4
|
+
* nothing throws, reads resolve to empty values.
|
|
5
|
+
*/
|
|
6
|
+
export interface PlatformModuleApi {
|
|
7
|
+
/** The world runs on the platform and the link is established. */
|
|
8
|
+
readonly available: boolean;
|
|
9
|
+
readonly player: PlatformPlayerApi;
|
|
10
|
+
/** Parameters this world was opened with; empty when opened from its own page. */
|
|
11
|
+
readonly openedWith: Readonly<WorldOpenParams>;
|
|
12
|
+
/**
|
|
13
|
+
* The world that switched to this one with `openWorld`; `null` when the
|
|
14
|
+
* player came straight to this page (a reload included).
|
|
15
|
+
*/
|
|
16
|
+
readonly referrer: WorldName | null;
|
|
17
|
+
/**
|
|
18
|
+
* Asks the platform to put another world of the same author on this page,
|
|
19
|
+
* in place of this one. `state` reaches it as its `openedWith`. Resolves to
|
|
20
|
+
* `false` when the platform refuses (another author, no such world) or off
|
|
21
|
+
* the platform; on success this world is torn down.
|
|
22
|
+
*/
|
|
23
|
+
openWorld: (world: WorldName, state?: WorldOpenParams) => Promise<boolean>;
|
|
24
|
+
/**
|
|
25
|
+
* Checks a name that came at runtime (a chat command, player input) against
|
|
26
|
+
* the author's worlds: resolves to its url-name, typed as `WorldName`, or
|
|
27
|
+
* `null` when there is no such world of this author, or off the platform.
|
|
28
|
+
* Takes a url-name or an id.
|
|
29
|
+
*/
|
|
30
|
+
resolveWorld: (world: string) => Promise<WorldName | null>;
|
|
31
|
+
/**
|
|
32
|
+
* Asks the platform to open an external link. The platform shows the address
|
|
33
|
+
* and asks the player; resolves to `false` when they decline, when the
|
|
34
|
+
* address is not http(s), or off the platform.
|
|
35
|
+
*/
|
|
36
|
+
openLink: (url: string) => Promise<boolean>;
|
|
37
|
+
}
|
|
38
|
+
/** Parameters a world is opened with. Values are strings. */
|
|
39
|
+
export type WorldOpenParams = Record<string, string>;
|
|
40
|
+
/**
|
|
41
|
+
* Filled in by the Kvyverse editor for the author's own world: `worlds` lists
|
|
42
|
+
* the author's worlds. Not filled (anywhere else) — any string.
|
|
43
|
+
*/
|
|
44
|
+
export interface WorldNamesRegister {
|
|
45
|
+
}
|
|
46
|
+
/** Url-name of one of the author's worlds: what `openWorld` takes and `referrer` holds. */
|
|
47
|
+
export type WorldName = WorldNamesRegister extends {
|
|
48
|
+
worlds: infer TWorld extends string;
|
|
49
|
+
} ? TWorld : string;
|
|
50
|
+
export interface PlatformPlayerApi {
|
|
51
|
+
/** Display name of the current player; `null` for a guest or off-platform. */
|
|
52
|
+
readonly username: string | null;
|
|
53
|
+
/** Server-side storage for this world + player pair. */
|
|
54
|
+
readonly storage: PlatformPlayerStorageApi;
|
|
55
|
+
}
|
|
56
|
+
/** Storage of one world for one player. Never throws: no platform — no value. */
|
|
57
|
+
export interface PlatformPlayerStorageApi {
|
|
58
|
+
get: () => Promise<string | null>;
|
|
59
|
+
save: (data: string) => Promise<void>;
|
|
60
|
+
}
|
package/dist/modules/index.d.ts
CHANGED
|
@@ -7,13 +7,16 @@ import { UtilsModule } from "./UtilsModule";
|
|
|
7
7
|
import type { AddonsRuntime } from "../addons/AddonsRuntime";
|
|
8
8
|
import type { AddonsConfig } from "../addons/types";
|
|
9
9
|
import type { EnvMode } from "./EnvModule";
|
|
10
|
+
import type { PlatformModuleApi } from "./contracts/platform.module-api";
|
|
10
11
|
import type { KvyverseModules } from "./registry";
|
|
12
|
+
import type { ContextModule } from "three-start";
|
|
11
13
|
export type { ChatModule } from "./chat/ChatModule";
|
|
12
14
|
export type { ChatDom, ChatDomOptions } from "./chat/ChatDom";
|
|
13
15
|
export { ControlsModule } from "./controls/ControlsModule";
|
|
14
16
|
export { EnvModule } from "./EnvModule";
|
|
15
17
|
export { HtmlHudModule } from "./HtmlHudModule";
|
|
16
18
|
export { InputSystemModule } from "./InputSystemModule";
|
|
19
|
+
export { NullPlatformModule } from "./platform/NullPlatformModule";
|
|
17
20
|
export { SoundsModule } from "./SoundsModule";
|
|
18
21
|
export { UtilsModule } from "./UtilsModule";
|
|
19
22
|
export { isMobileDevice } from "./is-mobile-device";
|
|
@@ -25,6 +28,7 @@ export type CoreWorldModules = {
|
|
|
25
28
|
env: EnvModule;
|
|
26
29
|
html: HtmlHudModule;
|
|
27
30
|
input: InputSystemModule;
|
|
31
|
+
platform: ContextModule & PlatformModuleApi;
|
|
28
32
|
sounds: SoundsModule;
|
|
29
33
|
utils: UtilsModule;
|
|
30
34
|
};
|
|
@@ -35,6 +39,8 @@ export interface CreateWorldModulesOptions {
|
|
|
35
39
|
addons: AddonsRuntime;
|
|
36
40
|
/** `settings.addons.json → modules`: which pluggable modules are on, and how. */
|
|
37
41
|
modules?: AddonsConfig["modules"];
|
|
42
|
+
/** `world.platform`: the host's implementation; omitted off the platform, and it turns no-op. */
|
|
43
|
+
platform?: ContextModule & PlatformModuleApi;
|
|
38
44
|
}
|
|
39
45
|
/**
|
|
40
46
|
* Assembles the world's ctx modules: core ones always, pluggable ones per config.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ContextModule } from "three-start";
|
|
2
|
+
import type { PlatformModuleApi, PlatformPlayerApi, WorldOpenParams } from "../contracts/platform.module-api";
|
|
3
|
+
/**
|
|
4
|
+
* `world.platform` без платформы: self-hosted экспорт, мир без моста. Все вызовы
|
|
5
|
+
* безопасны — чтения пустые, записи ничего не делают. Мир на платформе получает
|
|
6
|
+
* вместо него реализацию хоста, переданную в `createWorldModules({ platform })`.
|
|
7
|
+
*/
|
|
8
|
+
export declare class NullPlatformModule extends ContextModule implements PlatformModuleApi {
|
|
9
|
+
readonly available = false;
|
|
10
|
+
readonly player: PlatformPlayerApi;
|
|
11
|
+
readonly openedWith: Readonly<WorldOpenParams>;
|
|
12
|
+
readonly referrer: null;
|
|
13
|
+
openWorld(): Promise<boolean>;
|
|
14
|
+
resolveWorld(): Promise<null>;
|
|
15
|
+
openLink(): Promise<boolean>;
|
|
16
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ContextModule } from "three-start";
|
|
1
2
|
import type { RapierPhysics } from "./physics/RapierPhysics";
|
|
2
3
|
import type { AddonsRuntime } from "../addons/AddonsRuntime";
|
|
3
4
|
import type { ChatModule } from "./chat/ChatModule";
|
|
@@ -5,6 +6,7 @@ import type { ControlsModule } from "./controls/ControlsModule";
|
|
|
5
6
|
import type { EnvModule } from "./EnvModule";
|
|
6
7
|
import type { HtmlHudModule } from "./HtmlHudModule";
|
|
7
8
|
import type { InputSystemModule } from "./InputSystemModule";
|
|
9
|
+
import type { PlatformModuleApi } from "./contracts/platform.module-api";
|
|
8
10
|
import type { SoundsModule } from "./SoundsModule";
|
|
9
11
|
import type { UtilsModule } from "./UtilsModule";
|
|
10
12
|
/**
|
|
@@ -23,6 +25,8 @@ export interface KvyverseModules {
|
|
|
23
25
|
controls: ControlsModule;
|
|
24
26
|
env: EnvModule;
|
|
25
27
|
html: HtmlHudModule;
|
|
28
|
+
/** Реализацию даёт тот, кто собирает мир: хост на платформе или пустая. */
|
|
29
|
+
platform: ContextModule & PlatformModuleApi;
|
|
26
30
|
sounds: SoundsModule;
|
|
27
31
|
utils: UtilsModule;
|
|
28
32
|
rapier: RapierPhysics;
|
package/dist/version.gen.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const RUNTIME_VERSION = "0.6.
|
|
1
|
+
export declare const RUNTIME_VERSION = "0.6.8";
|
package/package.json
CHANGED