Skip to content

(ui): shared control styles, Touched list above its editor, middle-truncated file paths (#467) - #468

Merged
devsuitup merged 3 commits into
mainfrom
feat/467-touched-split-layout
Oct 4, 2026
Merged

devsuitup merged 3 commits into
mainfrom
feat/467-touched-split-layout

Conversation

@devsuitup

Copy link
Copy Markdown
Owner

(file panel): Keep Touched visible above its editor and preserve header filenames

Touched now keeps its file list above the shared Changes editor and splitter. Close and editor Escape restore the full-height list; row switching uses the existing dirty prompt, including edits made while the next file is being read. Changes keeps its Back behavior, and each list retains its own unsaved-buffer stash.

The sort control shares the unchanged Settings select style. The explanation sits behind an info button in a modal with Escape, outside-click and close-button dismissal, focus return and Escape isolation. All file/diff headers share a shrinking path head and a fixed filename tail, with the complete path available on hover. The Touched panel header remains Touched files.

Validation

  • Related tests: python .work-files/check-related.py expands to node --test over all 27 touched, changes, file-panel, settings, theme and viewer test files. Final result: 833 tests, 829 passed, 2 failed, 2 skipped; exit 1. Output: .work-files/related-tests.log.
  • The two failures are in unchanged Git fixture tests: git-changes-file-real-git.test.js:732 cannot create the custom Git-directory HEAD lock, and git-changes-runner-real-git.test.js:512 cannot create the linked worktree .git path (Permission denied). Their runtime modules are also unchanged. The sandbox restrictions remain unresolved; the suite is not fully green.
  • ESLint on every changed JavaScript file plus the new header test: exit 0, 0 errors, 11 existing warnings. Output: .work-files/eslint.log.
  • git diff --check: exit 0.
  • Local tests were written before implementation and reproduced the missing modal, list layout, row-switch prompt, sort behavior and path structure. Local RED evidence: .work-files/red-touched.log, .work-files/red-path.log, .work-files/red-pending-switch.log. The Settings fixture was corrected before its class mutation was verified.
  • 30/30 targeted mutations detected by assertion failures; source snapshots restored after each mutation. Reproduce with python .work-files/mutations-467.py. Details: .work-files/mutations.json.
  • E2E selectors and list/editor geometry assertions updated in e2e/changes.spec.js and e2e/theme-controls.spec.js; journeys not run. Electron was not launched. Visual width behavior, modal appearance and actual splitter geometry were not verified live.
  • CI was not run: this brief prohibits network access. Required CI RED/GREEN evidence is not available. jsdom exercises shipped renderer files with external IPC/editor edges stubbed; it does not measure rendered widths.

Mutation guards

Each row is one injected mutation and the targeted test that rejected it. Every command uses node --test --test-name-pattern=<pattern> <file> and is bounded to 180 seconds.

Guard Injected mutation Test pattern Result
Touched select class public/touched-files-view.js: sort.className = 'control-select'; -> sort.className = ''; Touched uses the shared select style in test/dom-file-panel-touched.test.js Detected, exit 1
Settings select class public/settings-panel.js: settings-select control-select -> settings-select Settings selects retain in test/viewer-header-path.test.js Detected, exit 1
Info opens a modal public/touched-files-view.js: document.body.appendChild(overlay); -> button.appendChild(overlay); Touched info closes by close in test/dom-file-panel-touched.test.js Detected, exit 1
Modal Escape closes public/touched-files-view.js: if (event.key === 'Escape') { -> if (event.key === 'Never') { Touched info closes by escape in test/dom-file-panel-touched.test.js Detected, exit 1
Modal outside closes public/touched-files-view.js: if (event.target === overlay) close(); -> if (false) close(); Touched info closes by outside in test/dom-file-panel-touched.test.js Detected, exit 1
Modal close button public/touched-files-view.js: closeButton.addEventListener('click', close); -> closeButton.addEventListener('click', () => {}); Touched info closes by close in test/dom-file-panel-touched.test.js Detected, exit 1
Modal focus returns public/touched-files-view.js: button.focus(); -> document.body.focus(); Touched info closes by close in test/dom-file-panel-touched.test.js Detected, exit 1
Modal Escape isolation public/touched-files-view.js: event.stopImmediatePropagation(); -> Touched info closes by escape in test/dom-file-panel-touched.test.js Detected, exit 1
Visible upper list public/touched-files-view.js: const shown = listTab?.type === 'touched'; -> const shown = tab?.type === 'touched'; Touched keeps its list above in test/dom-file-panel-touched.test.js Detected, exit 1
Splitter drag sizing public/file-panel.js: clampChangesListHeight(startSize + delta, available) / available -> clampChangesListHeight(startSize, available) / available Touched keeps its list above in test/dom-file-panel-touched.test.js Detected, exit 1
Splitter ratio persistence public/file-panel.js: String(touched ? touchedListRatio : changesListDesiredHeight) -> String(touched ? DEFAULT_TOUCHED_LIST_RATIO : changesListDesiredHeight) Touched keeps its list above in test/dom-file-panel-touched.test.js Detected, exit 1
Restore stored ratio public/file-panel.js: if (value > 0 && value < 1) return value; -> if (false) return value; defaults on 0.6 in test/dom-file-panel-touched.test.js Detected, exit 1
Storage read fallback public/file-panel.js: } catch {} return DEFAULT_TOUCHED_LIST_RATIO; -> } catch { throw new Error("storage unavailable"); } return DEFAULT_TOUCHED_LIST_RATIO; localStorage refuses reads in test/dom-file-panel-touched.test.js Detected, exit 1
Storage write fallback public/file-panel.js: String(touched ? touchedListRatio : changesListDesiredHeight)); } catch {} -> String(touched ? touchedListRatio : changesListDesiredHeight)); } catch { throw new Error("storage unavailable"); } localStorage refuses reads in test/dom-file-panel-touched.test.js Detected, exit 1
Resize follows ratio public/file-panel.js: if (tab?.selectedFile) applyChangesListHeight(); -> if (false) applyChangesListHeight(); defaults on 0.6 in test/dom-file-panel-touched.test.js Detected, exit 1
Close restores full height public/file-panel.js: else layout.list.style.height = ''; -> else layout.list.style.height = '240px'; Touched keeps its list above in test/dom-file-panel-touched.test.js Detected, exit 1
Dirty close guard public/file-panel.js: if (!confirmDiscardChangesEdits(tab)) return false; -> if (false) return false; Touched dirty close in test/dom-file-panel-touched.test.js Detected, exit 1
Dirty Escape guard public/file-panel.js: if (!confirmDiscardChangesEdits(tab)) return false; -> if (false) return false; Touched dirty escape in test/dom-file-panel-touched.test.js Detected, exit 1
Dirty switch guard public/touched-files-view.js: if (!confirmDiscardChangesEdits(current)) return; -> if (false) return; Touched dirty switch in test/dom-file-panel-touched.test.js Detected, exit 1
Edits during row read public/touched-files-view.js: if (readChangesEditorContent(current) !== agreedContent && !confirmDiscardChangesEdits(current)) return; -> if (false) return; Touched asks again in test/dom-file-panel-touched.test.js Detected, exit 1
Clean row replacement public/touched-files-view.js: if (current.absolutePath === filePath) return; -> if (current.absolutePath) return; Touched rows can switch clean in test/dom-file-panel-touched.test.js Detected, exit 1
No Back in Touched public/file-panel.js: tab.returnList?.type !== 'touched' && -> Touched keeps its list above in test/dom-file-panel-touched.test.js Detected, exit 1
Header origin public/touched-files-view.js: title.textContent = 'Touched files'; -> title.textContent = 'Changes'; Touched keeps its list above in test/dom-file-panel-touched.test.js Detected, exit 1
Close makes no list IPC public/file-panel.js: renderPanel(currentPanelSessionId); return true; -> refreshTouched(currentPanelSessionId); renderPanel(currentPanelSessionId); return true; Touched keeps its list above in test/dom-file-panel-touched.test.js Detected, exit 1
Shared path title public/viewer-toolbar.js: el.title = value; -> el.title = ''; shared viewer path keeps in test/viewer-header-path.test.js Detected, exit 1
Shared filename tail public/viewer-toolbar.js: tail.textContent = value.slice(split); -> tail.textContent = ''; shared viewer path keeps in test/viewer-header-path.test.js Detected, exit 1
Nonshrinking filename CSS public/style.css: .viewer-path-tail { flex: 0 0 auto; -> .viewer-path-tail { flex: 1 1 auto; shared path CSS in test/viewer-header-path.test.js Detected, exit 1
Touched header helper public/file-panel.js: window.setViewerPath(changesDiffTitleEl, tab.selectedFile.path); -> changesDiffTitleEl.textContent = tab.selectedFile.path; file and diff editor headers in test/dom-file-panel-touched.test.js Detected, exit 1
Diff header helper public/file-panel.js: if (pathEl) window.setViewerPath(pathEl, tab.filePath || ''); -> if (pathEl) pathEl.textContent = tab.filePath || ''; file and diff editor headers in test/dom-file-panel-touched.test.js Detected, exit 1
ViewerPanel path helper public/viewer-toolbar.js: setPath(text) { setViewerPath(pathEl, text); } -> setPath(text) { pathEl.textContent = text; } ViewerPanel headers in test/viewer-header-path.test.js Detected, exit 1

Delivery

  • Context documentation and the Unreleased / Changed entry ending (#467) are updated.
  • Work is left uncommitted in wt/467. No stash, installation, app launch, push or external application write was performed.
  • Actual starting HEAD: 374afe85d72d497bf2c22f026112e86d3f16ffd3; the brief states 43e7d4a, but the worktree was clean at start. Branch and worktree were retained.
  • Continuity registration for this session was attempted with role implementer, but the external .harness/.sessions.lock was inaccessible. Readback reports the session is not registered; no checkpoint was created.

Harness-Claim: true
Agent: switchboard-dev
Host: ANCHOR
Base-Ref: origin/main
Base-SHA: 43e7d4a
Claimed-At: 2026-10-04T14:04:29.3153722Z
Nonce: 887f60fb3d8a3450
…uncated file paths (#467)

The Touched sort used the browser's select and its explanation took several lines. The sort now shares the Settings select style, the explanation sits behind an (i) modal, and Touched keeps its list above the open file with a resizable split instead of a Back control. Editor headers show the start and end of a path with the full path on hover.

Closes #467
@devsuitup

Copy link
Copy Markdown
Owner Author

Review round 1 at 952f3c9: nothing blocking.

The dirty prompt holds on every path tried. Checked by reading, and by 4 mutations, all caught:

  • a row switch asks, and asks again if the buffer changed during the read;
  • close and Escape ask;
  • the Touched toggle asks;
  • the per-list stash still works.

Also checked:

  • Escape in the modal does not close the file.
  • Changes behaves as on main.
  • Paths are rendered with textContent only, with the full path in the title.

CI is green on 952f3c9.

Minor fixes in progress:

  • the ResizeObserver misses panel-shell resizes, so the ratio is not reapplied;
  • Settings selects gain hover and focus styles, which is now said in the changelog;
  • a file name wider than the header is cut with no ellipsis;
  • a misplaced JSDoc block;
  • changes-view.md does not say the splitter is now shared.

…le names

Observe the Touched and Changes containers so opening the panel shell reapplies the stored ratio, let the path tail shrink with its own ellipsis, take the tail from the last non-empty segment, and note the shared Settings select styles in the changelog.
@devsuitup

Copy link
Copy Markdown
Owner Author

Review round 2 at cc08a74: approve.

The five round-1 findings are fixed:

  • the observer watches the Touched and Changes containers;
  • the changelog mentions the Settings select styles;
  • the path tail has its own ellipsis, and the head shrinks first;
  • the JSDoc block is back in place;
  • changes-view.md documents the shared splitter.

CI is green on cc08a74, and the host suite passes with 0 failures.

@devsuitup
devsuitup merged commit e056648 into main Oct 4, 2026
12 checks passed
@devsuitup
devsuitup deleted the feat/467-touched-split-layout branch October 4, 2026 15:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant