@antha/gamepad-type 0.1.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.
Files changed (66) hide show
  1. package/LICENSE-CC0 +121 -0
  2. package/LICENSE-MIT +21 -0
  3. package/README.md +102 -0
  4. package/dist/create.d.ts +10 -0
  5. package/dist/create.js +20 -0
  6. package/dist/default-layouts.d.ts +8 -0
  7. package/dist/default-layouts.js +339 -0
  8. package/dist/default-models.d.ts +14 -0
  9. package/dist/default-models.js +46 -0
  10. package/dist/demo/elements/gamepad/vir-gamepad-axe.element.d.ts +7 -0
  11. package/dist/demo/elements/gamepad/vir-gamepad-axe.element.js +61 -0
  12. package/dist/demo/elements/gamepad/vir-gamepad-button.element.d.ts +7 -0
  13. package/dist/demo/elements/gamepad/vir-gamepad-button.element.js +87 -0
  14. package/dist/demo/elements/gamepad/vir-gamepad-input.element.d.ts +7 -0
  15. package/dist/demo/elements/gamepad/vir-gamepad-input.element.js +38 -0
  16. package/dist/demo/elements/gamepad/vir-gamepad-inputs.element.d.ts +20 -0
  17. package/dist/demo/elements/gamepad/vir-gamepad-inputs.element.js +110 -0
  18. package/dist/demo/elements/gamepad/vir-gamepad-name.element.d.ts +11 -0
  19. package/dist/demo/elements/gamepad/vir-gamepad-name.element.js +158 -0
  20. package/dist/demo/elements/gamepad/vir-gamepad.element.d.ts +8 -0
  21. package/dist/demo/elements/gamepad/vir-gamepad.element.js +41 -0
  22. package/dist/demo/elements/modals/vir-create-new-type.modal.element.d.ts +9 -0
  23. package/dist/demo/elements/modals/vir-create-new-type.modal.element.js +135 -0
  24. package/dist/demo/elements/modals/vir-edit-mappings.modal.element.d.ts +29 -0
  25. package/dist/demo/elements/modals/vir-edit-mappings.modal.element.js +185 -0
  26. package/dist/demo/elements/modals/vir-modal.element.d.ts +3 -0
  27. package/dist/demo/elements/modals/vir-modal.element.js +88 -0
  28. package/dist/demo/elements/vir-demo.element.d.ts +27 -0
  29. package/dist/demo/elements/vir-demo.element.js +469 -0
  30. package/dist/demo/events/modal-close.event.d.ts +1 -0
  31. package/dist/demo/events/modal-close.event.js +2 -0
  32. package/dist/demo/events/selected-gamepad-index-change.event.d.ts +1 -0
  33. package/dist/demo/events/selected-gamepad-index-change.event.js +2 -0
  34. package/dist/demo/gamepad-brand-color.d.ts +1 -0
  35. package/dist/demo/gamepad-brand-color.js +14 -0
  36. package/dist/demo/global-vars.d.ts +6 -0
  37. package/dist/demo/global-vars.js +3 -0
  38. package/dist/demo/overrides.d.ts +7 -0
  39. package/dist/demo/overrides.js +1 -0
  40. package/dist/demo/sentry-setup.d.ts +1 -0
  41. package/dist/demo/sentry-setup.js +11 -0
  42. package/dist/demo/util/casing.d.ts +1 -0
  43. package/dist/demo/util/casing.js +7 -0
  44. package/dist/demo/util/check-changes.d.ts +6 -0
  45. package/dist/demo/util/check-changes.js +28 -0
  46. package/dist/demo/util/local-save-data.d.ts +9 -0
  47. package/dist/demo/util/local-save-data.js +36 -0
  48. package/dist/demo/util/user-agent.d.ts +6 -0
  49. package/dist/demo/util/user-agent.js +2 -0
  50. package/dist/demo/util/window-listener.d.ts +1 -0
  51. package/dist/demo/util/window-listener.js +6 -0
  52. package/dist/find-matches.d.ts +42 -0
  53. package/dist/find-matches.js +77 -0
  54. package/dist/gamepad-layout.d.ts +29 -0
  55. package/dist/gamepad-layout.js +1 -0
  56. package/dist/gamepad-model.d.ts +67 -0
  57. package/dist/gamepad-model.js +69 -0
  58. package/dist/index.d.ts +8 -0
  59. package/dist/index.js +8 -0
  60. package/dist/known-input.d.ts +126 -0
  61. package/dist/known-input.js +128 -0
  62. package/dist/system-versions.d.ts +25 -0
  63. package/dist/system-versions.js +26 -0
  64. package/dist/util/lowercase-keys.d.ts +2 -0
  65. package/dist/util/lowercase-keys.js +10 -0
  66. package/package.json +67 -0
