Skip to content

Instantly share code, notes, and snippets.

@PEZ
Last active March 6, 2026 07:38
Show Gist options
  • Select an option

  • Save PEZ/3b0fdc406e7593eaaef609b6fb4a687d to your computer and use it in GitHub Desktop.

Select an option

Save PEZ/3b0fdc406e7593eaaef609b6fb4a687d to your computer and use it in GitHub Desktop.
{:epupp/script-name "pez/element_printing.cljs"
:epupp/description "Isolate any element on a web page for clean printing"}
(ns pez.element-printing)
;; --- State ---
(defonce !state
(atom {:mode :idle
:handlers {}
:highlighted-el nil
:isolated-el nil
:hidden-els []
:saved-ancestor-styles []
:original-body-padding nil
:control-bar nil
:print-style nil}))
(defn idle? [] (= :idle (:mode @!state)))
(defn hovering? [] (= :hovering (:mode @!state)))
(defn isolated? [] (= :isolated (:mode @!state)))
(def skip-tags #{"SCRIPT" "STYLE" "LINK"})
;; --- Highlight ---
(defn clear-highlight! []
(when-let [el (:highlighted-el @!state)]
(set! (.. el -style -outline) "")
(set! (.. el -style -cursor) "")
(swap! !state assoc :highlighted-el nil)))
(defn highlight-el! [el]
(when (and el (not= el js/document.body) (not= el js/document.documentElement))
(clear-highlight!)
(set! (.. el -style -outline) "2px solid cyan")
(set! (.. el -style -outlineOffset) "-2px")
(set! (.. el -style -cursor) "crosshair")
(swap! !state assoc :highlighted-el el)))
(defn on-mouseover [e]
(.stopPropagation e)
(highlight-el! (.-target e)))
;; --- DOM isolation ---
(defn isolate-element! [el]
(let [hidden (atom [])]
(loop [current el]
(let [parent (.-parentElement current)]
(when (and parent (not= current js/document.documentElement))
(doseq [sibling (array-seq (.-children parent))]
(when (and (not= sibling current)
(not (skip-tags (.-tagName sibling)))
(not= "none" (.. sibling -style -display)))
(swap! hidden conj {:el sibling :display (.. sibling -style -display)})
(set! (.. sibling -style -display) "none")))
(when (not= parent js/document.body)
(recur parent)))))
@hidden))
(def ^:private style-props ["overflow" "overflow-y" "max-height" "height"])
(defn- save-style-props [style]
(into {} (map (fn [prop]
[prop {:value (.getPropertyValue style prop)
:priority (.getPropertyPriority style prop)}])
style-props)))
(defn- set-overflow-props! [style overflow overflowY]
(.setProperty style "overflow" overflow "important")
(.setProperty style "overflow-y" overflowY "important")
(.setProperty style "max-height" "none" "important")
(.setProperty style "height" "auto" "important"))
(defn- restore-style-props! [style saved-props]
(doseq [prop style-props]
(let [{:keys [value priority]} (get saved-props prop)]
(if (= "" value)
(.removeProperty style prop)
(.setProperty style prop value priority)))))
(defn reset-ancestor-overflow! [el]
(let [saved (atom [])]
(loop [current (.-parentElement el)]
(when current
(let [style (.-style current)
props (save-style-props style)]
(swap! saved conj {:el current :props props})
(if (or (= current js/document.body) (= current js/document.documentElement))
(set-overflow-props! style "auto" "auto")
(set-overflow-props! style "visible" "visible")))
(when (not= current js/document.documentElement)
(recur (.-parentElement current)))))
(swap! !state assoc :saved-ancestor-styles @saved)))
(defn restore-page! []
(doseq [{:keys [el display]} (:hidden-els @!state)]
(set! (.. el -style -display) (or display "")))
(doseq [{:keys [el props]} (:saved-ancestor-styles @!state)]
(restore-style-props! (.-style el) props))
(when-let [padding (:original-body-padding @!state)]
(set! (.. js/document.body -style -paddingTop) padding))
(when-let [bar (:control-bar @!state)]
(.remove bar))
(when-let [style (:print-style @!state)]
(.remove style))
(swap! !state assoc
:mode :idle
:hidden-els []
:saved-ancestor-styles []
:isolated-el nil
:control-bar nil
:print-style nil
:original-body-padding nil))
(declare cancel!)
;; --- Control bar ---
(defn create-button [text on-click-fn]
(let [btn (js/document.createElement "button")]
(set! (.-textContent btn) text)
(set! (.. btn -style -cssText)
(str "padding: 6px 16px; margin: 0 6px; border: 1px solid rgba(255,255,255,0.3);"
"border-radius: 4px; background: rgba(255,255,255,0.15); color: white;"
"cursor: pointer; font-size: 14px;"))
(.addEventListener btn "click" on-click-fn)
btn))
(defn add-print-style! []
(let [style (js/document.createElement "style")]
(set! (.-textContent style)
"@media print { #epupp-print-bar { display: none !important; } body { padding-top: 0 !important; } }")
(.appendChild js/document.head style)
(swap! !state assoc :print-style style)))
(defn show-control-bar! []
(let [bar (js/document.createElement "div")]
(set! (.-id bar) "epupp-print-bar")
(set! (.. bar -style -cssText)
(str "position: fixed; top: 0; left: 0; right: 0; z-index: 2147483647;"
"background: rgba(30,41,59,0.95); color: white; padding: 8px 16px;"
"display: flex; align-items: center; justify-content: flex-end;"
"gap: 8px; font-family: system-ui, sans-serif; font-size: 14px;"
"box-shadow: 0 2px 8px rgba(0,0,0,0.3);"))
(let [label (js/document.createElement "span")]
(set! (.-textContent label) "Element isolated for printing")
(set! (.. label -style -marginRight) "auto")
(.appendChild bar label))
(.appendChild bar (create-button "Cancel" (fn [_] (cancel!))))
(.appendChild bar (create-button "Print" (fn [_] (.print js/window))))
(.appendChild js/document.body bar)
(swap! !state assoc
:original-body-padding (.. js/document.body -style -paddingTop)
:control-bar bar)
(let [bar-height (.-offsetHeight bar)]
(set! (.. js/document.body -style -paddingTop) (str bar-height "px")))
(add-print-style!)))
;; --- Isolation flow ---
(defn do-isolate! [el]
(let [hidden (isolate-element! el)]
(swap! !state assoc
:hidden-els hidden
:isolated-el el
:mode :isolated)
(reset-ancestor-overflow! el)
(show-control-bar!)))
;; --- Hover mode ---
(defn stop-hover! []
(when (hovering?)
(let [{:keys [handlers]} @!state]
(when-let [mo (:mouseover handlers)]
(.removeEventListener js/document "mouseover" mo true))
(when-let [cl (:click handlers)]
(.removeEventListener js/document "click" cl true)))
(clear-highlight!)
(swap! !state assoc :mode :idle :handlers {})))
(defn on-click [e]
(.stopPropagation e)
(.preventDefault e)
(let [el (.-target e)]
(when (and el (not= el js/document.body) (not= el js/document.documentElement))
(clear-highlight!)
(stop-hover!)
(do-isolate! el))))
(defn start-hover! []
(cond
(hovering?) (stop-hover!)
(isolated?) nil
:else
(do
(.addEventListener js/document "mouseover" on-mouseover true)
(.addEventListener js/document "click" on-click true)
(swap! !state assoc
:mode :hovering
:handlers {:mouseover on-mouseover
:click on-click}))))
;; --- Cancel ---
(defn cancel! []
(restore-page!)
(start-hover!))
;; --- Keyboard ---
(defn on-keydown [e]
(when (= "Escape" (.-key e))
(.preventDefault e)
(.stopPropagation e)
(cond
(isolated?) (cancel!)
(hovering?) (stop-hover!))))
(defonce escape-handler
(do (.addEventListener js/document "keydown" on-keydown true)
on-keydown))
;; --- Entry point ---
(defn start! []
(start-hover!))
(start!)
@PEZ
Copy link
Author

PEZ commented Mar 2, 2026

Here's a demo where Copilot creates this userscript: https://www.youtube.com/watch?v=CuEWN5yYVa8

The Copilot Prompt

The prompt can of course be adapted to any agent, but this one is tuned for Copilot.

Prerequisites:

  • You use the prompt from your copy of the My Epupp HQ template project: https://github.com/PEZ/my-epupp-hq
  • You have installed things and followed the instructions in ^ that ^ project.
  • You use the Epupp Assistant custom agent
  • I recommend to use Opus 4.6. It could work with Sonnet 4.6 too, but probably will take more iteration.
**Write an Epupp userscript that lets me isolate any element on a web page for clean printing.**

## How it should work

The script should auto-start into a "hover to select" mode as soon as it runs. As I move my mouse over the page, highlight the element under the cursor with a visible outline (something like cyan, inset so it doesn't shift layout). Change the cursor to crosshair so I know I'm in selection mode.

When I click an element, isolate it by hiding everything else on the page - not by removing nodes, but by walking up the DOM tree from the clicked element to the body, hiding all siblings at each level. This way the element keeps its original CSS context (inherited styles, cascade, layout) while everything else disappears.

Once isolated, show a small fixed control bar at the top of the page with two buttons:

- **Cancel** - restore the page and go back to hover mode so I can try a different element
- **Print** - trigger the browser's print dialog

The control bar should look like a tool overlay - dark semi-transparent background, white text, high z-index. Push the body content down with padding so nothing hides behind the bar.

### Print behavior

When printing, the control bar and its body padding should be hidden via a `@media print` rule so the printout is clean - just the isolated element as if the page naturally only contained that content.

### Tall element printing

Many web apps use scrolling containers with constrained heights (`overflow: scroll/hidden/auto`). After hiding siblings, the isolated element can still be clipped by these ancestor containers, causing the printout to be cropped to the visible viewport area.

After isolating, reset overflow and height constraints on all ancestors from the isolated element up through `<body>` and `<html>`. Save and restore the original inline style values (including `!important` priority) alongside the other state preservation.

### Keyboard

Escape should work as progressive undo:

- In hover mode: cancel selection and return to idle
- In isolated mode: restore the page and return to hover mode (same as Cancel button)

### State preservation

Save and restore all original values - element display properties, body padding-top, ancestor overflow/height styles.  Use `defonce` for the state atom so the script survives REPL re-evaluations during development.

### Iteration workflow

The key UX insight: users rarely pick the right element on the first try. After canceling isolation, automatically re-enter hover mode so they can immediately try another element without re-running the script.

### Event handling

Use capture phase (third argument `true`) for mouseover and click listeners so the script intercepts events before the page's own handlers can interfere. Stop propagation and prevent default on clicks to avoid accidentally navigating links or triggering page behavior while selecting.

### Safety

Store event handler references in the state atom so they can be cleanly removed when exiting hover mode. If `start-hover!` is called while already hovering, toggle it off. If called while isolated, no-op.

The script should work on any website with zero configuration - no site-specific selectors, no external dependencies beyond `clojure.string`.

## How you should work

Please use the structural create clojure file tool to create a stub for the script, which should be named `pez/element_printing.cljs`. Include no-op function `start!` and a call to that function. Then task an Epupp Assistant subagent to plan the implementation in some incremental progression steps. Then hand the requirements and the plan over to an Epupp Assistant subagent with instructions to use the epupp-default repl and the askQuestions tool to develop the functionality interactively and in coop with the user. The agent should bring piece by piece of the functionality to life and instruct the user how to test at each step using askQuestions to collect the feedback. When all functionality is in place the subagent should in turn task a subagent to edit the userscript file with the code that implements the requirements. Then your subagent should report back to you and then you synthesize the work done in the chat.

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