From a7ab2d6cc81658db2c5fa28356347b1387f8a2df Mon Sep 17 00:00:00 2001 From: NuklearRabbit <145918611+NuklearRabbit@users.noreply.github.com> Date: Wed, 29 Jul 2026 16:22:46 +0200 Subject: [PATCH] fix: harden responsive accessibility contracts --- SOURCE_MANIFEST.txt | 6 +++--- src/renderer/app.js | 14 ++++++++++++-- src/renderer/styles.css | 8 ++++++++ tests/renderer-workflow.test.mjs | 10 ++++++++++ 4 files changed, 33 insertions(+), 5 deletions(-) diff --git a/SOURCE_MANIFEST.txt b/SOURCE_MANIFEST.txt index 0077f96..2dfed9e 100644 --- a/SOURCE_MANIFEST.txt +++ b/SOURCE_MANIFEST.txt @@ -120,14 +120,14 @@ e89b54e7e3174b4b0a1dcd9058d8344e29431f9d16d0e6bb8d11559b691440a0 2508 afef3841a3948b2121f8fba809aae4ea3da71bd2fda86973ba50200a5b1f89b2 14894 src/main/ssh-service.cjs 80426e8bdd7259c6ed47d1acb9f6cbbbe4b1075f74c8ce5fd759044d9af09d59 149912 src/main/unraid-deployment-service.cjs b654a9e45044ad32c61fabe4a6d897288615ec83739b53e3241ff881e32f56bd 21677 src/main/update-service.cjs -7c0ed496051f33a65312f7acf132eb288ca8260ce4b0b1d4b6f607c1cfac4071 240362 src/renderer/app.js +953b027fedcee2c2396ec5c8cf938013f2630d8ef76ab5e917a02d723303234f 241018 src/renderer/app.js 16efd2fca83004f781eae40ae0f706a004ce0bddf338dd087b8adf7eb10c1d84 85704 src/renderer/assets/itworx-mark.png 813b8cdeecac43794166f3db9d3c5d2c441e0292f9ab7bd465ba136d6201e95d 82476 src/renderer/assets/itworx-wordmark-dark.png 094c1b71cc2482a9db250ac175f45f3de68f53277dfbde371a03e61923d00988 75240 src/renderer/assets/itworx-wordmark-light.png 813b8cdeecac43794166f3db9d3c5d2c441e0292f9ab7bd465ba136d6201e95d 82476 src/renderer/assets/itworx-wordmark.png fb7ed47f9aac50d9259d7d3c3bb2010c7bfdd2fe8e8e47ca2744bb22f0057d54 830 src/renderer/index.html ae8d6778614077c5f46032035a10fb9803219c14e8bfabefacf1affdec06fb71 62802 src/renderer/mock-bridge.js -34ee56ed08dcd1c2295985b9bb419b981598a2699ee8e65394d86f4177f4797b 77647 src/renderer/styles.css +45692591428575b518678a6b548c25d3de95f568541e5648b0f06f42c48b5bbf 77872 src/renderer/styles.css 0a1e9d9d6cd4d190eb7f85dbc6668d80600b1cf2749cc0c2c51cc428f506f20d 1121 src/shared/clone-target.cjs 5d425d5c2f939d0f6beebee7ebb0c77146cb7e318535ba7286ec7081a4dc2269 2497 src/shared/deployment-policy.cjs 029e600229714d033c28e2dcb77817aa8269847001782ae0012960e83ffd183f 3057 src/shared/git-status.cjs @@ -156,7 +156,7 @@ e7aebcc0d484a6a59d463d5cb26c11b3ad56e28f6535e7c38a0fe166a41565ea 13690 caf98cbd9de9b119dae610ee53fa333a7a11214f34762247452fbb85e8bbf725 2392 tests/log-redaction.test.mjs 96432a97d313f331694900bf0a2c21e38c20eac96d59147977aeed9055a9e3ad 2287 tests/partial-staging.test.mjs c0f8f5a3784835f19d9ff1015185ccb385840b6fa1c9ec19f233393a7d952b65 3718 tests/preflight.test.mjs -15e05220fd282895b02ea52ceaa38327a61297af8db1484e6d9964e8b03a8fea 9156 tests/renderer-workflow.test.mjs +69a86fd630425227bf1e1881fe9b2c8db39a694310948a33cd4074c801326c92 9807 tests/renderer-workflow.test.mjs 2b4956fa4df4624a04117737e57ba74020564330ff71303b5746d8ccc881e880 854 tests/repository-matching.test.mjs f679072548554a64974f0452337ce5e7b0c567343c287223770cc0974b905348 1068 tests/repository-monitor.test.mjs 75b5b83836c75675bb9a48fe4363fcb8a24fc425e6af6f822d7955c6f3c79eac 2265 tests/repository-service.test.mjs diff --git a/src/renderer/app.js b/src/renderer/app.js index 8d421bb..96b04c8 100644 --- a/src/renderer/app.js +++ b/src/renderer/app.js @@ -686,7 +686,7 @@ function renderTitlebar() { return `
ForgeFlowby ITWorx.tech
${escapeHtml(title)}
- +
${icon("search")}
${connected ? escapeHtml(user?.login || "Gitea") : "Offline"} @@ -1208,7 +1208,7 @@ function renderServerInventory() { const candidate = linked ? `Linked to ${workload.link?.repositoryFullName || "repository"}` : topCandidate - ? `${topCandidate.repositoryFullName} suggested · ${topCandidate.confidence}` + ? `${topCandidate.repositoryFullName} suggested · ${topCandidate.confidence || topCandidate.status || "review required"}` : "No repository candidate; select one manually"; const canQuickLink = !linked && topCandidate && ["exact", "strong"].includes(topCandidate.confidence) && Boolean(workload.remoteFolderCandidate); const linkButton = canQuickLink @@ -1618,6 +1618,16 @@ function enhanceRenderedUi() { `

Operational audit log

Append-only release, pull-request and recovery events
${ui.auditEvents.length ? `${ui.auditEvents.map((item) => ``).join("")}
TimeEventRepositoryResult
${formatDate(item.timestamp)}${escapeHtml(item.event)}${escapeHtml(item.details?.repository || "—")}${escapeHtml(item.details?.result || item.details?.note || "—")}
` : '

Load the operational audit log.

'}
`, ); } + document.querySelectorAll("button.icon-button:not([aria-label])").forEach((button) => { + const action = String(button.title || button.dataset.action || "Action").replaceAll("-", " "); + button.setAttribute("aria-label", action.charAt(0).toUpperCase() + action.slice(1)); + }); + document.querySelectorAll(".field > label:not([for])").forEach((label, index) => { + const control = label.parentElement?.querySelector("input, select, textarea"); + if (!control) return; + if (!control.id) control.id = `forgeflow-field-${index}`; + label.htmlFor = control.id; + }); } function render() { diff --git a/src/renderer/styles.css b/src/renderer/styles.css index 5e20659..de9add7 100644 --- a/src/renderer/styles.css +++ b/src/renderer/styles.css @@ -2818,6 +2818,14 @@ kbd { color: var(--text-faint); font-size: 10px; } +.action-panel-body .panel-callout > h2, +.action-panel-body .panel-callout > p { + display: none; +} +@media (max-height: 760px) { + .palette-backdrop { padding-top: 3vh; } + .palette-list { max-height: calc(100vh - 150px); } +} .discovery-progress { min-height: 260px; display: grid; diff --git a/tests/renderer-workflow.test.mjs b/tests/renderer-workflow.test.mjs index 5d4c875..0e5faf0 100644 --- a/tests/renderer-workflow.test.mjs +++ b/tests/renderer-workflow.test.mjs @@ -265,3 +265,13 @@ test("Git Validator exposes scored best-practice checks and bounded repairs", as assert.match(preload, /gitValidatorScan/); assert.match(preload, /gitValidatorRepair/); }); + +test("renderer guards accessible names, labels and uncertain inventory evidence", async () => { + const renderer = await readFile(new URL("../src/renderer/app.js", import.meta.url), "utf8"); + const styles = await readFile(new URL("../src/renderer/styles.css", import.meta.url), "utf8"); + assert.match(renderer, /button\.icon-button:not\(\[aria-label\]\)/); + assert.match(renderer, /\.field > label:not\(\[for\]\)/); + assert.match(renderer, /topCandidate\.confidence \|\| topCandidate\.status \|\| "review required"/); + assert.match(styles, /\.action-panel-body \.panel-callout > h2/); + assert.match(styles, /@media \(max-height: 760px\)/); +});