From 851e542b207e5635d4a8daff531d1ca868344bf2 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Thu, 18 Dec 2025 20:03:33 -0500 Subject: [PATCH] ElementHighlighter: Remove Dupe --- .../elementHighlighter/index.tsx | 463 ------------------ .../elementHighlighter/styles.css | 104 ---- 2 files changed, 567 deletions(-) delete mode 100644 src/equicordplugins/elementHighlighter/index.tsx delete mode 100644 src/equicordplugins/elementHighlighter/styles.css diff --git a/src/equicordplugins/elementHighlighter/index.tsx b/src/equicordplugins/elementHighlighter/index.tsx deleted file mode 100644 index abbf65abc..000000000 --- a/src/equicordplugins/elementHighlighter/index.tsx +++ /dev/null @@ -1,463 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -import "./styles.css"; - -import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; -import { Button } from "@components/Button"; -import { EquicordDevs, IS_MAC } from "@utils/constants"; -import { copyWithToast } from "@utils/discord"; -import definePlugin, { OptionType } from "@utils/types"; -import { findComponentByCodeLazy } from "@webpack"; -import { useEffect, useState } from "@webpack/common"; - -const KeybindShortcut = findComponentByCodeLazy(".combo,", ".key,"); - -const cl = classNameFactory("vc-element-highlight-"); - -const DEFAULT_KEYBIND = ["mod", "shift", "y"]; - -let active = false; -let overlay: HTMLDivElement | null = null; -let tooltip: HTMLDivElement | null = null; -let lastTarget: Element | null = null; -let pendingFrame = 0; -let pendingX = 0; -let pendingY = 0; -let pendingElement: Element | null = null; - -const colorCache = new WeakMap(); -let cachedRules: { selector: string; specificity: number; color: string }[] = []; - -function KeybindRecorder() { - const [isListening, setIsListening] = useState(false); - const currentKeybind = settings.use(["keybind"]).keybind; - - useEffect(() => { - if (!isListening) return; - - const handleKeyDown = (event: KeyboardEvent) => { - event.preventDefault(); - event.stopPropagation(); - - if (["Control", "Shift", "Alt", "Meta"].includes(event.key)) return; - - const keys: string[] = []; - if (event.metaKey) keys.push("meta"); - if (event.ctrlKey) keys.push(IS_MAC ? "control" : "mod"); - if (event.shiftKey) keys.push("shift"); - if (event.altKey) keys.push("alt"); - - let mainKey = event.key.toLowerCase(); - if (mainKey === " ") mainKey = "space"; - if (mainKey === "escape") mainKey = "esc"; - - keys.push(mainKey); - settings.store.keybind = keys; - setIsListening(false); - }; - - const handleBlur = () => setIsListening(false); - - document.addEventListener("keydown", handleKeyDown, true); - window.addEventListener("blur", handleBlur); - - return () => { - document.removeEventListener("keydown", handleKeyDown, true); - window.removeEventListener("blur", handleBlur); - }; - }, [isListening]); - - return ( -
- - -
- ); -} - -const settings = definePluginSettings({ - keybind: { - description: "Toggle Highlighter", - type: OptionType.COMPONENT, - default: DEFAULT_KEYBIND, - component: KeybindRecorder - }, - showClasses: { - description: "Display the element's CSS class names in the tooltip.", - type: OptionType.BOOLEAN, - default: false - }, - showId: { - description: "Display the element's ID attribute in the tooltip.", - type: OptionType.BOOLEAN, - default: false - }, - showFont: { - description: "Display the computed font family and font size.", - type: OptionType.BOOLEAN, - default: false - }, - showPadding: { - description: "Display the element's padding values.", - type: OptionType.BOOLEAN, - default: false - }, - showMargin: { - description: "Display the element's margin values.", - type: OptionType.BOOLEAN, - default: false - }, - showBorderRadius: { - description: "Display the element's border-radius values.", - type: OptionType.BOOLEAN, - default: false - }, - showPosition: { - description: "Display the element's CSS position type and z-index.", - type: OptionType.BOOLEAN, - default: false - }, - showDisplay: { - description: "Display the element's display type along with flex or grid properties.", - type: OptionType.BOOLEAN, - default: false - } -}); - -function calcSpecificity(sel: string): number { - const ids = (sel.match(/#[\w-]+/g) ?? []).length; - const classes = (sel.match(/\.[\w-]+/g) ?? []).length + (sel.match(/\[[\w-]+/g) ?? []).length; - const tags = (sel.match(/(^|[\s>+~])[\w-]+/g) ?? []).length; - return ids * 10000 + classes * 100 + tags; -} - -function cacheRules() { - cachedRules = []; - for (const sheet of document.styleSheets) { - try { - for (const rule of sheet.cssRules) { - if (!(rule instanceof CSSStyleRule)) continue; - const color = rule.style.getPropertyValue("color"); - if (!color) continue; - - for (const sel of rule.selectorText.split(",")) { - const s = sel.trim(); - cachedRules.push({ selector: s, specificity: calcSpecificity(s), color }); - } - } - } catch { } - } - cachedRules.sort((a, b) => a.specificity - b.specificity); -} - -function getColorVarFromElement(el: Element): string | null { - if (el instanceof HTMLElement) { - const inline = el.style.getPropertyValue("color"); - if (inline) return inline; - } - - const styleAttr = el.getAttribute("style"); - if (styleAttr) { - const match = styleAttr.match(/color\s*:\s*([^;]+)/); - if (match) return match[1].trim(); - } - - let result: string | null = null; - for (const r of cachedRules) { - try { - if (el.matches(r.selector)) result = r.color; - } catch { } - } - return result; -} - -function getColorVar(el: Element): string | null { - const cached = colorCache.get(el); - if (cached !== undefined) return cached; - - let current: Element | null = el; - while (current) { - const color = getColorVarFromElement(current); - if (color) { - colorCache.set(el, color); - return color; - } - current = current.parentElement; - } - colorCache.set(el, null); - return null; -} - -function rgbToHex(rgb: string): string { - const match = rgb.match(/\d+/g); - if (!match || match.length < 3) return rgb; - const [r, g, b] = match.map(Number); - return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`; -} - -function formatBoxValue(value: string): string | null { - if (value === "0px" || value === "0px 0px 0px 0px") return null; - return value; -} - -function buildTooltipContent(el: Element, computed: CSSStyleDeclaration, rect: DOMRect): string { - const tag = el.tagName.toLowerCase(); - const colorVar = getColorVar(el) ?? computed.color; - const hex = rgbToHex(computed.color); - - let html = `<${tag}> `; - html += `${Math.round(rect.width)}x${Math.round(rect.height)}`; - html += `
${colorVar}
`; - html += `
${hex}
`; - - const { store } = settings; - - if (store.showId) { - const { id } = el; - if (id) html += `
id: #${id}
`; - } - - if (store.showClasses) { - const classes = el.className; - if (classes && typeof classes === "string") { - const truncated = classes.length > 60 ? classes.slice(0, 60) + "…" : classes; - html += `
class: ${truncated}
`; - } - } - - if (store.showFont) { - const font = computed.fontFamily.split(",")[0].replace(/["']/g, ""); - const size = computed.fontSize; - html += `
font: ${font} ${size}
`; - } - - if (store.showPadding) { - const padding = formatBoxValue(computed.padding); - if (padding) html += `
padding: ${padding}
`; - } - - if (store.showMargin) { - const margin = formatBoxValue(computed.margin); - if (margin) html += `
margin: ${margin}
`; - } - - if (store.showBorderRadius) { - const radius = formatBoxValue(computed.borderRadius); - if (radius) html += `
radius: ${radius}
`; - } - - if (store.showPosition) { - const pos = computed.position; - if (pos !== "static") { - const zIndex = computed.zIndex !== "auto" ? ` z:${computed.zIndex}` : ""; - html += `
position: ${pos}${zIndex}
`; - } - } - - if (store.showDisplay) { - const { display } = computed; - let extra = ""; - if (display === "flex" || display === "inline-flex") { - extra = ` ${computed.flexDirection} gap:${computed.gap}`; - } else if (display === "grid" || display === "inline-grid") { - extra = ` gap:${computed.gap}`; - } - html += `
display: ${display}${extra}
`; - } - - return html; -} - -function updateFrame() { - pendingFrame = 0; - const el = pendingElement; - if (!el || !overlay || !tooltip) return; - - const rect = el.getBoundingClientRect(); - - overlay.style.transform = `translate3d(${rect.left}px,${rect.top}px,0)`; - overlay.style.width = `${rect.width}px`; - overlay.style.height = `${rect.height}px`; - - if (el !== lastTarget) { - lastTarget = el; - const computed = getComputedStyle(el); - tooltip.innerHTML = buildTooltipContent(el, computed, rect); - } - - positionTooltip(pendingX, pendingY); -} - -function positionTooltip(x: number, y: number) { - if (!tooltip) return; - - const tw = tooltip.offsetWidth; - const th = tooltip.offsetHeight; - const margin = 15; - const edge = 10; - const vw = window.innerWidth; - const vh = window.innerHeight; - - let left = x + margin; - let top = y + margin; - - if (left + tw + edge > vw) { - left = x - margin - tw; - if (left < edge) left = Math.max(edge, vw - tw - edge); - } - - if (top + th + edge > vh) { - top = y - margin - th; - if (top < edge) top = Math.max(edge, vh - th - edge); - } - - tooltip.style.transform = `translate3d(${left}px,${top}px,0)`; -} - -function scheduleFrame(el: Element, x: number, y: number) { - pendingElement = el; - pendingX = x; - pendingY = y; - if (!pendingFrame) { - pendingFrame = requestAnimationFrame(updateFrame); - } -} - -function onMouseMove(e: MouseEvent) { - const el = document.elementFromPoint(e.clientX, e.clientY); - if (!el || el === overlay || el === tooltip || tooltip?.contains(el)) return; - scheduleFrame(el, e.clientX, e.clientY); -} - -function onClick(e: MouseEvent) { - e.preventDefault(); - e.stopPropagation(); - - const el = document.elementFromPoint(e.clientX, e.clientY); - if (!el || el === overlay || el === tooltip) return; - - const colorVar = getColorVar(el); - if (colorVar) copyWithToast(colorVar, "Copied color to clipboard!"); -} - -function onKeyDown(e: KeyboardEvent) { - if (e.key === "Escape") disable(); -} - -function enable() { - active = true; - cacheRules(); - - overlay = document.createElement("div"); - overlay.className = cl("overlay"); - document.body.appendChild(overlay); - - tooltip = document.createElement("div"); - tooltip.className = cl("tooltip"); - document.body.appendChild(tooltip); - - document.addEventListener("mousemove", onMouseMove, { capture: true, passive: true }); - document.addEventListener("click", onClick, true); - document.addEventListener("keydown", onKeyDown); - document.body.classList.add(cl("active")); -} - -function disable() { - active = false; - lastTarget = null; - pendingElement = null; - - if (pendingFrame) { - cancelAnimationFrame(pendingFrame); - pendingFrame = 0; - } - - overlay?.remove(); - overlay = null; - tooltip?.remove(); - tooltip = null; - - document.removeEventListener("mousemove", onMouseMove, true); - document.removeEventListener("click", onClick, true); - document.removeEventListener("keydown", onKeyDown); - document.body.classList.remove(cl("active")); -} - -function getConfiguredKeybind(): string[] { - const raw = settings.store.keybind; - if (Array.isArray(raw) && raw.length > 0) return raw; - return DEFAULT_KEYBIND; -} - -function matchesKeybind(e: KeyboardEvent): boolean { - const keybind = getConfiguredKeybind(); - const pressed = e.key.toLowerCase(); - const code = e.code.toLowerCase().replace("key", "").replace("digit", ""); - - let hasNonModifier = false; - for (const key of keybind) { - switch (key) { - case "mod": - if (!(IS_MAC ? e.metaKey : e.ctrlKey)) return false; - break; - case "control": - case "ctrl": - if (!e.ctrlKey) return false; - break; - case "meta": - case "cmd": - if (!e.metaKey) return false; - break; - case "shift": - if (!e.shiftKey) return false; - break; - case "alt": - if (!e.altKey) return false; - break; - default: - hasNonModifier = true; - if (pressed !== key && code !== key) return false; - } - } - return hasNonModifier; -} - -function onToggle(e: KeyboardEvent) { - if (matchesKeybind(e)) { - e.preventDefault(); - active ? disable() : enable(); - } -} - -export default definePlugin({ - name: "ElementHighlighter", - description: "Highlight and inspect elements easily.", - authors: [EquicordDevs.Prism], - settings, - - start() { - document.addEventListener("keydown", onToggle); - }, - - stop() { - document.removeEventListener("keydown", onToggle); - if (active) disable(); - } -}); diff --git a/src/equicordplugins/elementHighlighter/styles.css b/src/equicordplugins/elementHighlighter/styles.css deleted file mode 100644 index 919933b20..000000000 --- a/src/equicordplugins/elementHighlighter/styles.css +++ /dev/null @@ -1,104 +0,0 @@ -.vc-element-highlight-active { - cursor: crosshair !important; -} - -.vc-element-highlight-overlay { - position: fixed; - top: 0; - left: 0; - z-index: 999998; - border: 2px solid var(--text-brand); - background: var(--brand-15a); - border-radius: 4px; - pointer-events: none; - will-change: transform, width, height; - contain: layout style; -} - -.vc-element-highlight-tooltip { - position: fixed; - top: 0; - left: 0; - z-index: 999999; - background: rgb(0 0 0 / 70%); - backdrop-filter: blur(12px); - border: 1px solid rgb(255 255 255 / 10%); - border-radius: 6px; - padding: 6px 10px; - font-family: var(--font-code); - font-size: 12px; - line-height: 1.4; - color: var(--text-default); - pointer-events: none; - white-space: nowrap; - will-change: transform; - contain: layout style; -} - -.vc-element-highlight-tag { - color: var(--text-muted); -} - -.vc-element-highlight-size { - color: var(--text-muted); - margin-left: 6px; -} - -.vc-element-highlight-color { - margin-top: 4px; -} - -.vc-element-highlight-swatch { - display: inline-block; - width: 10px; - height: 10px; - border-radius: 2px; - border: 1px solid rgb(255 255 255 / 20%); - vertical-align: middle; - margin-right: 6px; - background: - linear-gradient(var(--c), var(--c)), - repeating-conic-gradient(rgb(255 255 255 / 20%) 0% 25%, transparent 0% 50%) 0 0 / 4px 4px; -} - -.vc-element-highlight-hex { - color: var(--text-muted); - font-size: 11px; - margin-top: 2px; -} - -.vc-element-highlight-info { - color: var(--text-muted); - font-size: 11px; - margin-top: 2px; - max-width: 300px; - overflow: hidden; - text-overflow: ellipsis; -} - -.vc-element-highlight-label { - color: var(--text-subtle); -} - -.vc-element-highlight-keybind-row { - display: flex; - flex-direction: row; - align-items: center; - gap: 8px; -} - -.vc-element-highlight-keybind-button { - display: flex; - align-items: center; - background: transparent; - border: none; - padding: 0; - cursor: pointer; -} - -.vc-element-highlight-keybind-button.vc-element-highlight-listening { - padding: 4px 8px; - background-color: var(--input-background); - border-radius: 4px; - color: var(--text-muted); -}