Skip to content

Commit 200b906

Browse files
committed
Generate Controls controller profiles with custom actions and confirmed reset - PR_26161_027-controls-controller-profile-generation
1 parent 39f34d8 commit 200b906

10 files changed

Lines changed: 1921 additions & 867 deletions

File tree

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,10 @@
1-
# git status --short
2-
M docs_build/dev/reports/codex_changed_files.txt
3-
M docs_build/dev/reports/codex_review.diff
4-
M docs_build/dev/reports/coverage_changed_js_guardrail.txt
5-
M docs_build/dev/reports/playwright_v8_coverage_report.txt
6-
M tests/playwright/tools/InputMappingV2Tool.spec.mjs
7-
M toolbox/controls/controls.js
8-
M toolbox/controls/index.html
9-
?? docs_build/dev/reports/controls-object-action-mapping-validation-report.md
10-
11-
# git ls-files --others --exclude-standard
12-
docs_build/dev/reports/controls-object-action-mapping-validation-report.md
13-
14-
# git diff --stat (excluding review artifact files)
15-
.../dev/reports/coverage_changed_js_guardrail.txt | 2 +-
16-
.../dev/reports/playwright_v8_coverage_report.txt | 8 +-
17-
tests/playwright/tools/InputMappingV2Tool.spec.mjs | 220 +++++++++++++++++++++
18-
toolbox/controls/controls.js | 205 +++++++++++++++++--
19-
toolbox/controls/index.html | 5 +
20-
5 files changed, 416 insertions(+), 24 deletions(-)
1+
docs_build/dev/reports/controls-controller-profile-generation-report.md
2+
docs_build/dev/reports/codex_review.diff
3+
docs_build/dev/reports/codex_changed_files.txt
4+
docs_build/dev/reports/playwright_v8_coverage_report.txt
5+
docs_build/dev/reports/coverage_changed_js_guardrail.txt
6+
src/dev-runtime/persistence/mock-db-store.js
7+
src/dev-runtime/persistence/tool-repositories/input-mapping-mock-repository.js
8+
tests/playwright/tools/InputMappingV2Tool.spec.mjs
9+
toolbox/controls/controls.js
10+
toolbox/controls/index.html

docs_build/dev/reports/codex_review.diff

