@dreamboard-games/sdk 0.5.0-alpha.3 → 0.5.0-alpha.4

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 (36) hide show
  1. package/README.md +52 -2
  2. package/dist/board-target-B0QG6OYx.d.ts +8 -0
  3. package/dist/chunk-3JXUL4VN.js +139 -0
  4. package/dist/chunk-3JXUL4VN.js.map +1 -0
  5. package/dist/{chunk-LRILEVKC.js → chunk-C77SYIVT.js} +82 -109
  6. package/dist/chunk-C77SYIVT.js.map +1 -0
  7. package/dist/{chunk-SP234IY4.js → chunk-OOFMBCFF.js} +178 -203
  8. package/dist/chunk-OOFMBCFF.js.map +1 -0
  9. package/dist/{chunk-ZPRKWPEP.js → chunk-UG2Q4NLG.js} +108 -29
  10. package/dist/chunk-UG2Q4NLG.js.map +1 -0
  11. package/dist/{chunk-FMM5LVJW.js → chunk-XU6WJW6K.js} +26 -12
  12. package/dist/chunk-XU6WJW6K.js.map +1 -0
  13. package/dist/{diagnostics-B0lgnuZ6.d.ts → diagnostics-B8d5aLad.d.ts} +2 -2
  14. package/dist/{definition-BaHNPZol.d.ts → extract-gVKcB8Og.d.ts} +484 -387
  15. package/dist/index.d.ts +404 -175
  16. package/dist/index.js +266 -215
  17. package/dist/index.js.map +1 -1
  18. package/dist/model-vdpLHrxh.d.ts +432 -0
  19. package/dist/react.d.ts +35 -7
  20. package/dist/react.js +136 -8
  21. package/dist/react.js.map +1 -1
  22. package/dist/reducer.d.ts +169 -118
  23. package/dist/reducer.js +206 -328
  24. package/dist/reducer.js.map +1 -1
  25. package/dist/testing.d.ts +5 -3
  26. package/dist/testing.js +9 -6
  27. package/dist/testing.js.map +1 -1
  28. package/dist/{types-DimqnJBq.d.ts → types-Bj5ZLTUV.d.ts} +38 -10
  29. package/package.json +19 -16
  30. package/dist/chunk-FMM5LVJW.js.map +0 -1
  31. package/dist/chunk-LRILEVKC.js.map +0 -1
  32. package/dist/chunk-SP234IY4.js.map +0 -1
  33. package/dist/chunk-VUWNJXLJ.js +0 -52
  34. package/dist/chunk-VUWNJXLJ.js.map +0 -1
  35. package/dist/chunk-ZPRKWPEP.js.map +0 -1
  36. package/dist/model-BHro6yXu.d.ts +0 -288
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  AmbiguousTargetError,
3
3
  createGameInstance
4
- } from "./chunk-SP234IY4.js";
4
+ } from "./chunk-OOFMBCFF.js";
5
5
  import {
6
6
  BoardStaticProjectionSchema,
7
7
  CancelInteractionCommandSchema,
@@ -36,7 +36,7 @@ import {
36
36
  parsePluginSessionDescriptor,
37
37
  parsePluginToHostEnvelope,
38
38
  staticSource
39
- } from "./chunk-ZPRKWPEP.js";
39
+ } from "./chunk-UG2Q4NLG.js";
40
40
  import "./chunk-DMFVAZI2.js";
41
41
  import {
42
42
  InputDomainSchema,
@@ -49,11 +49,14 @@ import {
49
49
  createHexBoardGeometry,
50
50
  digestPluginRuntimeJson,
51
51
  encodeCanonicalPluginRuntimeJson
52
- } from "./chunk-FMM5LVJW.js";
53
- import "./chunk-VUWNJXLJ.js";
52
+ } from "./chunk-XU6WJW6K.js";
53
+ import {
54
+ inputValueKey,
55
+ requireLookup
56
+ } from "./chunk-3JXUL4VN.js";
54
57
 
55
58
  // src/package-set.ts
56
- var DREAMBOARD_SDK_VERSION = true ? "0.5.0-alpha.3" : "0.0.0-development";
59
+ var DREAMBOARD_SDK_VERSION = true ? "0.5.0-alpha.4" : "0.0.0-development";
57
60
  var DREAMBOARD_SDK_PACKAGES = {
58
61
  "@dreamboard-games/sdk": DREAMBOARD_SDK_VERSION
59
62
  };
