Skip to content

Instantly share code, notes, and snippets.

@2called-chaos
Last active December 23, 2024 11:42
Show Gist options
  • Save 2called-chaos/aea0ca4fec45d185ee2016b024ba22e3 to your computer and use it in GitHub Desktop.
Save 2called-chaos/aea0ca4fec45d185ee2016b024ba22e3 to your computer and use it in GitHub Desktop.
xray-rails (crude edition) / screenshot below
// Note: make sure your editor in step 2 is non-blocking (i.e. not "subl -w")
//
// 1. Enable in your config/environment/development.rb
// config.action_view.annotate_rendered_view_with_filenames = true
//
// 2. Add this to your routes.rb
// post "__xray/open", to: ->(env) {
// editor = ENV["GEM_EDITOR"] || ENV["VISUAL"] || ENV["EDITOR"] || "/usr/local/bin/subl"
// params = JSON.parse(Rack::Request.new(env).body.read)
// path = Rails.root.join(params["path"].to_s)
// `#{editor} #{path.to_s.shellescape}` if path.exist?
// [200, { "Content-Type" => "text/plain" }, [""]]
// } if Rails.env.development?
//
// 3. Import openXray, closeXray or registerXrayShortcut function
// import { registerXrayShortcut } from "./xray_rails"
// registerXrayShortcut((ev, mac) => (mac && ev.metaKey || !mac && ev.ctrlKey) && ev.shiftKey && ev.code == "KeyX")
export class Xray {
static CSS = `
#rails-xray-container {
position: fixed;
top: 0;
bottom: 0;
right: 0;
left: 0;
//background: rgba(255, 255, 0, 0.1);
z-index: 99999;
}
#rails-xray-container .rails-xray-template {
position: absolute;
background: rgba(255, 0, 0, 0.1);
}
#rails-xray-container .rails-xray-template span {
position: absolute;
opacity: 0.6;
padding: 0.1rem 0.5rem;
background: rgba(33, 33, 33, 0.6);
z-index: 9;
}
#rails-xray-container .rails-xray-template:hover {
background: rgba(255, 0, 0, 0.5);
}
#rails-xray-container .rails-xray-template:hover span {
background: rgba(33, 33, 33, 0.9);
opacity: 1;
z-index: 10;
}
`
static get isMac() { return navigator.platform.toUpperCase().indexOf('MAC') >= 0 }
constructor() {
this.addStyles()
}
addStyles() {
if(document.getElementById("rails-xray-styles")) return false
const style = document.createElement("style")
document.head.appendChild(style)
style.id = "rails-xray-styles"
style.innerHTML = this.constructor.CSS
}
computeBoundingBox(contents) {
// Edge case: the container may not physically wrap its children, for
// example if they are floated and no clearfix is present.
if(contents.length == 1 && contents[0].offsetHeight <= 0) {
return this.computeBoundingBox(Array.from(contents[0].children))
}
const boxFrame = {
top: Number.POSITIVE_INFINITY,
left: Number.POSITIVE_INFINITY,
right: Number.NEGATIVE_INFINITY,
bottom: Number.NEGATIVE_INFINITY,
}
contents.forEach(el => {
if (!el || !( el.offsetWidth || el.offsetHeight || el.getClientRects().length )) return
const frame = el.getBoundingClientRect()
const frame_right = frame.left + frame.width
const frame_bottom = frame.top + frame.height
boxFrame.top = Math.min(frame.top, boxFrame.top)
boxFrame.left = Math.min(frame.left, boxFrame.left)
boxFrame.right = Math.max(frame_right, boxFrame.right)
boxFrame.bottom = Math.max(frame_bottom, boxFrame.bottom)
})
return {
left: boxFrame.left,
top: boxFrame.top,
width: boxFrame.right - boxFrame.left,
height: boxFrame.bottom - boxFrame.top,
}
}
findRenderedViews() {
const result = []
document.querySelectorAll('*:not(iframe):not(script)').forEach(el => {
el.childNodes.forEach(node => {
if (node.nodeType !== 8 || !node.data.startsWith(' BEGIN')) return false
const id = node.data.match(/^\sBEGIN\s(.+)\s$/)[1]
let el = node.nextSibling
const res = []
while (el && !(el.nodeType === 8 && el.data === ` END ${id} `)) {
if (el.nodeType === 1 && el.tagName !== 'SCRIPT') {
res.push(el)
}
el = el.nextSibling
}
result.push([id, res])
})
})
return result
}
closeOverlay() {
document.getElementById("rails-xray-container")?.remove()
}
renderOverlay() {
this.closeOverlay()
const ctn = document.createElement("div")
ctn.id = "rails-xray-container"
this.findRenderedViews().forEach(([id, contents]) => {
const box = this.computeBoundingBox(contents)
// console.log(id, contents, box)
if(!Number.isFinite(box.left)) return
const tel = document.createElement("div")
ctn.append(tel)
tel.classList.add("rails-xray-template")
tel.setAttribute("data-id", id)
tel.style.left = box.left + "px"
tel.style.top = box.top + "px"
tel.style.width = box.width + "px"
tel.style.height = box.height + "px"
tel.addEventListener("click", ev => {
fetch("/__xray/open", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ path: id }),
}).then(_ => this.closeOverlay())
})
const label = document.createElement("span")
tel.append(label)
label.innerText = id
})
document.body.append(ctn)
}
}
export function openXray() {
new Xray().renderOverlay()
}
export function closeXray() {
document.getElementById("rails-xray-container").remove()
}
export function registerXrayShortcut(callback) {
document.addEventListener("keydown", ev => {
if(callback(ev, Xray.isMac)) {
new Xray().renderOverlay()
} else if (ev.code == "Escape" && document.getElementById("rails-xray-container")) {
closeXray()
}
})
}
@2called-chaos
Copy link
Author

screenshot

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment