Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions src/view/frontend/web/js/inspector/accessibility.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,16 @@ export const accessibilityMethods = {
);
}

// Alt Text (for images)
// Alt Text (for images): false = attribute missing, "" = decorative image
if (a11yData.altText !== null) {
const altStatus = a11yData.altText ? a11yData.altText : "⚠️ Missing";
const altColor = a11yData.altText ? "#34d399" : "#ef4444";
let altStatus = a11yData.altText;
let altColor = "#34d399";
if (a11yData.altText === false) {
altStatus = "⚠️ Missing";
altColor = "#ef4444";
} else if (a11yData.altText === "") {
altStatus = "Empty (decorative)";
}
container.appendChild(
this.createInfoSection("Alt Text", altStatus, altColor),
);
Expand Down Expand Up @@ -141,9 +147,14 @@ export const accessibilityMethods = {

/**
* Get alt text for images
*
* @returns {string|false|null} The alt value ("" for decorative images),
* false when an image has no alt attribute, null for non-images.
*/
getAltText(element, tagName) {
return tagName === "img" ? element.getAttribute("alt") : null;
if (tagName !== "img") return null;

return element.hasAttribute("alt") ? element.getAttribute("alt") : false;
},

/**
Expand Down
20 changes: 11 additions & 9 deletions src/view/frontend/web/js/inspector/picker.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,15 @@ export const pickerMethods = {
* Toggle inspector on/off
*/
toggleInspector() {
this.isOpen = !this.isOpen;

// Closing also resets a pinned badge, otherwise the next open stays pinned
if (this.isOpen) {
this.activatePicker();
this.$dispatch("mageforge:inspector:opened");
} else {
this.deactivatePicker();
this.$dispatch("mageforge:inspector:closed");
this.closeInspector();
return;
}

this.isOpen = true;
this.activatePicker();
this.$dispatch("mageforge:inspector:opened");
this.updateFloatingButton();
},

Expand Down Expand Up @@ -109,7 +108,7 @@ export const pickerMethods = {
if (this.isPinned) return;

// Don't update if mouse is over the floating button
if (this.floatingButton && this.floatingButton.contains(e.target)) {
if (this._inspectorFloatButton?.contains(e.target)) {
return;
}

Expand Down Expand Up @@ -144,6 +143,8 @@ export const pickerMethods = {
if (this.highlightBox) {
this.highlightBox.style.display = "none";
}
// Forget the element so hovering it again shows the highlight again
this.hoveredElement = null;
// Badge stays visible until hovering another element
}
},
Expand All @@ -157,7 +158,8 @@ export const pickerMethods = {
// Check if click is outside badge
if (
!this.infoBadge.contains(e.target) &&
(!this.floatingButton || !this.floatingButton.contains(e.target))
(!this._inspectorFloatButton ||
!this._inspectorFloatButton.contains(e.target))
) {
this.unpinBadge();
return;
Expand Down
33 changes: 32 additions & 1 deletion src/view/frontend/web/js/shortcut-parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
/**
* @typedef {object} ParsedShortcut
* @property {string} key
* @property {string} code Physical key code (e.g. "KeyI") for single letters and digits, otherwise empty
* @property {boolean} ctrlOrMeta
* @property {boolean} shift
* @property {boolean} alt
Expand All @@ -26,6 +27,19 @@
*/
const parsedShortcutCache = new Map();

/**
* Map a single letter or digit to its physical KeyboardEvent.code.
*
* @param {string} key Lower-cased key from the shortcut string.
* @returns {string} Empty string for all other keys.
*/
function toKeyCode(key) {
if (/^[a-z]$/.test(key)) return `Key${key.toUpperCase()}`;
if (/^[0-9]$/.test(key)) return `Digit${key}`;

return "";
}

/**
* Parse a shortcut string into its components.
*
Expand All @@ -48,6 +62,7 @@ export function parseShortcut(shortcut) {

parsed = {
key,
code: toKeyCode(key),
ctrlOrMeta:
parts.includes("ctrl") ||
parts.includes("cmd") ||
Expand All @@ -73,8 +88,24 @@ export function matchesShortcut(event, shortcut) {
const parsed = parseShortcut(shortcut);
if (!parsed) return false;

// Some events (autofill, synthetic input) carry no key at all
const key = (event.key || "").toLowerCase();
if (key === "") return false;

// With Alt/Option macOS produces special characters (Option+I => "ˆ") or
// "Dead", so fall back to the physical key for those. A plain letter or
// digit is trusted as is, otherwise layouts where key and code disagree
// (key "y" on code KeyZ) would match two different shortcuts.
const isPlainKey = /^[a-z0-9]$/.test(key);
const keyMatches =
key === parsed.key ||
(parsed.alt &&
!isPlainKey &&
parsed.code !== "" &&
event.code === parsed.code);

return (
event.key.toLowerCase() === parsed.key &&
keyMatches &&
(event.ctrlKey || event.metaKey) === parsed.ctrlOrMeta &&
event.shiftKey === parsed.shift &&
event.altKey === parsed.alt
Expand Down
Loading