@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.
- package/LICENSE-CC0 +121 -0
- package/LICENSE-MIT +21 -0
- package/README.md +102 -0
- package/dist/create.d.ts +10 -0
- package/dist/create.js +20 -0
- package/dist/default-layouts.d.ts +8 -0
- package/dist/default-layouts.js +339 -0
- package/dist/default-models.d.ts +14 -0
- package/dist/default-models.js +46 -0
- package/dist/demo/elements/gamepad/vir-gamepad-axe.element.d.ts +7 -0
- package/dist/demo/elements/gamepad/vir-gamepad-axe.element.js +61 -0
- package/dist/demo/elements/gamepad/vir-gamepad-button.element.d.ts +7 -0
- package/dist/demo/elements/gamepad/vir-gamepad-button.element.js +87 -0
- package/dist/demo/elements/gamepad/vir-gamepad-input.element.d.ts +7 -0
- package/dist/demo/elements/gamepad/vir-gamepad-input.element.js +38 -0
- package/dist/demo/elements/gamepad/vir-gamepad-inputs.element.d.ts +20 -0
- package/dist/demo/elements/gamepad/vir-gamepad-inputs.element.js +110 -0
- package/dist/demo/elements/gamepad/vir-gamepad-name.element.d.ts +11 -0
- package/dist/demo/elements/gamepad/vir-gamepad-name.element.js +158 -0
- package/dist/demo/elements/gamepad/vir-gamepad.element.d.ts +8 -0
- package/dist/demo/elements/gamepad/vir-gamepad.element.js +41 -0
- package/dist/demo/elements/modals/vir-create-new-type.modal.element.d.ts +9 -0
- package/dist/demo/elements/modals/vir-create-new-type.modal.element.js +135 -0
- package/dist/demo/elements/modals/vir-edit-mappings.modal.element.d.ts +29 -0
- package/dist/demo/elements/modals/vir-edit-mappings.modal.element.js +185 -0
- package/dist/demo/elements/modals/vir-modal.element.d.ts +3 -0
- package/dist/demo/elements/modals/vir-modal.element.js +88 -0
- package/dist/demo/elements/vir-demo.element.d.ts +27 -0
- package/dist/demo/elements/vir-demo.element.js +469 -0
- package/dist/demo/events/modal-close.event.d.ts +1 -0
- package/dist/demo/events/modal-close.event.js +2 -0
- package/dist/demo/events/selected-gamepad-index-change.event.d.ts +1 -0
- package/dist/demo/events/selected-gamepad-index-change.event.js +2 -0
- package/dist/demo/gamepad-brand-color.d.ts +1 -0
- package/dist/demo/gamepad-brand-color.js +14 -0
- package/dist/demo/global-vars.d.ts +6 -0
- package/dist/demo/global-vars.js +3 -0
- package/dist/demo/overrides.d.ts +7 -0
- package/dist/demo/overrides.js +1 -0
- package/dist/demo/sentry-setup.d.ts +1 -0
- package/dist/demo/sentry-setup.js +11 -0
- package/dist/demo/util/casing.d.ts +1 -0
- package/dist/demo/util/casing.js +7 -0
- package/dist/demo/util/check-changes.d.ts +6 -0
- package/dist/demo/util/check-changes.js +28 -0
- package/dist/demo/util/local-save-data.d.ts +9 -0
- package/dist/demo/util/local-save-data.js +36 -0
- package/dist/demo/util/user-agent.d.ts +6 -0
- package/dist/demo/util/user-agent.js +2 -0
- package/dist/demo/util/window-listener.d.ts +1 -0
- package/dist/demo/util/window-listener.js +6 -0
- package/dist/find-matches.d.ts +42 -0
- package/dist/find-matches.js +77 -0
- package/dist/gamepad-layout.d.ts +29 -0
- package/dist/gamepad-layout.js +1 -0
- package/dist/gamepad-model.d.ts +67 -0
- package/dist/gamepad-model.js +69 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -0
- package/dist/known-input.d.ts +126 -0
- package/dist/known-input.js +128 -0
- package/dist/system-versions.d.ts +25 -0
- package/dist/system-versions.js +26 -0
- package/dist/util/lowercase-keys.d.ts +2 -0
- package/dist/util/lowercase-keys.js +10 -0
- 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 @@
|
|
|
1
|
+
export declare const SelectedGamepadIndexChange: import("element-vir").DefinedTypedEvent<"selected-gamepad-index-change", number>;
|
|
@@ -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 @@
|
|
|
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,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 @@
|
|
|
1
|
+
export declare function addWindowListener<K extends keyof WindowEventMap>(type: K, listener: (this: Window, ev: WindowEventMap[K]) => any, options?: boolean | AddEventListenerOptions): () => void;
|