Lines changed: 1227 additions & 650 deletions
Large diffs are not rendered by default.
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# PR_26161_027 Controls Controller Profile Generation
2+
3+
## Branch Validation
4+
PASS: current branch was verified as `main` before edits.
5+
6+
## Impacted Lane
7+
Controls / Input Mapping V2 targeted lane.
8+
9+
## Playwright Impacted
10+
Yes.
11+
12+
## Summary
13+
- Added explicit DB-backed controller profile generation from detected Device Type selections.
14+
- Added visible fallback status for Exact saved profile, Default Gamepad Mapping, Keyboard/Mouse Default, and Missing Mapping.
15+
- Added Create Profile From Default as a visible fallback action for unsaved detected gamepads.
16+
- Added generated gamepad input layout rows from exposed browser gamepad buttons, axes, triggers, and d-pad entries.
17+
- Added DB-backed custom Actions and exposed saved custom Actions in the Action catalog and mapping dropdowns after reload.
18+
- Converted center Input Mapping and Controller Profile areas into accordions.
19+
- Updated Reset Mappings to require the exact confirm dialog text: `This will delete all Mappings, are you sure?`
20+
21+
## Persistence
22+
- Input mappings remain DB-backed through the shared Controls repository.
23+
- Controller profiles remain DB-backed through the shared Controls repository.
24+
- Custom actions are now DB-backed through the shared Controls repository.
25+
- No page-local memory is used as the source of truth.
26+
27+
## Testing Performed
28+
- `node --check toolbox/controls/controls.js`
29+
- `node --check src/dev-runtime/persistence/tool-repositories/input-mapping-mock-repository.js`
30+
- `node --check src/dev-runtime/persistence/mock-db-store.js`
31+
- `node --check tests/playwright/tools/InputMappingV2Tool.spec.mjs`
32+
- `npx playwright test tests/playwright/tools/InputMappingV2Tool.spec.mjs`
33+
- Result: 7 passed.
34+
35+
## Playwright Coverage
36+
- Produced `docs_build/dev/reports/playwright_v8_coverage_report.txt`.
37+
- Changed browser runtime JS covered: `toolbox/controls/controls.js` at 92% function coverage.
38+
- Shared DB/mock adapter files are server-side and reported by the V8 coverage artifact as advisory WARN entries because Playwright browser V8 coverage does not collect server-side modules; their behavior was exercised through the targeted Controls Playwright API flows.
39+
40+
## Manual Validation Steps
41+
1. Open `toolbox/controls/index.html`.
42+
2. Click Refresh Devices and verify Keyboard, Mouse, and detected gamepads appear in Device Type.
43+
3. Select a gamepad and verify `Using Default Gamepad Mapping` and `Missing saved profile for this controller` appear without saving a profile.
44+
4. Click Add Profile and verify a DB-backed profile is created with generated input rows requiring Action assignment.
45+
5. Delete the saved profile, then click Create Profile From Default and verify a real DB-backed profile is created.
46+
6. Add a custom Action and verify it appears in the Actions catalog and Action dropdown after reload.
47+
7. Add, edit, and reset mappings; verify Reset Mappings opens the required confirm dialog and OK persists deletion after reload.
48+
49+
## Skipped Lanes
50+
- Full samples validation was skipped as requested. This PR is scoped to Controls UI/tool persistence and does not change sample JSON alignment or game runtime behavior.
51+
- `npm run test:workspace-v2` was skipped because the requested validation lane is the targeted Controls/Input Mapping Playwright suite, which covers the changed Controls tool behavior directly.
52+
53+
## Runtime Engine Behavior
54+
Confirmed: no production game runtime behavior was changed.
55+
56+
## Completion Check
57+
PASS: 100% of requested PR_26161_027 items were implemented and validated in the targeted lane.

docs_build/dev/reports/coverage_changed_js_guardrail.txt

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,10 @@ Missing changed runtime JS files are WARN, not FAIL.
66
Source: Playwright/Chromium built-in V8 coverage from the active Playwright run.
77

88
Changed runtime JS files considered:
9-
(0%) src/dev-runtime/guest-seeds/tool-metadata-inventory.js - WARNING: changed runtime JS file was not collected by Playwright V8 coverage; advisory only
109
(0%) src/dev-runtime/persistence/mock-db-store.js - WARNING: changed runtime JS file was not collected by Playwright V8 coverage; advisory only
1110
(0%) src/dev-runtime/persistence/tool-repositories/input-mapping-mock-repository.js - WARNING: changed runtime JS file was not collected by Playwright V8 coverage; advisory only
12-
(0%) src/dev-runtime/server/mock-api-router.mjs - WARNING: changed runtime JS file was not collected by Playwright V8 coverage; advisory only
13-
(93%) toolbox/controls/controls.js - executed lines 1379/1379; executed functions 138/149
14-
(100%) toolbox/controls/controls-api-client.js - executed lines 12/12; executed functions 2/2
11+
(92%) toolbox/controls/controls.js - executed lines 1622/1622; executed functions 165/180
1512

1613
Guardrail warnings:
17-
(0%) src/dev-runtime/guest-seeds/tool-metadata-inventory.js - WARNING: changed runtime JS file missing from coverage; advisory only
1814
(0%) src/dev-runtime/persistence/mock-db-store.js - WARNING: changed runtime JS file missing from coverage; advisory only
1915
(0%) src/dev-runtime/persistence/tool-repositories/input-mapping-mock-repository.js - WARNING: changed runtime JS file missing from coverage; advisory only
20-
(0%) src/dev-runtime/server/mock-api-router.mjs - WARNING: changed runtime JS file missing from coverage; advisory only

