diff --git a/packages/astro/src/runtime/client/dev-overlay/plugins/xray.ts b/packages/astro/src/runtime/client/dev-overlay/plugins/xray.ts
index a8cd84a3ae..af60db62ec 100644
--- a/packages/astro/src/runtime/client/dev-overlay/plugins/xray.ts
+++ b/packages/astro/src/runtime/client/dev-overlay/plugins/xray.ts
@@ -11,17 +11,115 @@ import { createWindowElement } from './utils/window.js';
const icon =
'';
+interface SourceInfo {
+ file: string;
+ loc?: string;
+}
+
+let altClickManager: AltClickManager | undefined = undefined;
+
export default {
id: 'astro:xray',
name: 'Inspect',
icon: icon,
init(canvas) {
let islandsOverlays: { highlightElement: DevOverlayHighlight; island: HTMLElement }[] = [];
+ let elementToSourceMap = new WeakMap();
+ altClickManager = new AltClickManager();
+ altClickManager.addEventListener('alt:hover', () => {
+
+ })
+ altClickManager.addEventListener('alt:click', () => {
+
+ })
+
+ afterSwap();
+ document.addEventListener('astro:after-swap', afterSwap);
+ document.addEventListener('astro:page-load', pageLoad);
- addIslandsOverlay();
+ function afterSwap() {
+ addIslandsOverlay();
+ removeMetadata();
+ }
- document.addEventListener('astro:after-swap', addIslandsOverlay);
- document.addEventListener('astro:page-load', refreshIslandsOverlayPositions);
+ function pageLoad() {
+ refreshIslandsOverlayPositions()
+ }
+
+ let altHoverHighlight: DevOverlayHighlight | undefined;
+ function stopAltHover() {
+ if (altHoverHighlight) {
+ altHoverHighlight.remove();
+ altHoverHighlight = undefined;
+ }
+ }
+ function onAltHover(element: HTMLElement | null) {
+ stopAltHover()
+ if (!element) return;
+ const rect = element.getBoundingClientRect();
+ altHoverHighlight = createHighlight(rect);
+ altHoverHighlight.style.setProperty('pointer-events', 'none');
+ canvas.append(altHoverHighlight);
+ }
+
+ // document.addEventListener('keydown', (evt) => {
+ // if (altKeyActive) {
+ // altKeyActive = false;
+ // }
+ // altKeyActive = evt.altKey;
+ // if (altKeyActive) {
+ // onAltHover(mouseoverElement);
+ // } else {
+ // stopAltHover()
+ // }
+ // })
+ // document.addEventListener('keyup', (evt) => {
+ // if (!altKeyActive) return;
+ // altKeyActive = evt.altKey;
+ // if (!altKeyActive) {
+ // stopAltHover()
+ // }
+ // })
+ // document.addEventListener('mousemove', (evt) => {
+ // if (!altKeyActive) {
+ // stopAltHover()
+ // mouseoverElement = evt.target as HTMLElement;
+ // return;
+ // }
+ // if (evt.target !== mouseoverElement) {
+ // mouseoverElement = evt.target as HTMLElement;
+ // onAltHover(mouseoverElement)
+ // }
+ // })
+ // document.addEventListener('click', (evt) => {
+ // if (!altKeyActive) return;
+ // if (!mouseoverElement) return;
+ // if (!(evt.target instanceof Node)) return;
+ // if (!mouseoverElement.contains(evt.target)) return;
+ // const info = getSourceInfo(mouseoverElement);
+ // const selection = document.getSelection();
+ // console.log('click to open?', { ...info, selectionAnchorOffset: selection?.anchorOffset });
+
+ // })
+
+ function getSourceInfo(target: EventTarget): SourceInfo | undefined {
+ if (!(target instanceof HTMLElement)) return;
+ const info = elementToSourceMap.get(target);
+ return info;
+ }
+
+ function removeMetadata() {
+ const elements = document.querySelectorAll('[data-astro-source-file]');
+ for (const el of elements) {
+ if (elementToSourceMap.has(el)) continue;
+ const { astroSourceFile: file, astroSourceLoc: loc } = el.dataset;
+ if (file && loc) {
+ elementToSourceMap.set(el, { file, loc })
+ delete el.dataset['astroSourceFile'];
+ delete el.dataset['astroSourceLoc'];
+ }
+ }
+ }
function addIslandsOverlay() {
islandsOverlays.forEach(({ highlightElement }) => {
@@ -164,3 +262,60 @@ export default {
}
},
} satisfies DevOverlayPlugin;
+
+class AltClickManager extends EventTarget {
+ static events = ['keydown', 'keyup', 'mousemove', 'click'];
+
+ target: EventTarget | null = null;
+ #keys = new Set()
+ get altKey() {
+ return this.#keys.has('Alt') && this.#keys.size === 1;
+ }
+
+ constructor() {
+ super();
+ this.#addEventListeners();
+ }
+
+ teardown() {
+ this.#removeEventListeners();
+ }
+
+ handleEvent(event: Event): void {
+ if (isMouseEvent(event)) {
+ this.target = event.target;
+ return;
+ }
+ if (isKeyboardEvent(event)) {
+ const prev = this.altKey;
+ const fn = event.type === 'keydown' ? 'add' : 'delete';
+ this.#keys[fn](event.key);
+ if (prev !== this.altKey) {
+ console.log(this.altKey);
+ }
+ return;
+ }
+ if (event.type === 'click') {
+ return;
+ }
+ }
+
+ #addEventListeners() {
+ for (const event of AltClickManager.events) {
+ document.addEventListener(event, this);
+ }
+ }
+
+ #removeEventListeners() {
+ for (const event of AltClickManager.events) {
+ document.removeEventListener(event, this);
+ }
+ }
+}
+
+function isMouseEvent(event: Event): event is MouseEvent {
+ return event.type === 'mousemove';
+}
+function isKeyboardEvent(event: Event): event is KeyboardEvent {
+ return event.type === 'keydown' || event.type === 'keyup';
+}