@@ -89,6 +92,10 @@ function iframeSource(options = {}) {
89
92
  recover: () => send({ type: "runtime.resume" }),
90
93
  close: () => target.removeEventListener("message", receive)
91
94
  });
95
+ const isOpen = () => {
96
+ const { connection } = lifecycle.source.store.get();
97
+ return connection !== "closed" && connection !== "failed";
98
+ };
92
99
  function receive(event) {
93
100
  if (event.source !== parent) return;
94
101
  const parsed = HostToPluginEnvelopeSchema.safeParse(event.data);
@@ -118,13 +125,11 @@ function iframeSource(options = {}) {
118
125
  const payload = envelope.payload;
119
126
  if (payload.type === "runtime.init") {
120
127
  lifecycle.session(payload.session);
121
- if (lifecycle.source.store.get().connection !== "closed")
122
- send({ type: "runtime.ready" });
128
+ if (isOpen()) send({ type: "runtime.ready" });
123
129
  } else if (payload.type === "gameplay.frame")
124
130
  lifecycle.frame(payload.frame);
125
131
  else lifecycle.result(payload);
126
- if (lifecycle.source.store.get().connection !== "closed")
127
- send({ type: "runtime.ack", sequence: envelope.sequence });
132
+ if (isOpen()) send({ type: "runtime.ack", sequence: envelope.sequence });
128
133
  }
129
134
  target.addEventListener("message", receive);
130
135
  return lifecycle.source;
@@ -484,7 +489,10 @@ function boardFeature(game, context) {
484
489
  return cached.collection;
485
490
  const projected = model.view;
486
491
  const boards = Object.values(projected?.boards?.byId ?? {}).map((data) => {
487
- const board = context.createBoard(data.id, data);
492
+ const board = context.createBoard(
493
+ data.id,
494
+ data
495
+ );
488
496
  let geometry = geometries.get(data);
489
497
  if (!geometry && data.layout === "hex") {
490
498
  geometry = createHexBoardGeometry({
@@ -500,6 +508,9 @@ function boardFeature(game, context) {
500
508
  const byId = new Map(boards.map((board) => [board.id, board]));
501
509
  const result = Object.freeze({
502
510
  get(id) {
511
+ return requireLookup(byId.get(id), "Board", id);
512
+ },
513
+ find(id) {
503
514
  return byId.get(id);
504
515
  },
505
516
  getAll: () => Object.freeze(boards)
@@ -512,6 +523,115 @@ function boardFeature(game, context) {
512
523
  };
513
524
  return result;
514
525
  }
526
+ function targetsFor(owner) {
527
+ const board = owner.data;
528
+ const captured = captures.get(owner);
529
+ function matchingTargets(model, kind, id) {
530
+ const inputKinds = kind === "space" ? ["space", "tile"] : [kind];
531
+ return model.interactions.list().flatMap(
532
+ (interaction) => interaction.getInputs().filter((input) => {
533
+ const domain = input.getDomain();
534
+ return domain.type === "boardTarget" && inputKinds.includes(String(domain.targetKind)) && (domain.valueKind === "player-board-space" ? board.scope === "perPlayer" && domain.boardId === board.baseId : domain.boardId === board.id);
535
+ }).map((input) => {
536
+ const domain = input.getDomain();
537
+ return {
538
+ interaction,
539
+ input,
540
+ value: domain.type === "boardTarget" && domain.valueKind === "player-board-space" ? Object.freeze({
541
+ boardId: board.baseId,
542
+ playerId: board.playerId,
543
+ spaceId: id
544
+ }) : id
545
+ };
546
+ })
547
+ );
548
+ }
549
+ function target(kind, id) {
550
+ const matches = matchingTargets(captured.model, kind, id);
551
+ const eligible = matches.some(
552
+ ({ input, value }) => input.getIsEligible(value)
553
+ );
554
+ const selected = matches.some(
555
+ ({ input, value }) => input.getIsSelected(value)
556
+ );
557
+ const selectable = matches.some(
558
+ ({ input, value }) => !input.getTargetProps(value).disabled
559
+ );
560
+ function select(options) {
561
+ if (game.getOptions().source !== captured.source || game.snapshot?.me !== captured.model.snapshot?.me)
562
+ return;
563
+ const matching = matchingTargets(game.getSnapshot(), kind, id).filter(
564
+ ({ interaction, input, value: value2 }) => (!options?.interaction || options.interaction === interaction.key) && (!options?.input || options.input === input.key) && !input.getTargetProps(value2).disabled
565
+ );
566
+ if (!matching.length) return;
567
+ if (matching.length > 1) throw new AmbiguousTargetError(id);
568
+ const domain = matching[0].input.getDomain();
569
+ if (domain.type !== "boardTarget") return;
570
+ const value = matching[0].value;
571
+ const target2 = domain.valueKind === "player-board-space" && typeof value !== "string" ? { kind: "space", valueKind: domain.valueKind, value } : {
572
+ kind,
573
+ valueKind: "board-id",
574
+ value: id,
575
+ boardId: board.id
576
+ };
577
+ context.routeTarget(target2, {
578
+ interaction: matching[0].interaction.key,
579
+ input: matching[0].input.key
580
+ });
581
+ }
582
+ return {
583
+ id,
584
+ getIsEligible: () => eligible,
585
+ getIsSelectable: () => selectable,
586
+ getIsSelected: () => selected,
587
+ getSelectHandler: (options) => () => select(options),
588
+ getTargetProps(options) {
589
+ const candidates = matches.filter(
590
+ ({ interaction, input, value }) => (!options?.interaction || interaction.key === options.interaction) && (!options?.input || options.input === input.key) && !input.getTargetProps(value).disabled
591
+ );
592
+ const only = candidates.length === 1 ? candidates[0] : void 0;
593
+ return {
594
+ type: "button",
595
+ disabled: candidates.length === 0,
596
+ "data-action": "select",
597
+ "data-value": only && typeof only.value !== "string" ? JSON.stringify(only.value) : id,
598
+ "data-board": board.id,
599
+ "data-interaction": options?.interaction ?? only?.interaction.key,
600
+ "data-input": only?.input.key,
601
+ "data-eligible": eligible,
602
+ "data-selected": selected,
603
+ "data-disabled": candidates.length === 0,
604
+ onClick: () => select(options)
605
+ };
606
+ }
607
+ };
608
+ }
609
+ return target;
610
+ }
611
+ const spaceCollections = /* @__PURE__ */ new WeakMap();
612
+ function spacesFor(owner) {
613
+ const cached2 = spaceCollections.get(owner);
614
+ if (cached2) return cached2;
615
+ const target = targetsFor(owner);
616
+ const data = owner.data;
617
+ const values = Object.freeze(
618
+ Object.values(data.spaces).map(
619
+ (space) => Object.freeze({
620
+ ...target("space", space.id),
621
+ data: space,
622
+ board: owner
623
+ })
624
+ )
625
+ );
626
+ const byId = new Map(values.map((space) => [space.id, space]));
627
+ const spaces = Object.freeze({
628
+ get: (id) => requireLookup(byId.get(id), `Space on board ${owner.id}`, id),
629
+ find: (id) => byId.get(id),
630
+ getAll: () => values
631
+ });
632
+ spaceCollections.set(owner, spaces);
633
+ return spaces;
634
+ }
515
635
  return {
516
636
  root: {
517
637
  get boards() {
@@ -519,11 +639,14 @@ function boardFeature(game, context) {
519
639
  }
520
640
  },
521
641
  board: {
642
+ get spaces() {
643
+ return spacesFor(this);
644
+ },
522
645
  getLayout(options) {
523
646
  const board = this.data;
524
647
  if (board.layout === "generic")
525
648
  throw new Error(
526
- `Board '${board.id}' has no spatial geometry; read its data instead.`
649
+ `Board '${board.id}' has no spatial geometry; use board.spaces for semantic selection.`
527
650
  );
528
651
  const {
529
652
  hexSize,
@@ -546,70 +669,16 @@ function boardFeature(game, context) {
546
669
  x: value.x * viewport.scale + viewport.x,
547
670
  y: value.y * viewport.scale + viewport.y
548
671
  });
549
- function matchingTargets(model, kind) {
550
- const inputKinds = kind === "space" ? ["space", "tile"] : [kind];
551
- return model.interactions.list().flatMap(
552
- (interaction) => interaction.getInputs().filter((input) => {
553
- const domain = input.getDomain();
554
- return domain.type === "boardTarget" && inputKinds.includes(String(domain.targetKind)) && (domain.boardId === board.id || domain.boardId === board.baseId) && (board.scope !== "perPlayer" || board.playerId === model.me?.id);
555
- }).map((input) => ({ interaction, input }))
556
- );
557
- }
558
- function target(kind, id) {
559
- const matches = matchingTargets(captured.model, kind);
560
- const eligible = matches.some(({ input }) => input.getIsEligible(id));
561
- const selected = matches.some(({ input }) => input.getIsSelected(id));
562
- const selectable = matches.some(
563
- ({ input }) => !input.getTargetProps(id).disabled
564
- );
565
- function select(options2) {
566
- if (game.getOptions().source !== captured.source || game.snapshot?.me !== captured.model.snapshot?.me)
567
- return;
568
- const matching = matchingTargets(game.getSnapshot(), kind).filter(
569
- ({ interaction, input }) => (!options2?.interaction || options2.interaction === interaction.key) && !input.getTargetProps(id).disabled
570
- );
571
- if (!matching.length) return;
572
- const domain = matching[0].input.getDomain();
573
- if (domain.type !== "boardTarget") return;
574
- context.routeTarget(kind, id, {
575
- ...options2,
576
- boardId: String(domain.boardId)
577
- });
578
- }
579
- return {
580
- id,
581
- getIsEligible: () => eligible,
582
- getIsSelectable: () => selectable,
583
- getIsSelected: () => selected,
584
- getSelectHandler: (options2) => () => select(options2),
585
- getTargetProps(options2) {
586
- const candidates = matches.filter(
587
- ({ interaction, input }) => (!options2?.interaction || interaction.key === options2.interaction) && !input.getTargetProps(id).disabled
588
- );
589
- const only = candidates.length === 1 ? candidates[0] : void 0;
590
- return {
591
- type: "button",
592
- disabled: candidates.length === 0,
593
- "data-action": "select",
594
- "data-value": id,
595
- "data-board": board.id,
596
- "data-interaction": options2?.interaction ?? only?.interaction.key,
597
- "data-input": only?.input.key,
598
- "data-eligible": eligible,
599
- "data-selected": selected,
600
- "data-disabled": candidates.length === 0,
601
- onClick: () => select(options2)
602
- };
603
- }
604
- };
605
- }
672
+ const target = targetsFor(this);
673
+ const semanticSpaces = spacesFor(this);
606
674
  const spaces = Object.freeze(
607
675
  geometry.spaces.map((space) => {
608
676
  const corners = Object.freeze(space.corners.map(point));
609
677
  const center = point(space.center);
610
678
  return Object.freeze({
611
- ...target("space", space.id),
612
- data: board.spaces[space.id],
679
+ ...semanticSpaces.get(
680
+ space.id
681
+ ),
613
682
  center,
614
683
  points: () => corners,
615
684
  transform: `translate(${center.x} ${center.y})`
@@ -655,6 +724,133 @@ function boardFeature(game, context) {
655
724
  };
656
725
  }
657
726
 
727
+ // src/headless/features/drag.ts
728
+ function dragFeature(game, context) {
729
+ let active = null;
730
+ let selection;
731
+ let source = game.getOptions().source;
732
+ let seat = game.snapshot?.me;
733
+ let disposed = false;
734
+ let version = game.snapshot?.version;
735
+ let lastInteractions = game.interactions;
736
+ let lastCards = game.cards;
737
+ let branch = snapshot();
738
+ function update(next) {
739
+ if (disposed) return;
740
+ active = next === null ? null : Object.freeze(next);
741
+ branch = snapshot();
742
+ context.invalidate();
743
+ }
744
+ function targets(cardId = active?.cardId, selected = selection) {
745
+ if (!cardId || game.connection !== "ready") return [];
746
+ const runtime = game;
747
+ const options = selected;
748
+ const resolved = (runtime.cards.find(cardId)?.getInteractions() ?? []).flatMap((candidate) => {
749
+ if (!candidate.getIsAvailable() || options?.interaction && candidate.key !== options.interaction)
750
+ return [];
751
+ const inputs = candidate.getInputs();
752
+ const cardInputs = inputs.filter(
753
+ (input) => input.getDomain().type === "cardTarget" && (!options?.input || input.key === options.input) && input.getIsEligible(cardId)
754
+ );
755
+ return cardInputs.flatMap(
756
+ (cardInput) => inputs.flatMap((input) => {
757
+ const domain = input.getDomain();
758
+ if (domain.type !== "boardTarget") return [];
759
+ const route = {
760
+ interactionKey: candidate.key,
761
+ cardInputKey: cardInput.key,
762
+ inputKey: input.key
763
+ };
764
+ if (domain.valueKind === "player-board-space")
765
+ return domain.eligibleTargets.filter((value) => !input.getTargetProps(value).disabled).map(
766
+ (value) => Object.freeze({
767
+ kind: "space",
768
+ valueKind: domain.valueKind,
769
+ value,
770
+ ...route
771
+ })
772
+ );
773
+ return domain.eligibleTargets.filter((value) => !input.getTargetProps(value).disabled).map(
774
+ (value) => Object.freeze({
775
+ kind: domain.targetKind,
776
+ valueKind: domain.valueKind,
777
+ value,
778
+ boardId: domain.boardId,
779
+ ...route
780
+ })
781
+ );
782
+ })
783
+ );
784
+ });
785
+ return resolved;
786
+ }
787
+ function sameTarget(left, right) {
788
+ return left.kind === right.kind && left.valueKind === right.valueKind && inputValueKey(left.value) === inputValueKey(right.value) && (left.valueKind !== "board-id" || right.valueKind === "board-id" && left.boardId === right.boardId) && left.interactionKey === right.interactionKey && left.cardInputKey === right.cardInputKey && left.inputKey === right.inputKey;
789
+ }
790
+ function cancel() {
791
+ if (active) update(null);
792
+ }
793
+ function snapshot() {
794
+ const captured = active;
795
+ const dropTargets = Object.freeze(targets());
796
+ return Object.freeze({
797
+ active: captured,
798
+ getCanDrag: (cardId, options) => !disposed && targets(cardId, options).length > 0,
799
+ begin(cardId, options) {
800
+ if (disposed || !targets(cardId, options).length) return false;
801
+ selection = options;
802
+ update({ cardId, target: null });
803
+ return true;
804
+ },
805
+ getDropTargets: () => dropTargets,
806
+ setDropTarget(target) {
807
+ if (!active || disposed) return;
808
+ if (target && !targets().some((candidate) => sameTarget(candidate, target))) {
809
+ update({ ...active, target: null });
810
+ return;
811
+ }
812
+ update({ ...active, target });
813
+ },
814
+ drop() {
815
+ const finished = active;
816
+ const eligible = finished?.target && targets().some((target) => sameTarget(target, finished.target));
817
+ cancel();
818
+ if (!disposed && finished?.target && eligible)
819
+ context.routeCardDrop(finished.cardId, finished.target);
820
+ },
821
+ cancel
822
+ });
823
+ }
824
+ const unsubscribe = game.subscribe(() => {
825
+ const nextSource = game.getOptions().source;
826
+ const nextSeat = game.snapshot?.me;
827
+ const nextVersion = game.snapshot?.version;
828
+ if (nextSource !== source || nextSeat !== seat || nextVersion !== version || game.connection !== "ready") {
829
+ source = nextSource;
830
+ seat = nextSeat;
831
+ version = nextVersion;
832
+ cancel();
833
+ }
834
+ });
835
+ return {
836
+ root: {
837
+ get drag() {
838
+ if (lastInteractions !== game.interactions || lastCards !== game.cards) {
839
+ lastInteractions = game.interactions;
840
+ lastCards = game.cards;
841
+ branch = snapshot();
842
+ }
843
+ return branch;
844
+ }
845
+ },
846
+ dispose() {
847
+ unsubscribe();
848
+ cancel();
849
+ disposed = true;
850
+ }
851
+ };
852
+ }
853
+
658
854
  // src/headless/features/pointer-session.ts
659
855
  function createPointerSession(callbacks) {
660
856
  let disposed = false;
@@ -717,151 +913,6 @@ function createPointerSession(callbacks) {
717
913
  };
718
914
  }
719
915
 
720
- // src/headless/features/drag.ts
721
- function dragFeature(game, context) {
722
- let active = null;
723
- let interaction;
724
- let source = game.getOptions().source;
725
- let seat = game.snapshot?.me;
726
- let disposed = false;
727
- let suppressPointerClick = false;
728
- let dragged = false;
729
- let lastInteractions = game.interactions;
730
- let lastCards = game.cards;
731
- let branch = snapshot();
732
- function update(next) {
733
- if (disposed) return;
734
- active = next === null ? null : Object.freeze({ ...next, offset: Object.freeze(next.offset) });
735
- branch = snapshot();
736
- context.invalidate();
737
- }
738
- function targets() {
739
- if (!active) return [];
740
- const cardId = active.cardId;
741
- return (game.cards.get(cardId)?.getInteractions() ?? []).filter(
742
- (candidate) => candidate.getIsAvailable() && (!interaction || candidate.key === interaction) && candidate.getInputs().some(
743
- (input) => input.getDomain().type === "cardTarget" && input.getIsEligible(cardId)
744
- )
745
- ).flatMap(
746
- (candidate) => candidate.getInputs().flatMap((input) => {
747
- const domain = input.getDomain();
748
- if (domain.type !== "boardTarget" || !["space", "edge", "vertex", "tile"].includes(
749
- String(domain.targetKind)
750
- ))
751
- return [];
752
- return input.getEligibleTargets().filter(
753
- (id) => typeof id === "string" && !input.getTargetProps(id).disabled
754
- ).map(
755
- (id) => Object.freeze({
756
- kind: domain.targetKind,
757
- id,
758
- boardId: String(domain.boardId),
759
- interaction: candidate.key
760
- })
761
- );
762
- })
763
- );
764
- }
765
- function sameTarget(left, right) {
766
- return left.kind === right.kind && left.id === right.id && left.boardId === right.boardId && left.interaction === right.interaction;
767
- }
768
- const pointer = createPointerSession({
769
- move: ({ delta }) => {
770
- if (Math.hypot(delta.x, delta.y) >= 5) dragged = true;
771
- if (active) update({ ...active, offset: delta });
772
- },
773
- end({ delta }) {
774
- if (Math.hypot(delta.x, delta.y) >= 5) dragged = true;
775
- suppressPointerClick = true;
776
- const finished = active;
777
- update(null);
778
- if (!finished) return;
779
- if (dragged) {
780
- if (finished.target)
781
- context.routeCardDrop(finished.cardId, finished.target);
782
- } else context.routeTarget("card", finished.cardId, { interaction });
783
- },
784
- cancel: () => {
785
- suppressPointerClick = true;
786
- update(null);
787
- }
788
- });
789
- function snapshot() {
790
- const captured = active;
791
- const dropTargets = Object.freeze(targets());
792
- return Object.freeze({
793
- active: captured,
794
- getDropTargets: () => dropTargets,
795
- setDropTarget(target) {
796
- if (!active || disposed) return;
797
- if (target && !targets().some((candidate) => sameTarget(candidate, target)))
798
- return;
799
- update({ ...active, target });
800
- },
801
- cancel: () => pointer.cancel()
802
- });
803
- }
804
- const unsubscribe = game.subscribe(() => {
805
- const nextSource = game.getOptions().source;
806
- const nextSeat = game.snapshot?.me;
807
- if (nextSource !== source || nextSeat !== seat) {
808
- source = nextSource;
809
- seat = nextSeat;
810
- pointer.cancel();
811
- }
812
- });
813
- return {
814
- root: {
815
- get drag() {
816
- if (lastInteractions !== game.interactions || lastCards !== game.cards) {
817
- lastInteractions = game.interactions;
818
- lastCards = game.cards;
819
- branch = snapshot();
820
- }
821
- return branch;
822
- }
823
- },
824
- card: {
825
- getDragProps(options) {
826
- return {
827
- style: {
828
- touchAction: "none",
829
- transform: active?.cardId === this.id ? `translate(${active.offset.x}px, ${active.offset.y}px)` : void 0
830
- },
831
- "data-drag-card": this.id,
832
- onPointerDown: (event) => {
833
- if (disposed || game.cards.get(this.id) !== this || !this.getCanSelect())
834
- return;
835
- if (!pointer.start(event)) return;
836
- suppressPointerClick = false;
837
- dragged = false;
838
- interaction = options?.interaction;
839
- update({ cardId: this.id, offset: { x: 0, y: 0 }, target: null });
840
- },
841
- onClick: (event) => {
842
- if (disposed) return;
843
- if (event.detail !== 0 && suppressPointerClick) {
844
- suppressPointerClick = false;
845
- event.preventDefault();
846
- return;
847
- }
848
- this.select(options);
849
- },
850
- onPointerMove: pointer.move,
851
- onPointerUp: pointer.end,
852
- onPointerCancel: pointer.cancel,
853
- onLostPointerCapture: pointer.cancel
854
- };
855
- }
856
- },
857
- dispose() {
858
- unsubscribe();
859
- pointer.dispose();
860
- disposed = true;
861
- }
862
- };
863
- }
864
-
865
916
  // src/headless/features/pan-zoom.ts
866
917
  function panZoomFeature(game, context, options = {}) {
867
918
  const minScale = options.minScale ?? 0.25;