@simpludify/web 0.1.2 → 0.1.3
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 +3 -3
- package/dist/canvas-visual.d.ts +4 -0
- package/dist/index.js +52 -5
- package/dist/index.js.map +1 -1
- package/dist/renderer.d.ts +1 -0
- package/dist/style.css +10 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# @simpludify/web
|
|
2
2
|
|
|
3
|
-
Experimental v0.1.
|
|
3
|
+
Experimental v0.1.3 browser SDK for Simpludify, a code-first TypeScript 2D engine. APIs may change between 0.x releases. Includes Game, PixiJS rendering, browser input and Canvas/DOM UI adapters; re-exports the host-independent Core API. Core and PixiJS are installed as runtime dependencies, not bundled into this package.
|
|
4
4
|
|
|
5
|
-
Registry installation (check availability with `npm view @simpludify/web@0.1.
|
|
5
|
+
Registry installation (check availability with `npm view @simpludify/web@0.1.3 version`):
|
|
6
6
|
|
|
7
7
|
```sh
|
|
8
|
-
npm install @simpludify/web@0.1.
|
|
8
|
+
npm install @simpludify/web@0.1.3
|
|
9
9
|
npm install --save-dev vite typescript
|
|
10
10
|
```
|
|
11
11
|
|
package/dist/canvas-visual.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ export declare class CanvasVisual implements CanvasPresentation {
|
|
|
18
18
|
private dirty;
|
|
19
19
|
private dead;
|
|
20
20
|
private active;
|
|
21
|
+
private cacheResolution;
|
|
21
22
|
private mirroredFocus;
|
|
22
23
|
private readonly subscriptions;
|
|
23
24
|
private syncs;
|
|
@@ -25,14 +26,17 @@ export declare class CanvasVisual implements CanvasPresentation {
|
|
|
25
26
|
graphics(graphics: Graphics, context: number | undefined): void;
|
|
26
27
|
text(text: Text, key: string | undefined): void;
|
|
27
28
|
changedText(key: string): void;
|
|
29
|
+
cacheResolution(): number | undefined;
|
|
28
30
|
});
|
|
29
31
|
get diagnostics(): {
|
|
30
32
|
nodes: number;
|
|
33
|
+
visibleNodes: number;
|
|
31
34
|
buttons: number;
|
|
32
35
|
syncs: number;
|
|
33
36
|
textMetrics: number;
|
|
34
37
|
};
|
|
35
38
|
private measure;
|
|
39
|
+
private clearCache;
|
|
36
40
|
beforeResolutionChange(): void;
|
|
37
41
|
focus(node: CanvasNode): void;
|
|
38
42
|
private native;
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "./style.css";
|
|
2
2
|
import { ActionController, Aspect, Camera, CanvasButton as CanvasButton$1, CanvasUI, Engine, EngineError, FixedStepRunner, GeometryTilemapLayer, PointerRouting, Renderable, ReplayPlayer, Scene, ScenePointer, SceneState, Shape, TilemapLayer, Transform, canvasControlText, canvasHoverHighlight, canvasOpacity, canvasScrollbars, canvasSliderGeometry, cleanupAll, containsScreenPoint, createViewTransform, imageSize, intersectUI, isKeyCode, positive, screenPoint, screenToWorld, spriteFrameBounds, worldToScreen, wrapCanvasText } from "@simpludify/core";
|
|
3
|
-
import { Application, CanvasTextMetrics, Container, Graphics, Rectangle, Sprite, Text, TextStyle, Texture } from "pixi.js";
|
|
3
|
+
import { Application, CanvasTextMetrics, Container, Graphics, Rectangle, RendererType, Sprite, Text, TextStyle, Texture } from "pixi.js";
|
|
4
4
|
export * from "@simpludify/core";
|
|
5
5
|
// packages/web/src/canvas-node.ts
|
|
6
6
|
/** Bridge owner cancellation to browser APIs such as fetch. */
|
|
@@ -527,6 +527,7 @@ var CanvasVisual = class {
|
|
|
527
527
|
dirty = true;
|
|
528
528
|
dead = false;
|
|
529
529
|
active = false;
|
|
530
|
+
cacheResolution;
|
|
530
531
|
mirroredFocus;
|
|
531
532
|
subscriptions = [];
|
|
532
533
|
syncs = 0;
|
|
@@ -581,6 +582,7 @@ var CanvasVisual = class {
|
|
|
581
582
|
get diagnostics() {
|
|
582
583
|
return {
|
|
583
584
|
nodes: this.entries.size,
|
|
585
|
+
visibleNodes: [...this.entries.values()].filter((e) => e.display.visible).length,
|
|
584
586
|
buttons: [...this.entries.values()].filter((e) => e.button).length,
|
|
585
587
|
syncs: this.syncs,
|
|
586
588
|
textMetrics: this.metrics.size
|
|
@@ -624,7 +626,14 @@ var CanvasVisual = class {
|
|
|
624
626
|
cache.values.set(width, result);
|
|
625
627
|
return result;
|
|
626
628
|
};
|
|
629
|
+
clearCache() {
|
|
630
|
+
if (this.cacheResolution === void 0) return;
|
|
631
|
+
this.display.cacheAsTexture(false);
|
|
632
|
+
this.cacheResolution = void 0;
|
|
633
|
+
}
|
|
627
634
|
beforeResolutionChange() {
|
|
635
|
+
this.clearCache();
|
|
636
|
+
this.dirty = true;
|
|
628
637
|
for (const entry of this.entries.values()) if (entry.text && !entry.text.destroyed) this.resources.changedText(entry.text.styleKey);
|
|
629
638
|
}
|
|
630
639
|
focus(node) {
|
|
@@ -753,6 +762,7 @@ var CanvasVisual = class {
|
|
|
753
762
|
const visited = /* @__PURE__ */ new Set(), buttons = [];
|
|
754
763
|
const paint = [];
|
|
755
764
|
let order = 0;
|
|
765
|
+
let scrollVisible = false;
|
|
756
766
|
let restoreNativeFocus;
|
|
757
767
|
this.ui.walk((node, parentClip) => {
|
|
758
768
|
const measuredText = node.kind === "panel" ? void 0 : this.measure(node, Math.max(0, node.bounds.width - node.style.padding * 2));
|
|
@@ -777,6 +787,19 @@ var CanvasVisual = class {
|
|
|
777
787
|
entry.button = entry.native.element;
|
|
778
788
|
} else entry.button = this.button(node);
|
|
779
789
|
this.element.append(entry.button);
|
|
790
|
+
} else if (node.kind === "label") {
|
|
791
|
+
entry.label = document.createElement("span");
|
|
792
|
+
entry.label.className = "s-canvas-label";
|
|
793
|
+
const semantics = node.labelSemantics;
|
|
794
|
+
if (semantics?.role) {
|
|
795
|
+
entry.label.setAttribute("role", semantics.role);
|
|
796
|
+
if (semantics.role === "heading") entry.label.setAttribute("aria-level", String(semantics.level));
|
|
797
|
+
else {
|
|
798
|
+
entry.label.setAttribute("aria-live", semantics.role === "alert" ? "assertive" : "polite");
|
|
799
|
+
entry.label.setAttribute("aria-atomic", "true");
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
this.element.append(entry.label);
|
|
780
803
|
}
|
|
781
804
|
if (node.kind !== "panel") {
|
|
782
805
|
entry.text = new Text({ text: node.text });
|
|
@@ -802,7 +825,14 @@ var CanvasVisual = class {
|
|
|
802
825
|
} : {}
|
|
803
826
|
});
|
|
804
827
|
if (entry.button) buttons.push(entry.button);
|
|
828
|
+
if (entry.label) {
|
|
829
|
+
buttons.push(entry.label);
|
|
830
|
+
if (entry.label.textContent !== node.text) entry.label.textContent = node.text;
|
|
831
|
+
entry.label.setAttribute("aria-hidden", String(canvasOpacity(node) === 0));
|
|
832
|
+
}
|
|
805
833
|
const b = node.bounds, s = node.style;
|
|
834
|
+
const scrollBox = intersectUI(b, clip);
|
|
835
|
+
if (node.style.scroll && scrollBox.width > 0 && scrollBox.height > 0 && canvasOpacity(node) > 0) scrollVisible = true;
|
|
806
836
|
let enabled = !node.busy;
|
|
807
837
|
for (let p = node; p; p = p.parent) enabled &&= p.enabled;
|
|
808
838
|
const signature = JSON.stringify([
|
|
@@ -823,9 +853,12 @@ var CanvasVisual = class {
|
|
|
823
853
|
if (entry.signature === signature) return;
|
|
824
854
|
entry.signature = signature;
|
|
825
855
|
entry.display.position.set(b.x, b.y);
|
|
856
|
+
const paintBox = intersectUI(b, clip);
|
|
857
|
+
const visible = paintBox.width > 0 && paintBox.height > 0 && canvasOpacity(node) > 0;
|
|
858
|
+
entry.display.visible = visible;
|
|
826
859
|
const clipped = clip.x !== 0 || clip.y !== 0 || clip.width !== width / this.ui.scale || clip.height !== height / this.ui.scale;
|
|
827
860
|
entry.display.mask = clipped ? entry.mask : null;
|
|
828
|
-
entry.mask.renderable = clipped;
|
|
861
|
+
entry.mask.renderable = clipped && visible;
|
|
829
862
|
const maskSignature = JSON.stringify([
|
|
830
863
|
clipped,
|
|
831
864
|
clip.x,
|
|
@@ -927,7 +960,8 @@ var CanvasVisual = class {
|
|
|
927
960
|
const display = entry.scrollbar;
|
|
928
961
|
if (!display) return;
|
|
929
962
|
this.display.setChildIndex(display, order++);
|
|
930
|
-
|
|
963
|
+
const viewport = intersectUI(node.bounds, clip);
|
|
964
|
+
display.visible = bars.length > 0 && viewport.width > 0 && viewport.height > 0 && canvasOpacity(node) > 0;
|
|
931
965
|
display.mask = entry.display.mask;
|
|
932
966
|
const signature = JSON.stringify([
|
|
933
967
|
node.style.color,
|
|
@@ -982,6 +1016,14 @@ var CanvasVisual = class {
|
|
|
982
1016
|
const button = focused ? this.entries.get(focused)?.button : void 0;
|
|
983
1017
|
if ((focused !== this.mirroredFocus || focused === restoreNativeFocus) && button && !button.disabled && document.activeElement !== button) button.focus({ preventScroll: true });
|
|
984
1018
|
this.mirroredFocus = this.ui.focusedNode;
|
|
1019
|
+
const ratio = this.resources.cacheResolution();
|
|
1020
|
+
const cache = scrollVisible && ratio !== void 0 && Math.max(width, height) * ratio <= 4096 ? ratio * this.ui.scale : void 0;
|
|
1021
|
+
if (cache !== this.cacheResolution) {
|
|
1022
|
+
this.clearCache();
|
|
1023
|
+
this.cacheResolution = cache;
|
|
1024
|
+
if (cache !== void 0) this.display.cacheAsTexture({ resolution: cache });
|
|
1025
|
+
}
|
|
1026
|
+
if (cache !== void 0) this.display.updateCacheTexture();
|
|
985
1027
|
}
|
|
986
1028
|
releaseEntry(entry) {
|
|
987
1029
|
const backgroundContext = entry.background.context?.uid, maskContext = entry.mask.context?.uid;
|
|
@@ -991,6 +1033,7 @@ var CanvasVisual = class {
|
|
|
991
1033
|
() => entry.abort.abort(),
|
|
992
1034
|
() => entry.native?.dispose(),
|
|
993
1035
|
() => entry.button?.remove(),
|
|
1036
|
+
() => entry.label?.remove(),
|
|
994
1037
|
() => entry.background.destroy({ context: true }),
|
|
995
1038
|
() => entry.scrollbar?.destroy({ context: true }),
|
|
996
1039
|
() => entry.text?.destroy({ style: true }),
|
|
@@ -1015,6 +1058,7 @@ var CanvasVisual = class {
|
|
|
1015
1058
|
cleanupAll([
|
|
1016
1059
|
...this.subscriptions.splice(0),
|
|
1017
1060
|
...entries.map((e) => () => this.releaseEntry(e)),
|
|
1061
|
+
() => this.clearCache(),
|
|
1018
1062
|
() => this.display.destroy({ children: true }),
|
|
1019
1063
|
() => this.element.remove()
|
|
1020
1064
|
], "Canvas visual cleanup failed");
|
|
@@ -1462,10 +1506,11 @@ var PixiRenderer = class {
|
|
|
1462
1506
|
pendingUITextKeys = /* @__PURE__ */ new Set();
|
|
1463
1507
|
canvasPresentations = /* @__PURE__ */ new Set();
|
|
1464
1508
|
get canvasDiagnostics() {
|
|
1465
|
-
let nodes = 0, buttons = 0, syncs = 0, textMetrics = 0;
|
|
1509
|
+
let nodes = 0, visibleNodes = 0, buttons = 0, syncs = 0, textMetrics = 0;
|
|
1466
1510
|
for (const p of this.canvasPresentations) {
|
|
1467
1511
|
const d = p.diagnostics;
|
|
1468
1512
|
nodes += d.nodes;
|
|
1513
|
+
visibleNodes += d.visibleNodes;
|
|
1469
1514
|
buttons += d.buttons;
|
|
1470
1515
|
syncs += d.syncs;
|
|
1471
1516
|
textMetrics += d.textMetrics;
|
|
@@ -1473,6 +1518,7 @@ var PixiRenderer = class {
|
|
|
1473
1518
|
return {
|
|
1474
1519
|
presentations: this.canvasPresentations.size,
|
|
1475
1520
|
nodes,
|
|
1521
|
+
visibleNodes,
|
|
1476
1522
|
buttons,
|
|
1477
1523
|
syncs,
|
|
1478
1524
|
textMetrics
|
|
@@ -1483,7 +1529,8 @@ var PixiRenderer = class {
|
|
|
1483
1529
|
const visual = new CanvasVisual(ui, this.host, routing, {
|
|
1484
1530
|
graphics: (graphics, context) => releaseUIGraphics(this.app.renderer, graphics, context),
|
|
1485
1531
|
text: (text, key) => releaseUIText(this.app.renderer, text, key),
|
|
1486
|
-
changedText: (key) => this.pendingUITextKeys.add(key)
|
|
1532
|
+
changedText: (key) => this.pendingUITextKeys.add(key),
|
|
1533
|
+
cacheResolution: () => this.app.renderer.type === RendererType.WEBGL ? this.app.renderer.resolution : void 0
|
|
1487
1534
|
});
|
|
1488
1535
|
this.canvasPresentations.add(visual);
|
|
1489
1536
|
let disposed = false;
|