docs_build/dev/reports/playwright_v8_coverage_report.txt

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,35 +17,25 @@ Exercised tool entry points detected:
1717
(59%) Theme V2 Shared JS - exercised 2 runtime JS files
1818

1919
Changed runtime JS files covered:
20-
(0%) src/dev-runtime/guest-seeds/tool-metadata-inventory.js - WARNING: changed runtime JS file was not collected by Playwright V8 coverage; advisory only
2120
(0%) src/dev-runtime/persistence/mock-db-store.js - WARNING: changed runtime JS file was not collected by Playwright V8 coverage; advisory only
2221
(0%) src/dev-runtime/persistence/tool-repositories/input-mapping-mock-repository.js - WARNING: changed runtime JS file was not collected by Playwright V8 coverage; advisory only
23-
(0%) src/dev-runtime/server/mock-api-router.mjs - WARNING: changed runtime JS file was not collected by Playwright V8 coverage; advisory only
24-
(93%) toolbox/controls/controls.js - executed lines 1379/1379; executed functions 138/149
25-
(100%) toolbox/controls/controls-api-client.js - executed lines 12/12; executed functions 2/2
22+
(92%) toolbox/controls/controls.js - executed lines 1622/1622; executed functions 165/180
2623

2724
Files with executed line/function counts where available:
2825
(53%) src/engine/api/server-api-client.js - executed lines 159/159; executed functions 10/19
2926
(58%) assets/theme-v2/js/gamefoundry-partials.js - executed lines 603/603; executed functions 30/52
3027
(64%) assets/theme-v2/js/tool-display-mode.js - executed lines 209/209; executed functions 9/14
3128
(75%) toolbox/tool-registry-api-client.js - executed lines 152/152; executed functions 21/28
32-
(93%) toolbox/controls/controls.js - executed lines 1379/1379; executed functions 138/149
29+
(92%) toolbox/controls/controls.js - executed lines 1622/1622; executed functions 165/180
3330
(100%) toolbox/controls/controls-api-client.js - executed lines 12/12; executed functions 2/2
3431
(100%) toolbox/objects/objects-api-client.js - executed lines 10/10; executed functions 2/2
3532

3633
Uncovered or low-coverage changed JS files:
37-
(0%) src/dev-runtime/guest-seeds/tool-metadata-inventory.js - WARNING: uncovered changed runtime JS file; advisory only
3834
(0%) src/dev-runtime/persistence/mock-db-store.js - WARNING: uncovered changed runtime JS file; advisory only
3935
(0%) src/dev-runtime/persistence/tool-repositories/input-mapping-mock-repository.js - WARNING: uncovered changed runtime JS file; advisory only
40-
(0%) src/dev-runtime/server/mock-api-router.mjs - WARNING: uncovered changed runtime JS file; advisory only
4136

4237
Changed JS files considered:
43-
(0%) src/dev-runtime/guest-seeds/tool-metadata-inventory.js - changed JS file not collected as browser runtime coverage
4438
(0%) src/dev-runtime/persistence/mock-db-store.js - changed JS file not collected as browser runtime coverage
4539
(0%) src/dev-runtime/persistence/tool-repositories/input-mapping-mock-repository.js - changed JS file not collected as browser runtime coverage
46-
(0%) src/dev-runtime/server/mock-api-router.mjs - changed JS file not collected as browser runtime coverage
47-
(0%) tests/playwright/tools/AdminDbViewer.spec.mjs - changed JS file not collected as browser runtime coverage
4840
(0%) tests/playwright/tools/InputMappingV2Tool.spec.mjs - changed JS file not collected as browser runtime coverage
49-
(0%) tests/playwright/tools/ToolboxAdminMetadataSsot.spec.mjs - changed JS file not collected as browser runtime coverage
50-
(93%) toolbox/controls/controls.js - changed JS file with browser V8 coverage
51-
(100%) toolbox/controls/controls-api-client.js - changed JS file with browser V8 coverage
41+
(92%) toolbox/controls/controls.js - changed JS file with browser V8 coverage

src/dev-runtime/persistence/mock-db-store.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ export const MOCK_DB_TOOL_GROUPS = Object.freeze({
8989
}),
9090
controls: Object.freeze({
9191
label: "Controls",
92-
tableNames: Object.freeze(["input_mapping_records", "input_controller_profile_records"]),
92+
tableNames: Object.freeze(["input_mapping_records", "input_controller_profile_records", "input_custom_action_records"]),
9393
}),
9494
"project-journey": Object.freeze({
9595
label: "Project Journey",
@@ -138,6 +138,7 @@ const MOCK_DB_TABLE_SCHEMAS = Object.freeze({
138138
object_definition_records: Object.freeze(["key", "id", "projectId", "name", "type", "state", "modelType", "renderType", "renderAssetKey", "renderPreviewPath", "capabilities", "behavior", "interaction", "recordOrder", "createdAt", "updatedAt", "createdBy", "updatedBy"]),
139139
input_mapping_records: Object.freeze(["key", "id", "projectId", "objectKey", "objectName", "action", "actionLabel", "inputDevice", "source", "binding", "label", "engine", "controllerProfileId", "mappingProfile", "state", "recordOrder", "createdAt", "updatedAt", "createdBy", "updatedBy"]),
140140
input_controller_profile_records: Object.freeze(["key", "id", "projectId", "deviceType", "controllerName", "controllerId", "mappingProfile", "inputs", "actions", "recordOrder", "createdAt", "updatedAt", "createdBy", "updatedBy"]),
141+
input_custom_action_records: Object.freeze(["key", "id", "projectId", "label", "recordOrder", "createdAt", "updatedAt", "createdBy", "updatedBy"]),
141142
project_journey_note_types: Object.freeze(["key", "typeSlug", "name", "seeded", "userExtensible", "createdAt", "updatedAt", "createdBy", "updatedBy"]),
142143
project_journey_notes: Object.freeze(["key", "slug", "projectKey", "ownerKey", "name", "typeKey", "createdAt", "updatedAt", "createdBy", "updatedBy"]),
143144
project_journey_templates: Object.freeze(["key", "templateSlug", "originalMeaning", "systemGuidance", "linkedToolContexts", "version", "isActive", "createdAt", "updatedAt", "createdBy", "updatedBy"]),

src/dev-runtime/persistence/tool-repositories/input-mapping-mock-repository.js

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
export const INPUT_MAPPING_TOOL_TABLES = Object.freeze([
88
"input_mapping_records",
99
"input_controller_profile_records",
10+
"input_custom_action_records",
1011
]);
1112

1213
const INPUT_MAPPING_DB_OWNER = "controls";
@@ -26,6 +27,7 @@ function cloneTables(tables) {
2627
function createEmptyTables() {
2728
return {
2829
input_controller_profile_records: [],
30+
input_custom_action_records: [],
2931
input_mapping_records: [],
3032
};
3133
}
@@ -44,9 +46,11 @@ function mappingKeyFromText(value) {
4446
function initialTables(options = {}) {
4547
const explicitRows = options.memoryDbTables?.input_mapping_records;
4648
const explicitProfiles = options.memoryDbTables?.input_controller_profile_records;
47-
if (Array.isArray(explicitRows) || Array.isArray(explicitProfiles)) {
49+
const explicitCustomActions = options.memoryDbTables?.input_custom_action_records;
50+
if (Array.isArray(explicitRows) || Array.isArray(explicitProfiles) || Array.isArray(explicitCustomActions)) {
4851
return normalizeMockDbTables(INPUT_MAPPING_DB_OWNER, {
4952
input_controller_profile_records: Array.isArray(explicitProfiles) ? explicitProfiles : [],
53+
input_custom_action_records: Array.isArray(explicitCustomActions) ? explicitCustomActions : [],
5054
input_mapping_records: Array.isArray(explicitRows) ? explicitRows : [],
5155
}, options);
5256
}
@@ -91,6 +95,15 @@ function controllerProfileFromRecord(record = {}) {
9195
};
9296
}
9397

98+
function customActionFromRecord(record = {}) {
99+
const label = normalizeText(record.label || record.actionLabel || record.action);
100+
const id = normalizeText(record.id) || mappingKeyFromText(label);
101+
return {
102+
id,
103+
label: label || id,
104+
};
105+
}
106+
94107
function mappingFromRecord(record = {}) {
95108
return {
96109
action: normalizeText(record.action),
@@ -151,6 +164,17 @@ export function createInputMappingToolMockRepository(options = {}) {
151164
.map(controllerProfileFromRecord);
152165
}
153166

167+
function listCustomActions(projectId = "") {
168+
const targetProjectId = normalizeText(projectId) || activeProjectId();
169+
return [...(tables.input_custom_action_records || [])]
170+
.sort((left, right) => (
171+
(Number(left.recordOrder) || 0) - (Number(right.recordOrder) || 0)
172+
|| normalizeText(left.label).localeCompare(normalizeText(right.label))
173+
))
174+
.filter((record) => normalizeText(record.projectId) === targetProjectId)
175+
.map(customActionFromRecord);
176+
}
177+
154178
function replaceControllerProfiles(profiles = [], projectId = "") {
155179
const targetProjectId = normalizeText(projectId) || activeProjectId();
156180
const existingRows = new Map(
@@ -197,6 +221,46 @@ export function createInputMappingToolMockRepository(options = {}) {
197221
};
198222
}
199223

224+
function replaceCustomActions(actions = [], projectId = "") {
225+
const targetProjectId = normalizeText(projectId) || activeProjectId();
226+
const existingRows = new Map(
227+
(tables.input_custom_action_records || [])
228+
.filter((record) => normalizeText(record.projectId) === targetProjectId)
229+
.map((record) => [normalizeText(record.id), record]),
230+
);
231+
const timestamp = new Date().toISOString();
232+
const userKey = activeUserKey();
233+
const nextRows = (Array.isArray(actions) ? actions : []).map((action, index) => {
234+
const label = normalizeText(action.label || action.actionLabel || action.action);
235+
const id = normalizeText(action.id) || mappingKeyFromText(label || `custom-action-${index + 1}`);
236+
const previous = existingRows.get(id);
237+
return {
238+
createdAt: previous?.createdAt || timestamp,
239+
createdBy: previous?.createdBy || userKey,
240+
id,
241+
key: previous?.key,
242+
label: label || id,
243+
projectId: targetProjectId,
244+
recordOrder: index + 1,
245+
updatedAt: timestamp,
246+
updatedBy: userKey,
247+
};
248+
});
249+
250+
tables.input_custom_action_records = [
251+
...(tables.input_custom_action_records || []).filter(
252+
(record) => normalizeText(record.projectId) !== targetProjectId,
253+
),
254+
...nextRows,
255+
];
256+
persistTables();
257+
return {
258+
customActions: listCustomActions(targetProjectId),
259+
saved: true,
260+
snapshot: getSnapshot(),
261+
};
262+
}
263+
200264
function replaceMappings(mappings = [], projectId = "") {
201265
const targetProjectId = normalizeText(projectId) || activeProjectId();
202266
const existingRows = new Map(
@@ -271,6 +335,7 @@ export function createInputMappingToolMockRepository(options = {}) {
271335
const normalizedTables = getTables();
272336
return {
273337
controllerProfiles: listControllerProfiles(),
338+
customActions: listCustomActions(),
274339
mappings: listMappings(),
275340
tableCounts: tableCounts(normalizedTables),
276341
tables: normalizedTables,
@@ -282,8 +347,10 @@ export function createInputMappingToolMockRepository(options = {}) {
282347
getSnapshot,
283348
getTables,
284349
listControllerProfiles,
350+
listCustomActions,
285351
listMappings,
286352
replaceControllerProfiles,
353+
replaceCustomActions,
287354
replaceMappings,
288355
resetMappings,
289356
};

0 commit comments

Comments
 (0)