@@ -0,0 +1,469 @@
1
+ import { copyThroughJson, makeWritable, pickObjectKeys } from '@augment-vir/common';
2
+ import { asyncProp, css, defineElement, html, listen } from 'element-vir';
3
+ import { CurrentInputsChangedEvent, InputDeviceHandler, InputDeviceType, } from 'input-device-handler';
4
+ import { sendLog } from 'sentry-vir';
5
+ import { LoaderAnimated24Icon, ViraButton, ViraColorVariant, ViraEmphasis, ViraIcon, noNativeSpacing, } from 'vira';
6
+ import { createEmptyGamepadLayout, findMatchingGamepadLayout, getSystemVersions, includesSystemVersion, } from '../../index.js';
7
+ import { ModalClose } from '../events/modal-close.event.js';
8
+ import { SelectedGamepadIndexChange } from '../events/selected-gamepad-index-change.event.js';
9
+ import { setupSentry } from '../sentry-setup.js';
10
+ import { extractNewChanges } from '../util/check-changes.js';
11
+ import { loadSavedLayouts, loadSavedModelMap, loadSubmittedChanges, resetAllSavedData, saveLayouts, saveModelMap, savedSubmittedChanges, } from '../util/local-save-data.js';
12
+ import { userAgent } from '../util/user-agent.js';
13
+ import { addWindowListener } from '../util/window-listener.js';
14
+ import { VirGamepadInputs } from './gamepad/vir-gamepad-inputs.element.js';
15
+ import { VirGamepadName } from './gamepad/vir-gamepad-name.element.js';
16
+ import { VirGamepad } from './gamepad/vir-gamepad.element.js';
17
+ import { VirCreateNewTypeModal } from './modals/vir-create-new-type.modal.element.js';
18
+ import { VirEditMappingsModal } from './modals/vir-edit-mappings.modal.element.js';
19
+ void setupSentry();
20
+ var EditMode;
21
+ (function (EditMode) {
22
+ EditMode["None"] = "none";
23
+ EditMode["Mappings"] = "mappings";
24
+ EditMode["CreateType"] = "create-type";
25
+ })(EditMode || (EditMode = {}));
26
+ export const VirDemo = defineElement()({
27
+ tagName: 'vir-demo',
28
+ styles: css `
29
+ :host {
30
+ box-sizing: border-box;
31
+ width: 100%;
32
+ height: 100%;
33
+ font-family: sans-serif;
34
+ }
35
+
36
+ main {
37
+ box-sizing: border-box;
38
+ width: 100%;
39
+ min-height: 100%;
40
+ display: flex;
41
+ flex-direction: column;
42
+ padding: 24px;
43
+ gap: 24px;
44
+ }
45
+
46
+ p {
47
+ margin: 0;
48
+ }
49
+
50
+ .description {
51
+ font-size: 0.5em;
52
+ opacity: 0.3;
53
+ transition: 100ms;
54
+ }
55
+
56
+ .description:hover {
57
+ opacity: 1;
58
+ }
59
+
60
+ .modal-wrapper {
61
+ position: absolute;
62
+ }
63
+
64
+ .gamepads-wrapper {
65
+ display: flex;
66
+ flex-direction: column;
67
+ gap: 64px;
68
+ }
69
+
70
+ ${ViraIcon} {
71
+ display: flex;
72
+ height: 100%;
73
+ width: 100%;
74
+ justify-content: center;
75
+ align-items: center;
76
+ transform: scale(2);
77
+ }
78
+
79
+ header {
80
+ display: flex;
81
+ flex-direction: column;
82
+ gap: 24px;
83
+ }
84
+
85
+ header .column {
86
+ display: flex;
87
+ flex-direction: column;
88
+ gap: 8px;
89
+ }
90
+
91
+ h1 {
92
+ ${noNativeSpacing};
93
+ }
94
+
95
+ header .row {
96
+ display: flex;
97
+ align-items: flex-start;
98
+ justify-content: space-between;
99
+ }
100
+
101
+ .submission-buttons {
102
+ display: flex;
103
+ gap: 16px;
104
+ align-items: flex-start;
105
+ }
106
+
107
+ .submission-buttons .subtitle {
108
+ opacity: 0.3;
109
+ font-size: 0.5em;
110
+ transition: 100ms;
111
+ }
112
+
113
+ .user-agent {
114
+ opacity: 0.5;
115
+ font-size: 0.7em;
116
+ transition: 100ms;
117
+ }
118
+
119
+ .user-agent:hover,
120
+ .submission-buttons .subtitle:hover {
121
+ opacity: 1;
122
+ }
123
+
124
+ .error {
125
+ font-weight: bold
126
+ color: red;
127
+ }
128
+ `,
129
+ state() {
130
+ return {
131
+ deviceHandler: new InputDeviceHandler({
132
+ startLoopImmediately: false,
133
+ disableMouseMovement: true,
134
+ globalDeadZone: 0.1,
135
+ }),
136
+ gamepadDevices: [],
137
+ newInputs: {
138
+ timestamp: 0,
139
+ devices: [],
140
+ },
141
+ gamepadIndexForEditing: 0,
142
+ inputForEditing: undefined,
143
+ savedGamepadLayouts: asyncProp({
144
+ defaultValue: loadSavedLayouts().then((result) => {
145
+ console.info('Loaded layouts:');
146
+ console.info(JSON.stringify(result));
147
+ return result;
148
+ }),
149
+ }),
150
+ submittedChanges: asyncProp({
151
+ defaultValue: loadSubmittedChanges(),
152
+ }),
153
+ savedGamepadModelMap: asyncProp({
154
+ defaultValue: loadSavedModelMap().then((result) => {
155
+ console.info('Loaded model map:');
156
+ console.info(JSON.stringify(result));
157
+ return result;
158
+ }),
159
+ }),
160
+ overrides: {
161
+ gamepadLayouts: [],
162
+ gamepadTypes: {},
163
+ },
164
+ editMode: EditMode.None,
165
+ cleanup: undefined,
166
+ };
167
+ },
168
+ init({ state, updateState }) {
169
+ const cleanupCallbacks = [
170
+ state.deviceHandler.listen(CurrentInputsChangedEvent, () => {
171
+ const allDevices = state.deviceHandler.getLastPollResults();
172
+ updateState({
173
+ gamepadDevices: Object.values(allDevices).filter((device) => {
174
+ return device.deviceType === InputDeviceType.Gamepad;
175
+ }),
176
+ });
177
+ }),
178
+ state.deviceHandler.listen(CurrentInputsChangedEvent, (event) => {
179
+ const now = Date.now();
180
+ const devicesWithNewInputs = event.detail.inputs.newInputs.reduce((accum, newInput) => {
181
+ accum.add(newInput.deviceKey);
182
+ return accum;
183
+ }, new Set());
184
+ if (devicesWithNewInputs.size) {
185
+ updateState({
186
+ newInputs: {
187
+ timestamp: now,
188
+ devices: Array.from(devicesWithNewInputs),
189
+ },
190
+ });
191
+ }
192
+ }),
193
+ addWindowListener('keydown', (event) => {
194
+ if (event.code.toLowerCase() === 'escape' && state.editMode !== EditMode.None) {
195
+ updateState({
196
+ editMode: EditMode.None,
197
+ });
198
+ }
199
+ }),
200
+ ];
201
+ updateState({
202
+ cleanup() {
203
+ cleanupCallbacks.forEach((cleanupCallback) => cleanupCallback());
204
+ },
205
+ });
206
+ state.deviceHandler.startPollingLoop();
207
+ },
208
+ cleanup({ state, updateState }) {
209
+ state.cleanup?.();
210
+ updateState({
211
+ cleanup: undefined,
212
+ });
213
+ },
214
+ render({ state, updateState }) {
215
+ if (state.savedGamepadLayouts.isWaiting() ||
216
+ state.savedGamepadModelMap.isWaiting() ||
217
+ state.submittedChanges.isWaiting()) {
218
+ return html `
219
+ <${ViraIcon.assign({
220
+ icon: LoaderAnimated24Icon,
221
+ })}></${ViraIcon}>
222
+ `;
223
+ }
224
+ else if (!state.savedGamepadLayouts.isResolved() ||
225
+ !state.savedGamepadModelMap.isResolved() ||
226
+ !state.submittedChanges.isResolved()) {
227
+ return html `
228
+ <p class="error">Failed to initialize.</p>
229
+ `;
230
+ }
231
+ const savedLayouts = makeWritable(state.savedGamepadLayouts.value);
232
+ const savedModelMap = state.savedGamepadModelMap.value;
233
+ const submittedChanges = state.submittedChanges.value;
234
+ const notSubmittedChanges = extractNewChanges({
235
+ layouts: savedLayouts,
236
+ models: savedModelMap,
237
+ }, submittedChanges);
238
+ const changes = extractNewChanges({
239
+ layouts: savedLayouts,
240
+ models: savedModelMap,
241
+ }, undefined);
242
+ const hasAnyChanges = changes.layouts.length || Object.values(changes.models).length;
243
+ const hasNotSubmitted = notSubmittedChanges.layouts.length || Object.values(notSubmittedChanges.models).length;
244
+ const gamepadDevices = [
245
+ ...state.gamepadDevices,
246
+ ];
247
+ const gamepadTemplates = gamepadDevices.map((gamepad, gamepadIndex) => {
248
+ const gamepadLayout = findMatchingGamepadLayout({
249
+ gamepad,
250
+ layouts: savedLayouts,
251
+ gamepadModelMap: savedModelMap,
252
+ });
253
+ return html `
254
+ <${VirGamepad.assign({
255
+ gamepad,
256
+ gamepadIndex,
257
+ gamepadLayout,
258
+ gamepadModelMap: savedModelMap,
259
+ })}
260
+ ${listen(VirGamepadName.events.editMappingsClick, () => {
261
+ updateState({
262
+ gamepadIndexForEditing: gamepadIndex,
263
+ editMode: EditMode.Mappings,
264
+ });
265
+ })}
266
+ ${listen(VirGamepadName.events.gamepadModelOverride, async (event) => {
267
+ const newModelMap = {
268
+ ...savedModelMap,
269
+ [gamepad.deviceName.toLowerCase()]: event.detail,
270
+ };
271
+ state.savedGamepadModelMap.setValue(newModelMap);
272
+ await saveModelMap(newModelMap);
273
+ })}
274
+ ${listen(VirGamepadInputs.events.gamepadInputClick, (event) => {
275
+ updateState({
276
+ editMode: EditMode.Mappings,
277
+ gamepadIndexForEditing: gamepadIndex,
278
+ inputForEditing: event.detail,
279
+ });
280
+ })}
281
+ ${listen(VirGamepadName.events.createNewModel, () => {
282
+ updateState({
283
+ editMode: EditMode.CreateType,
284
+ gamepadIndexForEditing: gamepadIndex,
285
+ });
286
+ })}
287
+ ></${VirGamepad}>
288
+ `;
289
+ });
290
+ const currentModalTemplate = state.editMode === EditMode.CreateType
291
+ ? html `
292
+ <${VirCreateNewTypeModal.assign({
293
+ gamepadDevices,
294
+ selectedGamepadIndex: state.gamepadIndexForEditing,
295
+ })}
296
+ ${listen(VirCreateNewTypeModal.events.newModelCreate, async (event) => {
297
+ const selectedGamepad = state.gamepadDevices[state.gamepadIndexForEditing];
298
+ if (!selectedGamepad) {
299
+ throw new Error('Cannot update layouts, failed to find selected device');
300
+ }
301
+ const newModelMap = {
302
+ ...savedModelMap,
303
+ [selectedGamepad.deviceName.toLowerCase()]: event.detail,
304
+ };
305
+ console.info('Saving updated model map:');
306
+ console.info(JSON.stringify(newModelMap));
307
+ state.savedGamepadModelMap.setValue(newModelMap);
308
+ await saveModelMap(newModelMap);
309
+ })}
310
+ ></${VirCreateNewTypeModal}>
311
+ `
312
+ : state.editMode === EditMode.Mappings
313
+ ? html `
314
+ <${VirEditMappingsModal.assign({
315
+ savedGamepadLayouts: savedLayouts,
316
+ gamepadDevices,
317
+ selectedGamepadIndex: state.gamepadIndexForEditing,
318
+ savedGamepadModelMap: savedModelMap,
319
+ selectedGamepadInput: state.inputForEditing,
320
+ inputDeviceHandler: state.deviceHandler,
321
+ })}
322
+ ${listen(VirEditMappingsModal.events.selectedGamepadInputChange, (event) => {
323
+ updateState({
324
+ inputForEditing: event.detail,
325
+ });
326
+ })}
327
+ ${listen(VirEditMappingsModal.events.inputMapSave, async (event) => {
328
+ if (savedLayouts instanceof Error) {
329
+ throw new TypeError('Cannot save mapping, failed to load current mappings.');
330
+ }
331
+ const selectedGamepad = state.gamepadDevices[state.gamepadIndexForEditing];
332
+ if (!selectedGamepad) {
333
+ throw new Error('Cannot update layouts, failed to find selected device');
334
+ }
335
+ const matchingLayout = findMatchingGamepadLayout({
336
+ gamepad: selectedGamepad,
337
+ layouts: savedLayouts,
338
+ gamepadModelMap: savedModelMap,
339
+ });
340
+ const systemVersions = getSystemVersions();
341
+ const areSystemsEqual = matchingLayout
342
+ ? includesSystemVersion(matchingLayout.systemVersions, systemVersions)
343
+ : true;
344
+ const emptyLayout = createEmptyGamepadLayout(selectedGamepad);
345
+ const layoutToEdit = areSystemsEqual
346
+ ? (matchingLayout ?? emptyLayout)
347
+ : matchingLayout
348
+ ? {
349
+ ...copyThroughJson(matchingLayout),
350
+ systemVersions: emptyLayout.systemVersions,
351
+ }
352
+ : emptyLayout;
353
+ layoutToEdit.inputMappings[event.detail.inputName] =
354
+ event.detail.mappedName;
355
+ if (!matchingLayout || !areSystemsEqual) {
356
+ savedLayouts.push(layoutToEdit);
357
+ }
358
+ console.info('Saving updated layouts:');
359
+ console.info(JSON.stringify(savedLayouts));
360
+ state.savedGamepadLayouts.setValue(copyThroughJson(savedLayouts));
361
+ await saveLayouts(savedLayouts);
362
+ })}
363
+ ></${VirEditMappingsModal}>
364
+ `
365
+ : '';
366
+ return html `
367
+ <main
368
+ ${listen(ModalClose, () => {
369
+ updateState({
370
+ editMode: EditMode.None,
371
+ });
372
+ })}
373
+ ${listen(SelectedGamepadIndexChange, (event) => {
374
+ updateState({
375
+ gamepadIndexForEditing: event.detail,
376
+ });
377
+ })}
378
+ >
379
+ <div class="modal-wrapper">${currentModalTemplate}</div>
380
+ <header>
381
+ <div class="column">
382
+ <h1>Gamepad Layout Tester</h1>
383
+ <p>
384
+ Map gamepad ids to gamepad models, brands, and input names using the
385
+ gamepad-type npm package.
386
+ </p>
387
+ <ul>
388
+ <li>
389
+ <a href="https://electrovir.github.io/gamepad-type/docs">
390
+ Full API docs
391
+ </a>
392
+ </li>
393
+ <li>
394
+ <a href="https://github.com/electrovir/gamepad-type">
395
+ Repository in GitHub
396
+ </a>
397
+ </li>
398
+ <li>
399
+ <a href="https://www.npmjs.com/package/gamepad-type">
400
+ Package on npm
401
+ </a>
402
+ </li>
403
+ </ul>
404
+ </div>
405
+ <div class="row">
406
+ <p
407
+ class="user-agent"
408
+ title="These versions determine gamepad input mappings."
409
+ >
410
+ Current system versions (used for correct mapping):
411
+ <br />
412
+ ${userAgent.osName} ${userAgent.osVersion}
413
+ <br />
414
+ ${userAgent.browserName} ${userAgent.browserVersion}
415
+ </p>
416
+ <div class="submission-buttons">
417
+ <div class="column">
418
+ <${ViraButton.assign({
419
+ text: 'Submit Changes',
420
+ isDisabled: !hasNotSubmitted,
421
+ })}
422
+ ${listen('click', async () => {
423
+ await savedSubmittedChanges(notSubmittedChanges);
424
+ state.submittedChanges.setValue(notSubmittedChanges);
425
+ sendLog.info('New mapping override received', {
426
+ context: {
427
+ changes: notSubmittedChanges,
428
+ systemVersions: getSystemVersions(),
429
+ connectedDevices: state.gamepadDevices.map((gamepadDevice) => {
430
+ return pickObjectKeys(gamepadDevice, [
431
+ 'deviceKey',
432
+ 'deviceName',
433
+ 'deviceType',
434
+ ]);
435
+ }),
436
+ },
437
+ });
438
+ })}
439
+ ></${ViraButton}>
440
+ <p class="subtitle">
441
+ Submit your changes. IP address is not stored.
442
+ </p>
443
+ </div>
444
+ <${ViraButton.assign({
445
+ text: 'Reset Changes',
446
+ isDisabled: !hasAnyChanges,
447
+ buttonEmphasis: ViraEmphasis.Subtle,
448
+ color: ViraColorVariant.Danger,
449
+ })}
450
+ ${listen('click', async () => {
451
+ await resetAllSavedData();
452
+ state.savedGamepadLayouts.setValue(loadSavedLayouts());
453
+ state.savedGamepadModelMap.setValue(loadSavedModelMap());
454
+ })}
455
+ ></${ViraButton}>
456
+ </div>
457
+ </div>
458
+ </header>
459
+ <div class="gamepads-wrapper">
460
+ ${gamepadTemplates.length
461
+ ? gamepadTemplates
462
+ : html `
463
+ Connect a gamepad and press a button on it.
464
+ `}
465
+ </div>
466
+ </main>
467
+ `;
468
+ },
469
+ });
@@ -0,0 +1 @@
1
+ export declare const ModalClose: import("element-vir").DefinedTypedEvent<"modal-close", void>;
@@ -0,0 +1,2 @@
1
+ import { defineTypedEvent } from 'element-vir';
2
+ export const ModalClose = defineTypedEvent()('modal-close');
@@ -0,0 +1 @@
1
+ export declare const SelectedGamepadIndexChange: import("element-vir").DefinedTypedEvent<"selected-gamepad-index-change", number>;
@@ -0,0 +1,2 @@
1
+ import { defineTypedEvent } from 'element-vir';
2
+ export const SelectedGamepadIndexChange = defineTypedEvent()('selected-gamepad-index-change');
@@ -0,0 +1 @@
1
+ export declare function getGamepadBrandColor(gamepadBrand: string | undefined): string;
@@ -0,0 +1,14 @@
1
+ import { check } from '@augment-vir/assert';
2
+ import { PredefinedGamepadBrand } from '../index.js';
3
+ const fallbackBrandColor = '#ffaaaa';
4
+ const gamepadBrandColors = {
5
+ [PredefinedGamepadBrand.Microsoft]: '#aaff97',
6
+ [PredefinedGamepadBrand.Nintendo]: '#ffaaaa',
7
+ [PredefinedGamepadBrand.Sony]: '#86e2ff',
8
+ [PredefinedGamepadBrand.Valve]: '#ffaaaa',
9
+ };
10
+ export function getGamepadBrandColor(gamepadBrand) {
11
+ return check.isEnumValue(gamepadBrand, PredefinedGamepadBrand)
12
+ ? gamepadBrandColors[gamepadBrand]
13
+ : fallbackBrandColor;
14
+ }
@@ -0,0 +1,6 @@
1
+ declare global {
2
+ const VITE_RELEASE_NAME: string;
3
+ }
4
+ export declare const globalVars: {
5
+ releaseName: string;
6
+ };
@@ -0,0 +1,3 @@
1
+ export const globalVars = {
2
+ releaseName: typeof VITE_RELEASE_NAME === 'undefined' ? 'unknown' : VITE_RELEASE_NAME,
3
+ };
@@ -0,0 +1,7 @@
1
+ import { type GamepadLayout, type PredefinedGamepadModel } from '../index.js';
2
+ export type GamepadOverrides = Readonly<{
3
+ gamepadTypes: Readonly<{
4
+ [deviceName: string]: PredefinedGamepadModel;
5
+ }>;
6
+ gamepadLayouts: ReadonlyArray<Readonly<GamepadLayout>>;
7
+ }>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export declare function setupSentry(): Promise<void>;
@@ -0,0 +1,11 @@
1
+ import { initSentry } from 'sentry-vir/dist/browser.js';
2
+ import { globalVars } from './global-vars.js';
3
+ const isDev = window.location.hostname === 'localhost';
4
+ export async function setupSentry() {
5
+ await initSentry({
6
+ dsn: 'https://5b5d58b459305bfcd595aabfd356136c@o4506447710060544.ingest.sentry.io/4506479530737664',
7
+ isDev,
8
+ releaseEnv: isDev ? 'dev' : 'prod',
9
+ releaseName: globalVars.releaseName,
10
+ });
11
+ }
@@ -0,0 +1 @@
1
+ export declare function kebabCaseToNameCase(input: string): string;
@@ -0,0 +1,7 @@
1
+ import { setFirstLetterCasing, StringCase } from '@augment-vir/common';
2
+ export function kebabCaseToNameCase(input) {
3
+ return input
4
+ .split('-')
5
+ .map((word) => setFirstLetterCasing(word, StringCase.Upper))
6
+ .join(' ');
7
+ }
@@ -0,0 +1,6 @@
1
+ import { type GamepadLayout, type GamepadModelMap } from '../../index.js';
2
+ export type ChangedEntries = {
3
+ layouts: ReadonlyArray<Readonly<GamepadLayout>>;
4
+ models: Readonly<GamepadModelMap>;
5
+ };
6
+ export declare function extractNewChanges({ layouts, models }: Readonly<ChangedEntries>, lastSubmitted: Readonly<ChangedEntries> | undefined): Readonly<ChangedEntries>;
@@ -0,0 +1,28 @@
1
+ import { check } from '@augment-vir/assert';
2
+ import { filterObject } from '@augment-vir/common';
3
+ import { defaultGamepadLayouts, defaultGamepadModelMap, } from '../../index.js';
4
+ export function extractNewChanges({ layouts, models }, lastSubmitted) {
5
+ const changedLayouts = layouts.filter((layout) => {
6
+ const matchesDefault = defaultGamepadLayouts.some((defaultLayout) => {
7
+ return check.jsonEquals(defaultLayout, layout);
8
+ });
9
+ const matchesSubmitted = lastSubmitted
10
+ ? lastSubmitted.layouts.some((submittedLayout) => {
11
+ return check.jsonEquals(submittedLayout, layout);
12
+ })
13
+ : false;
14
+ return !matchesDefault && !matchesSubmitted;
15
+ });
16
+ const changedModels = filterObject(models, (deviceName, newModel) => {
17
+ const newFromDefault = !(deviceName in defaultGamepadModelMap) ||
18
+ defaultGamepadModelMap[deviceName] !== newModel;
19
+ const newFromSubmitted = lastSubmitted
20
+ ? !(deviceName in lastSubmitted.models) || lastSubmitted.models[deviceName] !== newModel
21
+ : true;
22
+ return newFromSubmitted && newFromDefault;
23
+ });
24
+ return {
25
+ layouts: changedLayouts,
26
+ models: changedModels,
27
+ };
28
+ }
@@ -0,0 +1,9 @@
1
+ import { type GamepadLayout, type GamepadModelMap } from '../../index.js';
2
+ import { type ChangedEntries } from './check-changes.js';
3
+ export declare function resetAllSavedData(): Promise<void>;
4
+ export declare function loadSavedLayouts(): Promise<ReadonlyArray<Readonly<GamepadLayout>>>;
5
+ export declare function saveLayouts(layouts: ReadonlyArray<Readonly<GamepadLayout>>): Promise<void>;
6
+ export declare function loadSavedModelMap(): Promise<Readonly<GamepadModelMap>>;
7
+ export declare function saveModelMap(types: Readonly<GamepadModelMap>): Promise<void>;
8
+ export declare function loadSubmittedChanges(): Promise<Readonly<ChangedEntries> | undefined>;
9
+ export declare function savedSubmittedChanges(changes: Readonly<ChangedEntries>): Promise<void>;
@@ -0,0 +1,36 @@
1
+ import { Store } from 'indexed-vir';
2
+ import { defaultGamepadLayouts, defaultGamepadModelMap, } from '../../index.js';
3
+ const gamepadTypeStore = new Store('gamepad-type-indexed-vir');
4
+ const savedLayoutsKey = 'saved-layouts';
5
+ const savedTypesKey = 'saved-types';
6
+ const submittedChangesKey = 'submitted-changes';
7
+ async function loadSavedValue(key) {
8
+ const storedValue = await gamepadTypeStore.getItem(key);
9
+ return (storedValue ?? undefined);
10
+ }
11
+ export async function resetAllSavedData() {
12
+ await gamepadTypeStore.clear();
13
+ }
14
+ export async function loadSavedLayouts() {
15
+ return ((await loadSavedValue(savedLayoutsKey)) ??
16
+ defaultGamepadLayouts);
17
+ }
18
+ export async function saveLayouts(layouts) {
19
+ await gamepadTypeStore.setItem(savedLayoutsKey, layouts);
20
+ }
21
+ export async function loadSavedModelMap() {
22
+ const loadedModelMap = (await loadSavedValue(savedTypesKey)) ?? {};
23
+ return {
24
+ ...defaultGamepadModelMap,
25
+ ...loadedModelMap,
26
+ };
27
+ }
28
+ export async function saveModelMap(types) {
29
+ await gamepadTypeStore.setItem(savedTypesKey, types);
30
+ }
31
+ export async function loadSubmittedChanges() {
32
+ return (await loadSavedValue(submittedChangesKey)) || undefined;
33
+ }
34
+ export async function savedSubmittedChanges(changes) {
35
+ await gamepadTypeStore.setItem(submittedChangesKey, changes);
36
+ }
@@ -0,0 +1,6 @@
1
+ export declare const userAgent: Readonly<{
2
+ osName: string;
3
+ osVersion: string;
4
+ browserName: string;
5
+ browserVersion: string;
6
+ }>;
@@ -0,0 +1,2 @@
1
+ import { getSystemVersions } from '../../index.js';
2
+ export const userAgent = getSystemVersions();
@@ -0,0 +1 @@
1
+ export declare function addWindowListener<K extends keyof WindowEventMap>(type: K, listener: (this: Window, ev: WindowEventMap[K]) => any, options?: boolean | AddEventListenerOptions): () => void;
@@ -0,0 +1,6 @@
1
+ export function addWindowListener(type, listener, options) {
2
+ window.addEventListener(type, listener, options);
3
+ return () => {
4
+ window.removeEventListener(type, listener, options);
5
+ };
6
+ }