WebGL viewer: fix help-menu shortcut display & position (+ propose in-browser ROI drawing)#642
WebGL viewer: fix help-menu shortcut display & position (+ propose in-browser ROI drawing)#642jackgallant wants to merge 1 commit into
Conversation
The 'h' help menu had several long-standing display bugs:
- Shortcut keys were rendered with key.toUpperCase(), so a plain
lowercase binding (e.g. key:'h', key:'r', key:'s', key:'l') was shown
as an uppercase letter -- indistinguishable from the genuinely
Shift-modified bindings (key:'R'/'S'/'L' with modKeys:['shiftKey'],
which are different commands). The binding data already carries the
correct case, so render the key verbatim instead of uppercasing it.
- The modifier label was lowercased ("shift"); capitalize it ("Shift").
- #helpmenu was pinned to the left edge (left:0%), so its lower half
slid behind the lower-left legend. Center it horizontally and
vertically (left/top:50% + translate(-50%,-50%)).
- #helpmenu set no font-family, so the panel fell back to the
browser-default serif (Times in Firefox) while the rest of the UI is
sans-serif. Give it an explicit sans-serif.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request centers a UI element and updates the formatting of modifier keys in the MRI viewer. The review feedback suggests using a consistent font family (Helvetica Neue, sans-serif) in the CSS. In the JavaScript code, the reviewer recommends replacing fragile string manipulation for modifier keys with a mapping object, and warns of a potential TypeError if modKeys is undefined during wheel actions.
| var modKeys = list[i][name]['modKeys'] | ||
| modKeys = modKeys.map((modKey) => modKey.substring(0, modKey.length - 3)) | ||
| modKeys = modKeys.map((modKey) => modKey.charAt(0).toUpperCase() + modKey.substring(1, modKey.length - 3)) | ||
| modKeys = modKeys.join(' + ') | ||
| new_html += modKeys + ' + wheel </td><td>' + diplay_name + '</td></tr>' |
There was a problem hiding this comment.
If modKeys is undefined (e.g., if a wheel action does not require modifier keys), calling .map on it will throw a TypeError. We should guard against this by defaulting to an empty array, and also use a mapping object for cleaner and safer translation.
var modKeys = list[i][name]['modKeys'] || [];
const modMap = { shiftKey: 'Shift', ctrlKey: 'Ctrl', altKey: 'Alt', metaKey: 'Meta' };
modKeys = modKeys.map((modKey) => modMap[modKey] || modKey);
var modStr = modKeys.join(' + ');
new_html += (modStr ? modStr + ' + ' : '') + 'wheel </td><td>' + diplay_name + '</td></tr>'| max-width:400px; | ||
| display:none; | ||
| font-size:12pt; | ||
| font-family:Helvetica, Arial, sans-serif; |
| let modKeys = list[i][name]['modKeys']; | ||
| modKeys = modKeys.map((modKey) => modKey.substring(0, modKey.length - 3)); | ||
| modKeys = modKeys.map((modKey) => modKey.charAt(0).toUpperCase() + modKey.substring(1, modKey.length - 3)); | ||
| modKeys = modKeys.join(' + '); |
There was a problem hiding this comment.
Using string manipulation (substring and charAt) to format modifier keys is fragile and hard to maintain. If a modifier key name does not end with 'Key' or is formatted differently, this logic will break. Using a mapping object is much cleaner, safer, and more robust.
| let modKeys = list[i][name]['modKeys']; | |
| modKeys = modKeys.map((modKey) => modKey.substring(0, modKey.length - 3)); | |
| modKeys = modKeys.map((modKey) => modKey.charAt(0).toUpperCase() + modKey.substring(1, modKey.length - 3)); | |
| modKeys = modKeys.join(' + '); | |
| let modKeys = list[i][name]['modKeys']; | |
| const modMap = { shiftKey: 'Shift', ctrlKey: 'Ctrl', altKey: 'Alt', metaKey: 'Meta' }; | |
| modKeys = modKeys.map((modKey) => modMap[modKey] || modKey); | |
| modKeys = modKeys.join(' + '); |
Summary
This PR fixes several long-standing display bugs in the WebGL viewer's
hhelp menu. Separately — in this description only, no code in this PR — it also proposes upstreaming an in-browser ROI-drawing feature we've prototyped on top of the viewer.Bug fixes (ready to merge)
Small, self-contained diff in
mriview.js+mriview.css:key.toUpperCase(). The viewer binds both plain lowercase keys (r,s,l,h, …) and genuinely Shift-modified ones (key:'R'/'S'/'L'withmodKeys:['shiftKey']— these are different commands, e.g. toggle right hemisphere). Uppercasing made the two indistinguishable in the menu (both shown asR/S/L), and the help togglehwas shown asH(implying Shift+H). The binding data already carries the correct case, so we now render the key verbatim.shiftKeynow renders asShiftinstead ofshift.#helpmenuwas pinned to the left edge (left:0%), so its lower half slid behind the lower-left legend. It's now centered (left/top:50%+translate(-50%,-50%)).#helpmenuset nofont-family, so it fell back to the browser-default serif (Times in Firefox) while the rest of the UI is sans-serif. Now explicitly sans-serif.(Note on Firefox scroll-zoom: it's already correct on
main— the active camera controller inmovement.jsuses the standardwheel/deltaY. Only the legacyLandscapeControls.js, used bysimple.html, still reads the WebKit-onlywheelDelta, so it's intentionally left out of this PR.)Proposal: in-browser ROI drawing (discussion — no code here)
We've built a working prototype that adds ROI drawing + editing + export directly in the WebGL viewer:
It's structured as a self-contained drop-in bundle — pure
core/geometry (unit-tested), a thinViewerAdapterthat quarantines every pycortex-specific quirk (flat-offset, pivot matrices, SVG overlay,data-ptidxconvention), and host-agnosticui/. It attaches to a static or dynamic pycortex viewer with two<script>tags and no per-viewer edits.Live demo (drawing baked into the public group-stories viewer):
👉 https://gallantlab.org/viewer-stories-group-roidraw/
(Display/Draw toggle at the top → Draw → drag to lasso a region → name it → it's drawn on the surface; Export/Import live in the panel. Tested in Firefox.)
Would the maintainers be interested in incorporating this upstream so pycortex ships ROI drawing out of the box? If so I'm glad to open a follow-up PR with the implementation and wire it into the viewer build. A few design points we'd want to align on first:
cortex/webgl/resources/🤖 Generated with Claude Code