@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 CHANGED
@@ -1,11 +1,11 @@
1
1
  # @simpludify/web
2
2
 
3
- Experimental v0.1.2 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.
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.2 version`):
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.2
8
+ npm install @simpludify/web@0.1.3
9
9
  npm install --save-dev vite typescript
10
10
  ```
11
11
 
@@ -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
- display.visible = bars.length > 0;
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;