@simpludify/core 0.1.5 → 0.1.6

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.
@@ -1,11 +1,16 @@
1
1
  import type { ActionKind, ActionSet, ActionToken } from "./action-types.js";
2
+ import { type GamepadBinding } from "./gamepad-bindings.js";
2
3
  import { KeyCode } from "./key-code.js";
4
+ import { type MouseButton } from "./physical-input.js";
3
5
  export interface ButtonBinding {
4
6
  readonly keys?: readonly KeyCode[];
7
+ readonly mouse?: readonly MouseButton[];
5
8
  }
6
9
  export interface Axis1DBinding {
7
10
  readonly negative?: readonly KeyCode[];
8
11
  readonly positive?: readonly KeyCode[];
12
+ readonly mouseNegative?: readonly MouseButton[];
13
+ readonly mousePositive?: readonly MouseButton[];
9
14
  }
10
15
  export interface Axis2DBinding {
11
16
  readonly x?: Axis1DBinding;
@@ -14,16 +19,27 @@ export interface Axis2DBinding {
14
19
  export interface Axis3DBinding extends Axis2DBinding {
15
20
  readonly z?: Axis1DBinding;
16
21
  }
17
- export type ActionBinding<K extends ActionKind> = K extends "button" ? ButtonBinding : K extends "axis1d" ? Axis1DBinding : K extends "axis2d" ? Axis2DBinding : Axis3DBinding;
22
+ export type ActionBinding<K extends ActionKind> = (K extends "button" ? ButtonBinding : K extends "axis1d" ? Axis1DBinding : K extends "axis2d" ? Axis2DBinding : Axis3DBinding) & {
23
+ readonly gamepad?: GamepadBinding<K>;
24
+ };
18
25
  export type ActionBindings<A extends ActionSet> = {
19
26
  readonly [N in keyof A]?: ActionBinding<A[N]["kind"]>;
20
27
  };
21
- type Binding = ButtonBinding | Axis1DBinding | Axis2DBinding | Axis3DBinding;
28
+ type Binding = ActionBinding<ActionKind>;
22
29
  export declare function checkedBinding(action: ActionToken, value: unknown): Binding;
23
30
  export declare function keyLists(binding: Binding, path?: string): Array<{
24
31
  path: string;
25
32
  keys: readonly KeyCode[];
26
33
  }>;
34
+ export declare function mouseLists(binding: Binding, path?: string): Array<{
35
+ path: string;
36
+ buttons: readonly MouseButton[];
37
+ }>;
38
+ export declare function gamepadControls(binding: GamepadBinding<ActionKind>, path?: string): Array<{
39
+ kind: "button" | "axis";
40
+ index: number;
41
+ path: string;
42
+ }>;
27
43
  export declare function editBinding(current: Binding, patch: Binding, remove: boolean): Binding;
28
44
  /** Physical keyboard presets for any user-defined Axis2D Action. */
29
45
  export declare const KeyboardBindings: Readonly<{
@@ -10,11 +10,34 @@ import { type Scene } from "../scene/scene.js";
10
10
  import type { ActionInputPort, InputSystem } from "./input.js";
11
11
  import { OwnedInputSource } from "./input-source.js";
12
12
  import type { KeyCode } from "./key-code.js";
13
+ import type { MouseButton } from "./physical-input.js";
13
14
  type Token<A extends ActionSet> = A[keyof A];
14
15
  type ButtonToken<A extends ActionSet> = Extract<Token<A>, {
15
16
  kind: "button";
16
17
  }>;
17
18
  type Value = boolean | number | Axis2DValue | Axis3DValue;
19
+ type BindingUse<A extends ActionSet> = {
20
+ readonly action: Token<A>;
21
+ readonly path: string;
22
+ };
23
+ export interface KeyboardBindingConflict<A extends ActionSet> {
24
+ readonly key: KeyCode;
25
+ readonly bindings: readonly BindingUse<A>[];
26
+ }
27
+ export interface MouseBindingConflict<A extends ActionSet> {
28
+ readonly device: "mouse";
29
+ readonly button: MouseButton;
30
+ readonly bindings: readonly BindingUse<A>[];
31
+ }
32
+ export interface GamepadBindingConflict<A extends ActionSet> {
33
+ readonly device: "gamepad";
34
+ readonly kind: "button" | "axis";
35
+ readonly index: number;
36
+ readonly bindings: readonly BindingUse<A>[];
37
+ }
38
+ export type BindingConflict<A extends ActionSet> = (KeyboardBindingConflict<A> & {
39
+ readonly device: "keyboard";
40
+ }) | MouseBindingConflict<A> | GamepadBindingConflict<A>;
18
41
  export interface ButtonState {
19
42
  readonly held: boolean;
20
43
  readonly pressed: boolean;
@@ -44,6 +67,9 @@ export declare class ActionController<A extends ActionSet> implements LifetimeOw
44
67
  private readonly bus;
45
68
  private keyboardAllowed;
46
69
  private readonly blockedKeys;
70
+ private mouseAllowed;
71
+ private readonly blockedMouse;
72
+ private bindingGeneration;
47
73
  private pending;
48
74
  private transitions;
49
75
  constructor(actions: A, options: {
@@ -51,12 +77,17 @@ export declare class ActionController<A extends ActionSet> implements LifetimeOw
51
77
  input: InputSystem;
52
78
  bindings?: ActionBindings<A>;
53
79
  keyboard?: boolean;
80
+ mouse?: boolean;
54
81
  });
55
82
  get disposed(): boolean;
56
83
  get active(): boolean;
57
84
  get keyboardEnabled(): boolean;
58
85
  set keyboardEnabled(value: boolean);
59
86
  get sourceCount(): number;
87
+ get mouseEnabled(): boolean;
88
+ set mouseEnabled(value: boolean);
89
+ /** @internal Used by existing host adapters to follow common binding settings. */
90
+ get bindingRevision(): number;
60
91
  onDispose(callback: () => void): () => void;
61
92
  private entry;
62
93
  value<T extends Token<A>>(action: T): ActionValue<T["kind"]>;
@@ -70,28 +101,29 @@ export declare class ActionController<A extends ActionSet> implements LifetimeOw
70
101
  snapshot(): ActionSnapshot<A>;
71
102
  onPressed(action: ButtonToken<A>, handler: () => void): () => void;
72
103
  createSource(owner?: LifetimeOwner): ActionSource<A>;
73
- /** @internal */ write(source: ActionSource<A>, action: ActionToken, value: Value): void;
104
+ /** @internal */ write(source: ActionSource<A>, action: ActionToken, value: Value, edges?: boolean): void;
74
105
  /** @internal */ clearSource(source: ActionSource<A>, remove: boolean): void;
75
106
  private keyboard;
76
107
  private recompute;
77
108
  /** @internal */ keyChanged(): void;
78
109
  /** @internal */ accepts(code: KeyCode): boolean;
110
+ /** @internal */ acceptsMouse(button: MouseButton): boolean;
79
111
  /** @internal */ capture(override?: ActionFrame): void;
80
112
  /** @internal */ dispatch(): void;
81
113
  /** Clears held contributions and unconsumed edges; no synthetic button release. */
82
114
  reset(): void;
115
+ /** @internal Flush device transitions without destroying explicit program values. */
116
+ resetPhysical(): void;
83
117
  getBinding<T extends Token<A>>(action: T): ActionBinding<T["kind"]>;
84
118
  rebind<T extends Token<A>>(action: T, binding: NoInfer<ActionBinding<T["kind"]>>): void;
85
119
  addBindings<T extends Token<A>>(action: T, binding: NoInfer<ActionBinding<T["kind"]>>): void;
86
120
  removeBindings<T extends Token<A>>(action: T, binding: NoInfer<ActionBinding<T["kind"]>>): void;
87
121
  resetBindings(action?: Token<A>): void;
88
- conflicts(): ReadonlyArray<{
89
- readonly key: KeyCode;
90
- readonly bindings: ReadonlyArray<{
91
- readonly action: Token<A>;
92
- readonly path: string;
93
- }>;
94
- }>;
122
+ conflicts(): readonly KeyboardBindingConflict<A>[];
123
+ conflicts(device: "keyboard"): readonly KeyboardBindingConflict<A>[];
124
+ conflicts(device: "mouse"): readonly MouseBindingConflict<A>[];
125
+ conflicts(device: "gamepad"): readonly GamepadBindingConflict<A>[];
126
+ conflicts(device: "all"): readonly BindingConflict<A>[];
95
127
  exportBindings(): {
96
128
  version: 1;
97
129
  bindings: ActionBindings<A>;
@@ -110,5 +142,7 @@ export declare class ActionSource<A extends ActionSet> extends OwnedInputSource
110
142
  private readonly controller;
111
143
  /** @internal */ constructor(controller: ActionController<A>);
112
144
  set<T extends Token<A>>(action: T, value: NoInfer<ActionInputValue<T["kind"]>>): void;
145
+ /** @internal Reset one device binding without synthesizing logical edges. */
146
+ resetBinding(action: Token<A>): void;
113
147
  protected release(remove: boolean): void;
114
148
  }
@@ -0,0 +1,100 @@
1
+ import type { ActionKind, ActionSet, ActionToken } from "./action-types.js";
2
+ /** Standard mapping physical positions, not product-specific printed labels. */
3
+ export declare const GamepadButton: Readonly<{
4
+ South: 0;
5
+ East: 1;
6
+ West: 2;
7
+ North: 3;
8
+ LeftShoulder: 4;
9
+ RightShoulder: 5;
10
+ LeftTrigger: 6;
11
+ RightTrigger: 7;
12
+ Select: 8;
13
+ Start: 9;
14
+ LeftStick: 10;
15
+ RightStick: 11;
16
+ DPadUp: 12;
17
+ DPadDown: 13;
18
+ DPadLeft: 14;
19
+ DPadRight: 15;
20
+ Home: 16;
21
+ }>;
22
+ export type GamepadButton = (typeof GamepadButton)[keyof typeof GamepadButton];
23
+ export declare const GamepadAxis: Readonly<{
24
+ LeftX: 0;
25
+ LeftY: 1;
26
+ RightX: 2;
27
+ RightY: 3;
28
+ }>;
29
+ export type GamepadAxis = (typeof GamepadAxis)[keyof typeof GamepadAxis];
30
+ export interface GamepadButtonBinding {
31
+ readonly buttons?: readonly (number | {
32
+ readonly button: number;
33
+ readonly threshold?: number;
34
+ })[];
35
+ }
36
+ export interface GamepadAxisBinding {
37
+ readonly axis?: number;
38
+ readonly invert?: boolean;
39
+ readonly negative?: readonly number[];
40
+ readonly positive?: readonly number[];
41
+ readonly deadZone?: number;
42
+ }
43
+ export interface GamepadVectorBinding {
44
+ readonly x?: GamepadAxisBinding;
45
+ readonly y?: GamepadAxisBinding;
46
+ }
47
+ export interface GamepadVector3Binding extends GamepadVectorBinding {
48
+ readonly z?: GamepadAxisBinding;
49
+ }
50
+ export type GamepadBinding<K extends ActionKind> = K extends "button" ? GamepadButtonBinding : K extends "axis1d" ? GamepadAxisBinding : K extends "axis2d" ? GamepadVectorBinding : GamepadVector3Binding;
51
+ export type GamepadBindings<A extends ActionSet> = {
52
+ readonly [N in keyof A]?: GamepadBinding<A[N]["kind"]>;
53
+ };
54
+ /** Ready-to-use data; game Actions retain application-defined names. */
55
+ export declare const GamepadBindings: Readonly<{
56
+ LeftStick: Readonly<{
57
+ x: Readonly<{
58
+ axis: number;
59
+ }>;
60
+ y: Readonly<{
61
+ axis: number;
62
+ }>;
63
+ }>;
64
+ RightStick: Readonly<{
65
+ x: Readonly<{
66
+ axis: number;
67
+ }>;
68
+ y: Readonly<{
69
+ axis: number;
70
+ }>;
71
+ }>;
72
+ DPad: Readonly<{
73
+ x: Readonly<{
74
+ negative: readonly number[];
75
+ positive: readonly number[];
76
+ }>;
77
+ y: Readonly<{
78
+ negative: readonly number[];
79
+ positive: readonly number[];
80
+ }>;
81
+ }>;
82
+ Move: Readonly<{
83
+ x: Readonly<{
84
+ axis: 0;
85
+ negative: readonly number[];
86
+ positive: readonly number[];
87
+ }>;
88
+ y: Readonly<{
89
+ axis: 1;
90
+ negative: readonly number[];
91
+ positive: readonly number[];
92
+ }>;
93
+ }>;
94
+ }>;
95
+ type Binding = GamepadButtonBinding | GamepadAxisBinding | GamepadVector3Binding;
96
+ export type CheckedGamepadBinding = readonly [ActionToken, Binding];
97
+ export declare function gamepadIndex(value: unknown): number;
98
+ export declare function gamepadDeadZone(value: unknown): number;
99
+ export declare function checkedGamepadBindings(actions: ActionSet, value: unknown): readonly CheckedGamepadBinding[];
100
+ export {};
@@ -0,0 +1,38 @@
1
+ import { type LifetimeOwner } from "../foundation/lifetime.js";
2
+ import type { ActionBinding } from "./action-bindings.js";
3
+ import { InputDevice, type PhysicalInput } from "./physical-input.js";
4
+ export declare const InputCaptureState: Readonly<{
5
+ readonly Arming: "arming";
6
+ readonly Listening: "listening";
7
+ readonly Completed: "completed";
8
+ readonly Cancelled: "cancelled";
9
+ }>;
10
+ export type InputCaptureState = (typeof InputCaptureState)[keyof typeof InputCaptureState];
11
+ export interface InputCaptureOptions {
12
+ readonly devices?: readonly InputDevice[];
13
+ readonly gamepadIndex?: number;
14
+ readonly owner?: LifetimeOwner;
15
+ }
16
+ export type CapturedInput = PhysicalInput & {
17
+ readonly binding: ActionBinding<"button">;
18
+ };
19
+ /** A portable single-input session. Hosts supply neutral arming and physical events. */
20
+ export declare class InputCapture implements LifetimeOwner {
21
+ private readonly life;
22
+ private status;
23
+ readonly devices: readonly InputDevice[];
24
+ readonly gamepadIndex: number | undefined;
25
+ readonly result: Promise<CapturedInput | undefined>;
26
+ private settle;
27
+ constructor(options?: InputCaptureOptions);
28
+ get state(): InputCaptureState;
29
+ get disposed(): boolean;
30
+ onDispose(callback: () => void): () => void;
31
+ /** @internal Hosts call only after all eligible preheld buttons/keys are neutral. */
32
+ arm(): void;
33
+ /** @internal Offer one fresh physical down, never a repeat or opening gesture. */
34
+ accept(input: PhysicalInput): boolean;
35
+ private finish;
36
+ cancel(): void;
37
+ dispose(): void;
38
+ }
@@ -2,6 +2,7 @@ import { type LifetimeOwner } from "../foundation/lifetime.js";
2
2
  import type { ActionFrame } from "./action-frame.js";
3
3
  import { OwnedInputSource } from "./input-source.js";
4
4
  import { type KeyCode } from "./key-code.js";
5
+ import { type MouseButton } from "./physical-input.js";
5
6
  /** Host device adapter. Sampled before all Action frames; reset flushes held device state. */
6
7
  export interface InputAdapter {
7
8
  sample(): void;
@@ -13,10 +14,12 @@ export interface ActionInputPort {
13
14
  readonly active: boolean;
14
15
  readonly sourceCount: number;
15
16
  accepts(code: KeyCode): boolean;
17
+ acceptsMouse(button: MouseButton): boolean;
16
18
  keyChanged(): void;
17
19
  capture(frame?: ActionFrame): void;
18
20
  dispatch(): void;
19
21
  reset(): void;
22
+ resetPhysical(): void;
20
23
  dispose(): void;
21
24
  }
22
25
  /** A keyboard device contribution, not a game-facing state reader.
@@ -27,11 +30,20 @@ export declare class KeyboardSource extends OwnedInputSource {
27
30
  set(code: KeyCode, pressed: boolean): void;
28
31
  protected release(remove: boolean): void;
29
32
  }
33
+ /** Physical mouse contribution; game state is read through ActionController. */
34
+ export declare class MouseSource extends OwnedInputSource {
35
+ private readonly input;
36
+ /** @internal */ constructor(input: InputSystem);
37
+ set(button: MouseButton, pressed: boolean): void;
38
+ protected release(remove: boolean): void;
39
+ }
30
40
  /** Engine-owned routing and update boundary. No raw game-state/alternate movement API. */
31
41
  export declare class InputSystem {
32
42
  private readonly actions;
33
43
  private readonly adapters;
34
44
  private readonly keyboards;
45
+ private readonly mice;
46
+ private captureCancel;
35
47
  private dead;
36
48
  private playback;
37
49
  private frames;
@@ -42,12 +54,17 @@ export declare class InputSystem {
42
54
  /** Host integration boundary; ordinary game input uses ActionSource instead. */
43
55
  addAdapter(adapter: InputAdapter): () => void;
44
56
  acceptsKey(code: KeyCode): boolean;
57
+ acceptsMouse(button: MouseButton): boolean;
58
+ /** @internal */ mouseDown(button: MouseButton): boolean;
45
59
  private notify;
46
60
  /** @internal Device state is resolved only through Action bindings. */
47
61
  keyDown(code: KeyCode): boolean;
48
62
  /** @internal Freeze every controller before invoking any callbacks. */
49
63
  beginStep(captured?: () => void): void;
50
64
  createKeyboardSource(owner?: LifetimeOwner): KeyboardSource;
65
+ createMouseSource(owner?: LifetimeOwner): MouseSource;
66
+ /** @internal */ writeMouse(source: MouseSource, button: MouseButton, pressed: boolean): void;
67
+ /** @internal */ releaseMouse(source: MouseSource, remove: boolean): void;
51
68
  /** @internal */ writeKey(source: KeyboardSource, code: KeyCode, pressed: boolean): void;
52
69
  /** @internal */ releaseKeyboard(source: KeyboardSource, remove: boolean): void;
53
70
  /** @internal */ hasController(controller: object): boolean;
@@ -56,6 +73,9 @@ export declare class InputSystem {
56
73
  /** @internal */ lockPlayback(): void;
57
74
  /** @internal */ playbackFrames(frames: ReadonlyMap<object, ActionFrame>): void;
58
75
  /** @internal */ unlockPlayback(): void;
76
+ /** @internal Host capture gate. Program ActionSource values remain explicit. */
77
+ suspendLiveInput(cancel: () => void): () => void;
78
+ private resetPhysical;
59
79
  clear(): void;
60
80
  /** Device diagnostics only; game state is ActionController.snapshot(). */
61
81
  snapshot(): readonly KeyCode[];
@@ -0,0 +1,32 @@
1
+ import type { KeyCode } from "./key-code.js";
2
+ export declare const InputDevice: Readonly<{
3
+ readonly Keyboard: "keyboard";
4
+ readonly Mouse: "mouse";
5
+ readonly Gamepad: "gamepad";
6
+ }>;
7
+ export type InputDevice = (typeof InputDevice)[keyof typeof InputDevice];
8
+ /** Standard PointerEvent.button indices; these are not the buttons bit mask. */
9
+ export declare const MouseButton: Readonly<{
10
+ readonly Left: 0;
11
+ readonly Middle: 1;
12
+ readonly Right: 2;
13
+ readonly Back: 3;
14
+ readonly Forward: 4;
15
+ }>;
16
+ export type MouseButton = (typeof MouseButton)[keyof typeof MouseButton];
17
+ export declare function isMouseButton(value: unknown): value is MouseButton;
18
+ export declare function parseMouseButton(value: unknown): MouseButton;
19
+ /** One physical digital input, independent of an Action's gameplay meaning. */
20
+ export type PhysicalInput = {
21
+ readonly device: "keyboard";
22
+ readonly code: KeyCode;
23
+ } | {
24
+ readonly device: "mouse";
25
+ readonly button: MouseButton;
26
+ } | {
27
+ readonly device: "gamepad";
28
+ readonly index: number;
29
+ readonly id: string;
30
+ readonly mapping: string;
31
+ readonly button: number;
32
+ };
@@ -79,6 +79,8 @@ export declare class PointerRouting {
79
79
  hasHover(id: number): boolean;
80
80
  has(id: number): boolean;
81
81
  owns(id: number): boolean;
82
+ /** @internal Mouse bindings observe world ownership without claiming a second gesture. */
83
+ worldOwns(id: number): boolean;
82
84
  move(data: PointerInfo): void;
83
85
  private sampleVelocity;
84
86
  private claimScroll;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simpludify/core",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {