Skip to content

Commit 2040094

Browse files
committed
Persist Controls controller profiles through shared DB adapter - PR_26161_019-controls-db-controller-profiles
1 parent 9b5058f commit 2040094

11 files changed

Lines changed: 4112 additions & 1367 deletions

File tree

docs_build/dev/reports/codex_changed_files.txt

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,26 @@ M docs_build/dev/reports/codex_changed_files.txt
33
M docs_build/dev/reports/codex_review.diff
44
M docs_build/dev/reports/coverage_changed_js_guardrail.txt
55
M docs_build/dev/reports/playwright_v8_coverage_report.txt
6-
M src/dev-runtime/guest-seeds/tool-metadata-inventory.js
6+
M src/dev-runtime/persistence/mock-db-store.js
7+
M src/dev-runtime/persistence/tool-repositories/input-mapping-mock-repository.js
78
M tests/playwright/tools/InputMappingV2Tool.spec.mjs
89
M toolbox/controls/index.html
910
M toolbox/input-mapping-v2/index.html
10-
?? docs_build/dev/reports/controls-wireframe-controller-profiles-report.md
11+
M toolbox/input-mapping-v2/input-mapping-v2.js
12+
?? docs_build/dev/reports/controls-db-controller-profiles-report.md
1113

1214
# git ls-files --others --exclude-standard
13-
docs_build/dev/reports/controls-wireframe-controller-profiles-report.md
15+
docs_build/dev/reports/controls-db-controller-profiles-report.md
1416

1517
# git diff --stat
16-
docs_build/dev/reports/codex_changed_files.txt | 26 +-
17-
docs_build/dev/reports/codex_review.diff | 1100 +++++++++++++++-----
18-
.../dev/reports/coverage_changed_js_guardrail.txt | 7 -
19-
.../dev/reports/playwright_v8_coverage_report.txt | 11 -
20-
.../guest-seeds/tool-metadata-inventory.js | 4 +-
21-
tests/playwright/tools/InputMappingV2Tool.spec.mjs | 21 +-
22-
toolbox/controls/index.html | 32 +
23-
toolbox/input-mapping-v2/index.html | 32 +
24-
8 files changed, 927 insertions(+), 306 deletions(-)
18+
docs_build/dev/reports/codex_changed_files.txt | 30 +-
19+
docs_build/dev/reports/codex_review.diff | 2400 ++++++++------------
20+
.../dev/reports/coverage_changed_js_guardrail.txt | 6 +-
21+
.../dev/reports/playwright_v8_coverage_report.txt | 14 +-
22+
src/dev-runtime/persistence/mock-db-store.js | 5 +-
23+
.../input-mapping-mock-repository.js | 98 +-
24+
tests/playwright/tools/InputMappingV2Tool.spec.mjs | 92 +-
25+
toolbox/controls/index.html | 21 +-
26+
toolbox/input-mapping-v2/index.html | 21 +-
27+
toolbox/input-mapping-v2/input-mapping-v2.js | 326 ++-
28+
10 files changed, 1567 insertions(+), 1446 deletions(-)

docs_build/dev/reports/codex_review.diff

Lines changed: 3469 additions & 1321 deletions
Large diffs are not rendered by default.
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# PR_26161_019 Controls DB Controller Profiles Report
2+
3+
## Branch Validation
4+
5+
- PASS: Current branch was `main`.
6+
- Expected branch: `main`.
7+
- Worktree was clean before PR_26161_019 edits.
8+
9+
## Scope
10+
11+
- Impacted lane: Controls/Input Mapping recovery/UAT.
12+
- Playwright impacted: Yes.
13+
- Full samples validation: SKIPPED. Samples and sample JSON alignment are explicitly out of scope for this Controls DB-backed profile change.
14+
- Engine runtime behavior: Unchanged. No production controller runtime, engine input runtime, auth behavior, sample alignment, or unrelated rewrite was added.
15+
16+
## Requirement Checklist
17+
18+
- PASS: Read `docs_build/dev/PROJECT_INSTRUCTIONS.md`.
19+
- PASS: Verified branch `main` before edits.
20+
- PASS: Continued from PR_26161_018.
21+
- PASS: Controls status remains Wireframe.
22+
- PASS: Controller profiles persist through the shared DB/mock adapter using `input_controller_profile_records`.
23+
- PASS: Controller profile records include Device Type, Controller Name, Controller ID, Mapping Profile, Inputs, and Actions.
24+
- PASS: Page load reads mappings and controller profiles from the shared DB/mock adapter.
25+
- PASS: Mappings can reference a saved controller profile through `controllerProfileId` and `mappingProfile`.
26+
- PASS: Unknown controller status remains actionable and does not create fake profile rows.
27+
- PASS: Existing table-first mapping behavior, Add Mapping, keyboard capture, token delete, Mapping JSON, Devices, and Status panels are preserved.
28+
- PASS: No sample JSON alignment, auth behavior, or production controller runtime was added.
29+
30+
## Files Changed
31+
32+
- `src/dev-runtime/persistence/mock-db-store.js`
33+
- `src/dev-runtime/persistence/tool-repositories/input-mapping-mock-repository.js`
34+
- `toolbox/input-mapping-v2/input-mapping-v2.js`
35+
- `toolbox/input-mapping-v2/index.html`
36+
- `toolbox/controls/index.html`
37+
- `tests/playwright/tools/InputMappingV2Tool.spec.mjs`
38+
- `docs_build/dev/reports/controls-db-controller-profiles-report.md`
39+
- `docs_build/dev/reports/playwright_v8_coverage_report.txt`
40+
- `docs_build/dev/reports/coverage_changed_js_guardrail.txt`
41+
- `docs_build/dev/reports/codex_review.diff`
42+
- `docs_build/dev/reports/codex_changed_files.txt`
43+
44+
## Testing Performed
45+
46+
- PASS: `node --check toolbox/input-mapping-v2/input-mapping-v2.js`
47+
- PASS: `node --check src/dev-runtime/persistence/tool-repositories/input-mapping-mock-repository.js`
48+
- PASS: `node --check src/dev-runtime/persistence/mock-db-store.js`
49+
- PASS: `node --check tests/playwright/tools/InputMappingV2Tool.spec.mjs`
50+
- PASS: HTML restriction scan for inline scripts, style blocks, inline styles, and inline event handlers in both Controls routes.
51+
- PASS: `git diff --check`
52+
- Note: Git reported CRLF normalization warnings on touched files only.
53+
- PASS: `npx playwright test tests/playwright/tools/InputMappingV2Tool.spec.mjs --workers=1 --reporter=line`
54+
- Final result: 5 passed.
55+
56+
## Playwright Coverage
57+
58+
- Generated `docs_build/dev/reports/playwright_v8_coverage_report.txt`.
59+
- Generated `docs_build/dev/reports/coverage_changed_js_guardrail.txt`.
60+
- Coverage includes `toolbox/input-mapping-v2/input-mapping-v2.js` with browser V8 execution.
61+
- Coverage includes advisory WARN entries for server-side DB/mock adapter files because they are exercised through the local server API, not collected by browser V8 coverage.
62+
- The report may also list the prior Wireframe metadata file as advisory because the coverage helper includes the current HEAD comparison window; it is non-blocking and no Controls metadata file changed in PR_26161_019.
63+
64+
## Manual Validation Steps
65+
66+
1. Open `/toolbox/input-mapping-v2/index.html`.
67+
2. Confirm Controller Profiles shows no saved profile row before explicit user action.
68+
3. Confirm the unknown controller status tells the creator to Refresh Devices and use Add Profile.
69+
4. Click Add Profile, fill Device Type, Controller Name, Controller ID, Mapping Profile, Inputs, and Actions, then save.
70+
5. Reload and confirm the saved controller profile persists.
71+
6. Add a mapping, select the saved Mapping Profile, save, reload, and confirm the mapping still references the saved profile.
72+
7. Capture `KeyA`, confirm the mapping appears in the table and Mapping JSON, then click the token to delete it.
73+
74+
## Skipped Lanes
75+
76+
- Full samples smoke: SKIPPED because samples and sample JSON alignment are out of scope.
77+
- Broad workspace suite: SKIPPED because this PR only changes Controls/Input Mapping and its direct DB/mock adapter path.
78+
- Engine tests: SKIPPED because no production engine runtime behavior changed.

docs_build/dev/reports/coverage_changed_js_guardrail.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ Source: Playwright/Chromium built-in V8 coverage from the active Playwright run.
77

88
Changed runtime JS files considered:
99
(0%) src/dev-runtime/guest-seeds/tool-metadata-inventory.js - WARNING: changed runtime JS file was not collected by Playwright V8 coverage; advisory only
10-
(92%) toolbox/input-mapping-v2/input-mapping-v2.js - executed lines 703/703; executed functions 80/87
10+
(0%) src/dev-runtime/persistence/mock-db-store.js - WARNING: changed runtime JS file was not collected by Playwright V8 coverage; advisory only
11+
(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+
(90%) toolbox/input-mapping-v2/input-mapping-v2.js - executed lines 1000/1000; executed functions 99/110
1113

1214
Guardrail warnings:
1315
(0%) src/dev-runtime/guest-seeds/tool-metadata-inventory.js - WARNING: changed runtime JS file missing from coverage; advisory only
16+
(0%) src/dev-runtime/persistence/mock-db-store.js - WARNING: changed runtime JS file missing from coverage; advisory only
17+
(0%) src/dev-runtime/persistence/tool-repositories/input-mapping-mock-repository.js - WARNING: changed runtime JS file missing from coverage; advisory only

docs_build/dev/reports/playwright_v8_coverage_report.txt

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,27 +12,33 @@ Note: entry percentages use function coverage when available, otherwise line cov
1212
Note: coverage entries are aggregated across every page/tool where coverageReporter.start(page) and coverageReporter.stop(page) ran.
1313

1414
Exercised tool entry points detected:
15-
(88%) Toolbox Index - exercised 4 runtime JS files
15+
(87%) Toolbox Index - exercised 4 runtime JS files
1616
(0%) Tool Template V2 - not exercised by this Playwright run
1717
(59%) Theme V2 Shared JS - exercised 2 runtime JS files
1818

1919
Changed runtime JS files covered:
2020
(0%) src/dev-runtime/guest-seeds/tool-metadata-inventory.js - WARNING: changed runtime JS file was not collected by Playwright V8 coverage; advisory only
21-
(92%) toolbox/input-mapping-v2/input-mapping-v2.js - executed lines 703/703; executed functions 80/87
21+
(0%) src/dev-runtime/persistence/mock-db-store.js - WARNING: changed runtime JS file was not collected by Playwright V8 coverage; advisory only
22+
(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+
(90%) toolbox/input-mapping-v2/input-mapping-v2.js - executed lines 1000/1000; executed functions 99/110
2224

2325
Files with executed line/function counts where available:
2426
(53%) src/engine/api/server-api-client.js - executed lines 159/159; executed functions 10/19
2527
(58%) assets/theme-v2/js/gamefoundry-partials.js - executed lines 603/603; executed functions 30/52
2628
(64%) assets/theme-v2/js/tool-display-mode.js - executed lines 209/209; executed functions 9/14
2729
(75%) toolbox/tool-registry-api-client.js - executed lines 152/152; executed functions 21/28
28-
(92%) toolbox/input-mapping-v2/input-mapping-v2.js - executed lines 703/703; executed functions 80/87
30+
(90%) toolbox/input-mapping-v2/input-mapping-v2.js - executed lines 1000/1000; executed functions 99/110
2931
(100%) toolbox/input-mapping-v2/input-mapping-api-client.js - executed lines 12/12; executed functions 2/2
3032
(100%) toolbox/objects/objects-api-client.js - executed lines 10/10; executed functions 2/2
3133

3234
Uncovered or low-coverage changed JS files:
3335
(0%) src/dev-runtime/guest-seeds/tool-metadata-inventory.js - WARNING: uncovered changed runtime JS file; advisory only
36+
(0%) src/dev-runtime/persistence/mock-db-store.js - WARNING: uncovered changed runtime JS file; advisory only
37+
(0%) src/dev-runtime/persistence/tool-repositories/input-mapping-mock-repository.js - WARNING: uncovered changed runtime JS file; advisory only
3438

3539
Changed JS files considered:
3640
(0%) src/dev-runtime/guest-seeds/tool-metadata-inventory.js - changed JS file not collected as browser runtime coverage
41+
(0%) src/dev-runtime/persistence/mock-db-store.js - changed JS file not collected as browser runtime coverage
42+
(0%) src/dev-runtime/persistence/tool-repositories/input-mapping-mock-repository.js - changed JS file not collected as browser runtime coverage
3743
(0%) tests/playwright/tools/InputMappingV2Tool.spec.mjs - changed JS file not collected as browser runtime coverage
38-
(92%) toolbox/input-mapping-v2/input-mapping-v2.js - changed JS file with browser V8 coverage
44+
(90%) toolbox/input-mapping-v2/input-mapping-v2.js - changed JS file with browser V8 coverage

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ export const MOCK_DB_TOOL_GROUPS = Object.freeze({
8989
}),
9090
"input-mapping-v2": Object.freeze({
9191
label: "Input Mapping",
92-
tableNames: Object.freeze(["input_mapping_records"]),
92+
tableNames: Object.freeze(["input_mapping_records", "input_controller_profile_records"]),
9393
}),
9494
"project-journey": Object.freeze({
9595
label: "Project Journey",
@@ -136,7 +136,8 @@ const MOCK_DB_TABLE_SCHEMAS = Object.freeze({
136136
game_configuration_records: Object.freeze(["key", "projectKey", "status", "summary", "createdAt", "updatedAt", "createdBy", "updatedBy"]),
137137
game_configuration_validation_items: Object.freeze(["key", "projectKey", "label", "status", "action", "createdAt", "updatedAt", "createdBy", "updatedBy"]),
138138
object_definition_records: Object.freeze(["key", "id", "projectId", "name", "type", "state", "modelType", "renderType", "renderAssetKey", "renderPreviewPath", "capabilities", "behavior", "interaction", "recordOrder", "createdAt", "updatedAt", "createdBy", "updatedBy"]),
139-
input_mapping_records: Object.freeze(["key", "id", "projectId", "objectKey", "objectName", "action", "actionLabel", "inputDevice", "source", "binding", "label", "engine", "state", "recordOrder", "createdAt", "updatedAt", "createdBy", "updatedBy"]),
139+
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"]),
140+
input_controller_profile_records: Object.freeze(["key", "id", "projectId", "deviceType", "controllerName", "controllerId", "mappingProfile", "inputs", "actions", "recordOrder", "createdAt", "updatedAt", "createdBy", "updatedBy"]),
140141
project_journey_note_types: Object.freeze(["key", "typeSlug", "name", "seeded", "userExtensible", "createdAt", "updatedAt", "createdBy", "updatedBy"]),
141142
project_journey_notes: Object.freeze(["key", "slug", "projectKey", "ownerKey", "name", "typeKey", "createdAt", "updatedAt", "createdBy", "updatedBy"]),
142143
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: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66

77
export const INPUT_MAPPING_TOOL_TABLES = Object.freeze([
88
"input_mapping_records",
9+
"input_controller_profile_records",
910
]);
1011

1112
const INPUT_MAPPING_DB_OWNER = "input-mapping-v2";
@@ -24,6 +25,7 @@ function cloneTables(tables) {
2425

2526
function createEmptyTables() {
2627
return {
28+
input_controller_profile_records: [],
2729
input_mapping_records: [],
2830
};
2931
}
@@ -41,14 +43,26 @@ function mappingKeyFromText(value) {
4143

4244
function initialTables(options = {}) {
4345
const explicitRows = options.memoryDbTables?.input_mapping_records;
44-
if (Array.isArray(explicitRows)) {
46+
const explicitProfiles = options.memoryDbTables?.input_controller_profile_records;
47+
if (Array.isArray(explicitRows) || Array.isArray(explicitProfiles)) {
4548
return normalizeMockDbTables(INPUT_MAPPING_DB_OWNER, {
46-
input_mapping_records: explicitRows,
49+
input_controller_profile_records: Array.isArray(explicitProfiles) ? explicitProfiles : [],
50+
input_mapping_records: Array.isArray(explicitRows) ? explicitRows : [],
4751
}, options);
4852
}
4953
return normalizeMockDbTables(INPUT_MAPPING_DB_OWNER, createEmptyTables(), options);
5054
}
5155

56+
function normalizeList(value) {
57+
if (Array.isArray(value)) {
58+
return value.map(normalizeText).filter(Boolean);
59+
}
60+
return normalizeText(value)
61+
.split(",")
62+
.map(normalizeText)
63+
.filter(Boolean);
64+
}
65+
5266
function sortedMappingRows(tables) {
5367
return [...(tables.input_mapping_records || [])].sort((left, right) => (
5468
(Number(left.recordOrder) || 0) - (Number(right.recordOrder) || 0)
@@ -57,6 +71,26 @@ function sortedMappingRows(tables) {
5771
));
5872
}
5973

74+
function sortedControllerProfileRows(tables) {
75+
return [...(tables.input_controller_profile_records || [])].sort((left, right) => (
76+
(Number(left.recordOrder) || 0) - (Number(right.recordOrder) || 0)
77+
|| normalizeText(left.mappingProfile).localeCompare(normalizeText(right.mappingProfile))
78+
|| normalizeText(left.controllerName).localeCompare(normalizeText(right.controllerName))
79+
));
80+
}
81+
82+
function controllerProfileFromRecord(record = {}) {
83+
return {
84+
actions: normalizeList(record.actions),
85+
controllerId: normalizeText(record.controllerId),
86+
controllerName: normalizeText(record.controllerName),
87+
deviceType: normalizeText(record.deviceType),
88+
id: normalizeText(record.id),
89+
inputs: normalizeList(record.inputs),
90+
mappingProfile: normalizeText(record.mappingProfile),
91+
};
92+
}
93+
6094
function mappingFromRecord(record = {}) {
6195
return {
6296
action: normalizeText(record.action),
@@ -66,6 +100,8 @@ function mappingFromRecord(record = {}) {
66100
id: normalizeText(record.id),
67101
inputDevice: normalizeText(record.inputDevice),
68102
label: normalizeText(record.label),
103+
controllerProfileId: normalizeText(record.controllerProfileId),
104+
mappingProfile: normalizeText(record.mappingProfile),
69105
objectKey: normalizeText(record.objectKey),
70106
objectName: normalizeText(record.objectName),
71107
source: normalizeText(record.source),
@@ -108,6 +144,59 @@ export function createInputMappingToolMockRepository(options = {}) {
108144
.map(mappingFromRecord);
109145
}
110146

147+
function listControllerProfiles(projectId = "") {
148+
const targetProjectId = normalizeText(projectId) || activeProjectId();
149+
return sortedControllerProfileRows(tables)
150+
.filter((record) => normalizeText(record.projectId) === targetProjectId)
151+
.map(controllerProfileFromRecord);
152+
}
153+
154+
function replaceControllerProfiles(profiles = [], projectId = "") {
155+
const targetProjectId = normalizeText(projectId) || activeProjectId();
156+
const existingRows = new Map(
157+
(tables.input_controller_profile_records || [])
158+
.filter((record) => normalizeText(record.projectId) === targetProjectId)
159+
.map((record) => [normalizeText(record.id), record]),
160+
);
161+
const timestamp = new Date().toISOString();
162+
const userKey = activeUserKey();
163+
const nextRows = (Array.isArray(profiles) ? profiles : []).map((profile, index) => {
164+
const mappingProfile = normalizeText(profile.mappingProfile);
165+
const controllerId = normalizeText(profile.controllerId);
166+
const id = normalizeText(profile.id) || mappingKeyFromText(`${controllerId}-${mappingProfile}-${index + 1}`);
167+
const previous = existingRows.get(id);
168+
return {
169+
actions: normalizeList(profile.actions),
170+
controllerId,
171+
controllerName: normalizeText(profile.controllerName),
172+
createdAt: previous?.createdAt || timestamp,
173+
createdBy: previous?.createdBy || userKey,
174+
deviceType: normalizeText(profile.deviceType) || "Gamepad",
175+
id,
176+
inputs: normalizeList(profile.inputs),
177+
key: previous?.key,
178+
mappingProfile,
179+
projectId: targetProjectId,
180+
recordOrder: index + 1,
181+
updatedAt: timestamp,
182+
updatedBy: userKey,
183+
};
184+
});
185+
186+
tables.input_controller_profile_records = [
187+
...(tables.input_controller_profile_records || []).filter(
188+
(record) => normalizeText(record.projectId) !== targetProjectId,
189+
),
190+
...nextRows,
191+
];
192+
persistTables();
193+
return {
194+
profiles: listControllerProfiles(targetProjectId),
195+
saved: true,
196+
snapshot: getSnapshot(),
197+
};
198+
}
199+
111200
function replaceMappings(mappings = [], projectId = "") {
112201
const targetProjectId = normalizeText(projectId) || activeProjectId();
113202
const existingRows = new Map(
@@ -134,6 +223,8 @@ export function createInputMappingToolMockRepository(options = {}) {
134223
inputDevice: normalizeText(mapping.inputDevice),
135224
key: previous?.key,
136225
label: normalizeText(mapping.label),
226+
controllerProfileId: normalizeText(mapping.controllerProfileId),
227+
mappingProfile: normalizeText(mapping.mappingProfile),
137228
objectKey,
138229
objectName: normalizeText(mapping.objectName) || "Global",
139230
projectId: targetProjectId,
@@ -179,6 +270,7 @@ export function createInputMappingToolMockRepository(options = {}) {
179270
function getSnapshot() {
180271
const normalizedTables = getTables();
181272
return {
273+
controllerProfiles: listControllerProfiles(),
182274
mappings: listMappings(),
183275
tableCounts: tableCounts(normalizedTables),
184276
tables: normalizedTables,
@@ -189,7 +281,9 @@ export function createInputMappingToolMockRepository(options = {}) {
189281
INPUT_MAPPING_TOOL_TABLES,
190282
getSnapshot,
191283
getTables,
284+
listControllerProfiles,
192285
listMappings,
286+
replaceControllerProfiles,
193287
replaceMappings,
194288
resetMappings,
195289
};

0 commit comments

Comments
 (0)