yuuna-engine 0.9.0 → 0.10.0
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 +1 -1
- package/lib/engine/types.d.ts +19 -1
- package/lib/index.cjs +38 -10
- package/lib/index.cjs.map +1 -1
- package/lib/index.js +38 -10
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -84,7 +84,7 @@ runEngine<GameState>({
|
|
|
84
84
|
`CIRCLE`, `TEXT`, `SPRITE`, `ANIMATED_SPRITE`, `LINE`, and `GROUP`. Give
|
|
85
85
|
one an `id` plus `isClickable`/`isHoverable` to make it interactive.
|
|
86
86
|
- **Events** — `nextState` receives one `GameEvent` per call: `TIME`,
|
|
87
|
-
`CLICK`, `HOVER_IN`, `HOVER_OUT`, `MOUSE_MOVE`, `MOUSE_LEAVE`,
|
|
87
|
+
`CLICK`, `RIGHT_CLICK`, `HOVER_IN`, `HOVER_OUT`, `MOUSE_MOVE`, `MOUSE_LEAVE`,
|
|
88
88
|
`MUSIC_END`, or a `CUSTOM` event of a type you define yourself, for
|
|
89
89
|
reporting things like an async `fetch()` resolving back into your
|
|
90
90
|
state machine.
|
package/lib/engine/types.d.ts
CHANGED
|
@@ -117,6 +117,18 @@ export type ClickEvent = {
|
|
|
117
117
|
y: number;
|
|
118
118
|
};
|
|
119
119
|
};
|
|
120
|
+
export type RightClickEvent = {
|
|
121
|
+
tag: "RIGHT_CLICK";
|
|
122
|
+
id?: string;
|
|
123
|
+
mouse: {
|
|
124
|
+
x: number;
|
|
125
|
+
y: number;
|
|
126
|
+
};
|
|
127
|
+
worldMouse: {
|
|
128
|
+
x: number;
|
|
129
|
+
y: number;
|
|
130
|
+
};
|
|
131
|
+
};
|
|
120
132
|
export type HoverInEvent = {
|
|
121
133
|
tag: "HOVER_IN";
|
|
122
134
|
id?: string;
|
|
@@ -178,7 +190,7 @@ export type FullscreenChangeEvent = {
|
|
|
178
190
|
tag: "FULLSCREEN_CHANGE";
|
|
179
191
|
isFullscreen: boolean;
|
|
180
192
|
};
|
|
181
|
-
export type GameEvent = TimeEvent | ClickEvent | HoverInEvent | HoverOutEvent | MouseMoveEvent | MouseLeaveEvent | TabBlurEvent | TabFocusEvent | MusicEndEvent | FullscreenChangeEvent;
|
|
193
|
+
export type GameEvent = TimeEvent | ClickEvent | RightClickEvent | HoverInEvent | HoverOutEvent | MouseMoveEvent | MouseLeaveEvent | TabBlurEvent | TabFocusEvent | MusicEndEvent | FullscreenChangeEvent;
|
|
182
194
|
export type CustomGameEvent<Custom> = {
|
|
183
195
|
tag: "CUSTOM";
|
|
184
196
|
event: Custom;
|
|
@@ -196,6 +208,11 @@ export type NextStateProps<State, Custom = never> = {
|
|
|
196
208
|
isJustPressed: boolean;
|
|
197
209
|
isJustReleased: boolean;
|
|
198
210
|
};
|
|
211
|
+
rightMouseButton: {
|
|
212
|
+
isPressed: boolean;
|
|
213
|
+
isJustPressed: boolean;
|
|
214
|
+
isJustReleased: boolean;
|
|
215
|
+
};
|
|
199
216
|
playSound: (id: string) => void;
|
|
200
217
|
playMusic: (id: string) => void;
|
|
201
218
|
pauseMusic: () => void;
|
|
@@ -240,6 +257,7 @@ export type RunEngineProps<State, Custom = never> = {
|
|
|
240
257
|
height?: number;
|
|
241
258
|
backgroundColor?: string;
|
|
242
259
|
resize?: "none" | "fit" | "stretch";
|
|
260
|
+
disableContextMenu?: boolean;
|
|
243
261
|
pixelRatio?: number | true;
|
|
244
262
|
};
|
|
245
263
|
camera?: (state: State) => {
|
package/lib/index.cjs
CHANGED
|
@@ -692,21 +692,33 @@ const runEngine = async (props) => {
|
|
|
692
692
|
// one tick rather than each recomputing props.timeScale(state)
|
|
693
693
|
// separately (state may itself have just changed this same tick).
|
|
694
694
|
let currentTimeScale = 1;
|
|
695
|
-
// Shared by the mouse "click"
|
|
696
|
-
// firing a CLICK is the same "is
|
|
697
|
-
// isClickable" check either way; only how
|
|
698
|
-
// differs (a real click event vs. a lifted
|
|
699
|
-
|
|
695
|
+
// Shared by the mouse "click"/"contextmenu" listeners and the touch
|
|
696
|
+
// handlers below — firing a CLICK (or RIGHT_CLICK) is the same "is
|
|
697
|
+
// whatever's currently hovered isClickable" check either way; only how
|
|
698
|
+
// `mouse` was determined differs (a real click event vs. a lifted
|
|
699
|
+
// finger), and which button it was.
|
|
700
|
+
const fireClick = (mouse, tag = "CLICK") => {
|
|
700
701
|
if (hoveredId === null)
|
|
701
702
|
return;
|
|
702
703
|
const { renderables, camera } = renderState(state);
|
|
703
704
|
const hovered = renderables.find((e) => e.id === hoveredId);
|
|
704
705
|
if (hovered !== undefined && hovered.isClickable) {
|
|
705
|
-
events.push({ tag
|
|
706
|
+
events.push({ tag, id: hovered.id, mouse, worldMouse: toWorldPosition(mouse, camera) });
|
|
706
707
|
}
|
|
707
708
|
};
|
|
708
709
|
const handleClick = (ev) => fireClick(getCanvasPosition(ev));
|
|
709
710
|
canvas.addEventListener("click", handleClick);
|
|
711
|
+
// contextmenu rather than mouseup with button 2 — it's the browser's
|
|
712
|
+
// own "secondary click" (Ctrl+click on macOS included), the same way
|
|
713
|
+
// "click" above is its primary one, and it's the event that has to be
|
|
714
|
+
// cancelled to keep the browser's menu from opening anyway.
|
|
715
|
+
const handleContextMenu = (ev) => {
|
|
716
|
+
if (props.canvas?.disableContextMenu ?? true) {
|
|
717
|
+
ev.preventDefault();
|
|
718
|
+
}
|
|
719
|
+
fireClick(getCanvasPosition(ev), "RIGHT_CLICK");
|
|
720
|
+
};
|
|
721
|
+
canvas.addEventListener("contextmenu", handleContextMenu);
|
|
710
722
|
const initialState = {
|
|
711
723
|
keyboardState: createRecord(keyboardKeys, () => false),
|
|
712
724
|
};
|
|
@@ -735,22 +747,30 @@ const runEngine = async (props) => {
|
|
|
735
747
|
}
|
|
736
748
|
};
|
|
737
749
|
canvas.addEventListener("keyup", handleKeyUp);
|
|
738
|
-
// mouseButton state (see NextStateProps.mouseButton)
|
|
739
|
-
// exactly like keyboardState above,
|
|
750
|
+
// mouseButton/rightMouseButton state (see NextStateProps.mouseButton)
|
|
751
|
+
// — double-buffers exactly like keyboardState above, one per button.
|
|
740
752
|
let previousMouseButtonState = { isPressed: false };
|
|
741
753
|
const currentMouseButtonState = { isPressed: false };
|
|
754
|
+
let previousRightMouseButtonState = { isPressed: false };
|
|
755
|
+
const currentRightMouseButtonState = { isPressed: false };
|
|
742
756
|
const handleMouseDown = (event) => {
|
|
743
|
-
//
|
|
744
|
-
//
|
|
757
|
+
// Primary and secondary buttons only — matching CLICK and
|
|
758
|
+
// RIGHT_CLICK respectively. Middle/back/forward stay ignored.
|
|
745
759
|
if (event.button === 0) {
|
|
746
760
|
currentMouseButtonState.isPressed = true;
|
|
747
761
|
}
|
|
762
|
+
else if (event.button === 2) {
|
|
763
|
+
currentRightMouseButtonState.isPressed = true;
|
|
764
|
+
}
|
|
748
765
|
};
|
|
749
766
|
canvas.addEventListener("mousedown", handleMouseDown);
|
|
750
767
|
const handleMouseUp = (event) => {
|
|
751
768
|
if (event.button === 0) {
|
|
752
769
|
currentMouseButtonState.isPressed = false;
|
|
753
770
|
}
|
|
771
|
+
else if (event.button === 2) {
|
|
772
|
+
currentRightMouseButtonState.isPressed = false;
|
|
773
|
+
}
|
|
754
774
|
};
|
|
755
775
|
// On window rather than the canvas — so releasing the button after
|
|
756
776
|
// having dragged off the canvas while still holding it down still
|
|
@@ -1048,12 +1068,18 @@ const runEngine = async (props) => {
|
|
|
1048
1068
|
isJustPressed: currentMouseButtonState.isPressed && !previousMouseButtonState.isPressed,
|
|
1049
1069
|
isJustReleased: !currentMouseButtonState.isPressed && previousMouseButtonState.isPressed,
|
|
1050
1070
|
};
|
|
1071
|
+
const rightMouseButton = {
|
|
1072
|
+
isPressed: currentRightMouseButtonState.isPressed,
|
|
1073
|
+
isJustPressed: currentRightMouseButtonState.isPressed && !previousRightMouseButtonState.isPressed,
|
|
1074
|
+
isJustReleased: !currentRightMouseButtonState.isPressed && previousRightMouseButtonState.isPressed,
|
|
1075
|
+
};
|
|
1051
1076
|
for (const nextState of nextStateFns) {
|
|
1052
1077
|
const result = nextState({
|
|
1053
1078
|
state,
|
|
1054
1079
|
event,
|
|
1055
1080
|
keyboard,
|
|
1056
1081
|
mouseButton,
|
|
1082
|
+
rightMouseButton,
|
|
1057
1083
|
playSound,
|
|
1058
1084
|
playMusic,
|
|
1059
1085
|
pauseMusic,
|
|
@@ -1185,6 +1211,7 @@ const runEngine = async (props) => {
|
|
|
1185
1211
|
lastFrame = now;
|
|
1186
1212
|
previousState.keyboardState = { ...currentState.keyboardState };
|
|
1187
1213
|
previousMouseButtonState = { ...currentMouseButtonState };
|
|
1214
|
+
previousRightMouseButtonState = { ...currentRightMouseButtonState };
|
|
1188
1215
|
}, 0);
|
|
1189
1216
|
resetCanvas = () => {
|
|
1190
1217
|
clearInterval(intervalId);
|
|
@@ -1206,6 +1233,7 @@ const runEngine = async (props) => {
|
|
|
1206
1233
|
// slow enough device or long enough playground session, that pile-up
|
|
1207
1234
|
// is what "clicks stop working" actually looks like.
|
|
1208
1235
|
canvas.removeEventListener("click", handleClick);
|
|
1236
|
+
canvas.removeEventListener("contextmenu", handleContextMenu);
|
|
1209
1237
|
canvas.removeEventListener("keydown", handleKeyDown);
|
|
1210
1238
|
canvas.removeEventListener("keyup", handleKeyUp);
|
|
1211
1239
|
canvas.removeEventListener("mousedown", handleMouseDown);
|
package/lib/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/lib/index.js
CHANGED
|
@@ -690,21 +690,33 @@ const runEngine = async (props) => {
|
|
|
690
690
|
// one tick rather than each recomputing props.timeScale(state)
|
|
691
691
|
// separately (state may itself have just changed this same tick).
|
|
692
692
|
let currentTimeScale = 1;
|
|
693
|
-
// Shared by the mouse "click"
|
|
694
|
-
// firing a CLICK is the same "is
|
|
695
|
-
// isClickable" check either way; only how
|
|
696
|
-
// differs (a real click event vs. a lifted
|
|
697
|
-
|
|
693
|
+
// Shared by the mouse "click"/"contextmenu" listeners and the touch
|
|
694
|
+
// handlers below — firing a CLICK (or RIGHT_CLICK) is the same "is
|
|
695
|
+
// whatever's currently hovered isClickable" check either way; only how
|
|
696
|
+
// `mouse` was determined differs (a real click event vs. a lifted
|
|
697
|
+
// finger), and which button it was.
|
|
698
|
+
const fireClick = (mouse, tag = "CLICK") => {
|
|
698
699
|
if (hoveredId === null)
|
|
699
700
|
return;
|
|
700
701
|
const { renderables, camera } = renderState(state);
|
|
701
702
|
const hovered = renderables.find((e) => e.id === hoveredId);
|
|
702
703
|
if (hovered !== undefined && hovered.isClickable) {
|
|
703
|
-
events.push({ tag
|
|
704
|
+
events.push({ tag, id: hovered.id, mouse, worldMouse: toWorldPosition(mouse, camera) });
|
|
704
705
|
}
|
|
705
706
|
};
|
|
706
707
|
const handleClick = (ev) => fireClick(getCanvasPosition(ev));
|
|
707
708
|
canvas.addEventListener("click", handleClick);
|
|
709
|
+
// contextmenu rather than mouseup with button 2 — it's the browser's
|
|
710
|
+
// own "secondary click" (Ctrl+click on macOS included), the same way
|
|
711
|
+
// "click" above is its primary one, and it's the event that has to be
|
|
712
|
+
// cancelled to keep the browser's menu from opening anyway.
|
|
713
|
+
const handleContextMenu = (ev) => {
|
|
714
|
+
if (props.canvas?.disableContextMenu ?? true) {
|
|
715
|
+
ev.preventDefault();
|
|
716
|
+
}
|
|
717
|
+
fireClick(getCanvasPosition(ev), "RIGHT_CLICK");
|
|
718
|
+
};
|
|
719
|
+
canvas.addEventListener("contextmenu", handleContextMenu);
|
|
708
720
|
const initialState = {
|
|
709
721
|
keyboardState: createRecord(keyboardKeys, () => false),
|
|
710
722
|
};
|
|
@@ -733,22 +745,30 @@ const runEngine = async (props) => {
|
|
|
733
745
|
}
|
|
734
746
|
};
|
|
735
747
|
canvas.addEventListener("keyup", handleKeyUp);
|
|
736
|
-
// mouseButton state (see NextStateProps.mouseButton)
|
|
737
|
-
// exactly like keyboardState above,
|
|
748
|
+
// mouseButton/rightMouseButton state (see NextStateProps.mouseButton)
|
|
749
|
+
// — double-buffers exactly like keyboardState above, one per button.
|
|
738
750
|
let previousMouseButtonState = { isPressed: false };
|
|
739
751
|
const currentMouseButtonState = { isPressed: false };
|
|
752
|
+
let previousRightMouseButtonState = { isPressed: false };
|
|
753
|
+
const currentRightMouseButtonState = { isPressed: false };
|
|
740
754
|
const handleMouseDown = (event) => {
|
|
741
|
-
//
|
|
742
|
-
//
|
|
755
|
+
// Primary and secondary buttons only — matching CLICK and
|
|
756
|
+
// RIGHT_CLICK respectively. Middle/back/forward stay ignored.
|
|
743
757
|
if (event.button === 0) {
|
|
744
758
|
currentMouseButtonState.isPressed = true;
|
|
745
759
|
}
|
|
760
|
+
else if (event.button === 2) {
|
|
761
|
+
currentRightMouseButtonState.isPressed = true;
|
|
762
|
+
}
|
|
746
763
|
};
|
|
747
764
|
canvas.addEventListener("mousedown", handleMouseDown);
|
|
748
765
|
const handleMouseUp = (event) => {
|
|
749
766
|
if (event.button === 0) {
|
|
750
767
|
currentMouseButtonState.isPressed = false;
|
|
751
768
|
}
|
|
769
|
+
else if (event.button === 2) {
|
|
770
|
+
currentRightMouseButtonState.isPressed = false;
|
|
771
|
+
}
|
|
752
772
|
};
|
|
753
773
|
// On window rather than the canvas — so releasing the button after
|
|
754
774
|
// having dragged off the canvas while still holding it down still
|
|
@@ -1046,12 +1066,18 @@ const runEngine = async (props) => {
|
|
|
1046
1066
|
isJustPressed: currentMouseButtonState.isPressed && !previousMouseButtonState.isPressed,
|
|
1047
1067
|
isJustReleased: !currentMouseButtonState.isPressed && previousMouseButtonState.isPressed,
|
|
1048
1068
|
};
|
|
1069
|
+
const rightMouseButton = {
|
|
1070
|
+
isPressed: currentRightMouseButtonState.isPressed,
|
|
1071
|
+
isJustPressed: currentRightMouseButtonState.isPressed && !previousRightMouseButtonState.isPressed,
|
|
1072
|
+
isJustReleased: !currentRightMouseButtonState.isPressed && previousRightMouseButtonState.isPressed,
|
|
1073
|
+
};
|
|
1049
1074
|
for (const nextState of nextStateFns) {
|
|
1050
1075
|
const result = nextState({
|
|
1051
1076
|
state,
|
|
1052
1077
|
event,
|
|
1053
1078
|
keyboard,
|
|
1054
1079
|
mouseButton,
|
|
1080
|
+
rightMouseButton,
|
|
1055
1081
|
playSound,
|
|
1056
1082
|
playMusic,
|
|
1057
1083
|
pauseMusic,
|
|
@@ -1183,6 +1209,7 @@ const runEngine = async (props) => {
|
|
|
1183
1209
|
lastFrame = now;
|
|
1184
1210
|
previousState.keyboardState = { ...currentState.keyboardState };
|
|
1185
1211
|
previousMouseButtonState = { ...currentMouseButtonState };
|
|
1212
|
+
previousRightMouseButtonState = { ...currentRightMouseButtonState };
|
|
1186
1213
|
}, 0);
|
|
1187
1214
|
resetCanvas = () => {
|
|
1188
1215
|
clearInterval(intervalId);
|
|
@@ -1204,6 +1231,7 @@ const runEngine = async (props) => {
|
|
|
1204
1231
|
// slow enough device or long enough playground session, that pile-up
|
|
1205
1232
|
// is what "clicks stop working" actually looks like.
|
|
1206
1233
|
canvas.removeEventListener("click", handleClick);
|
|
1234
|
+
canvas.removeEventListener("contextmenu", handleContextMenu);
|
|
1207
1235
|
canvas.removeEventListener("keydown", handleKeyDown);
|
|
1208
1236
|
canvas.removeEventListener("keyup", handleKeyUp);
|
|
1209
1237
|
canvas.removeEventListener("mousedown", handleMouseDown);
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/package.json
CHANGED