Download beholder.js from haraberget/WanGP-Injection: direct link, hf CLI and curl.
- Browser
- Download file 8.86 kB
-
https://huggingface.co/haraberget/WanGP-Injection/resolve/main/beholder.js
- Command line
-
hf download hf://haraberget/WanGP-Injection/beholder.js
-
curl -L -o beholder.js https://huggingface.co/haraberget/WanGP-Injection/resolve/main/beholder.js
8.86 kB
| (() => { | |
| const HOST_ID = "injection-beholder-root"; | |
| const old = document.getElementById(HOST_ID); | |
| if (old) { old.remove(); return; } | |
| const host = document.createElement("div"); | |
| host.id = HOST_ID; | |
| Object.assign(host.style, { | |
| position: "fixed", inset: "0", zIndex: "2147483647", | |
| pointerEvents: "none", overflow: "hidden" | |
| }); | |
| (document.documentElement || document.body).appendChild(host); | |
| const shadow = host.attachShadow({ mode: "open" }); | |
| const style = document.createElement("style"); | |
| style.textContent = ` | |
| :host { all: initial; } | |
| .beholder { | |
| --skin: #8b9c55; --dark: #26301b; --eye: #f4e7c5; | |
| position: fixed; right: clamp(18px, 4vw, 58px); bottom: 28px; | |
| width: 220px; height: 210px; pointer-events: auto; | |
| cursor: grab; transform-origin: 50% 65%; | |
| animation: hover 4.8s ease-in-out infinite; | |
| filter: drop-shadow(0 13px 12px #0007); | |
| user-select: none; touch-action: none; | |
| } | |
| .beholder:active { cursor: grabbing; } | |
| .body { | |
| position: absolute; left: 36px; top: 76px; width: 150px; height: 116px; | |
| border-radius: 48% 52% 46% 54% / 55% 52% 48% 45%; | |
| background: radial-gradient(ellipse at 34% 25%, #c1ca7a 0 8%, transparent 35%), | |
| radial-gradient(ellipse at 65% 72%, #536538 0 5%, transparent 42%), | |
| linear-gradient(145deg, #a8b76b, var(--skin) 58%, #56673a); | |
| border: 3px solid #52613a; | |
| box-shadow: inset -10px -13px 17px #26351e88, inset 8px 7px 14px #e5e9a655; | |
| } | |
| .main-eye { | |
| position: absolute; left: 75px; top: 27px; width: 84px; height: 84px; | |
| border-radius: 50%; background: radial-gradient(circle at 36% 30%, white, var(--eye) 58%, #c6b994); | |
| border: 5px solid #53613b; box-shadow: inset 0 0 0 2px #fff8, 0 2px 8px #0006; | |
| overflow: hidden; z-index: 4; | |
| } | |
| .pupil { | |
| position: absolute; left: 29px; top: 24px; width: 28px; height: 38px; | |
| border-radius: 50%; background: radial-gradient(ellipse at 35% 25%, #5d6e75, #10191d 70%); | |
| transition: transform .12s ease-out; | |
| } | |
| .pupil:after { content:""; position:absolute; width:8px; height:8px; left:5px; top:5px; border-radius:50%; background:#fff; } | |
| .lid { | |
| position:absolute; inset:-2px 0 auto; height:0; z-index:5; | |
| background:linear-gradient(#81934d,#64773d); border-radius:50%; | |
| animation: blink 5.7s infinite; | |
| } | |
| .stalk { | |
| position:absolute; width:8px; height:66px; border-radius:100% 100% 35% 35%; | |
| background:linear-gradient(90deg,#53673a,#b3bf70 48%,#687b42); | |
| transform-origin:50% 100%; z-index:2; border:1px solid #52613a; | |
| animation: sway var(--dur,3s) ease-in-out infinite alternate; | |
| } | |
| .stalk:after { | |
| content:""; position:absolute; width:27px; height:27px; left:50%; top:-17px; | |
| transform:translateX(-50%); border-radius:50%; | |
| background:radial-gradient(circle at 35% 30%,#fff,#e9dfbd 55%,#a79d7b); | |
| border:3px solid #53613b; box-shadow:0 2px 4px #0005; | |
| } | |
| .stalk .tiny-pupil { | |
| position:absolute; z-index:3; width:8px; height:11px; left:50%; top:-9px; | |
| transform:translateX(-50%); border-radius:50%; background:#20292a; | |
| } | |
| .s1 { left:49px; top:48px; --dur:2.7s; transform:rotate(-43deg); } | |
| .s2 { left:75px; top:34px; --dur:3.2s; transform:rotate(-20deg); } | |
| .s3 { left:151px; top:36px; --dur:2.4s; transform:rotate(22deg); } | |
| .s4 { left:177px; top:57px; --dur:3.5s; transform:rotate(47deg); } | |
| .s5 { left:35px; top:94px; --dur:2.9s; transform:rotate(-65deg); } | |
| .mouth { | |
| position:absolute; z-index:6; left:83px; top:151px; width:64px; height:25px; | |
| border-radius: 10px 10px 45px 45px; background:#321d20; | |
| border:3px solid #45542f; overflow:hidden; | |
| transform:rotate(-3deg); | |
| } | |
| .teeth { | |
| position:absolute; left:6px; top:0; width:48px; height:10px; | |
| background: repeating-linear-gradient(90deg,#fff0d5 0 8px,#b7ad8e 8px 10px); | |
| clip-path:polygon(0 0,100% 0,94% 100%,82% 60%,70% 100%,57% 60%,44% 100%,30% 60%,17% 100%,7% 60%); | |
| } | |
| .cheek { position:absolute; top:140px; width:15px; height:9px; border-radius:50%; background:#c57d61; opacity:.65; } | |
| .c1 { left:60px } .c2 { left:151px } | |
| .spark { position:absolute; right:15px; top:83px; color:#e7e6ad; font: bold 20px sans-serif; opacity:0; } | |
| .beholder.excited .spark { animation: sparkle .65s ease-out; } | |
| .beholder.excited .body { filter:brightness(1.2) saturate(1.25); } | |
| .beholder.excited { animation: bounce .55s ease-out 2; } | |
| .beholder.sleepy .lid { animation: sleepy 2.2s forwards; } | |
| .hint { | |
| position:absolute; bottom:-2px; left:35px; width:150px; text-align:center; | |
| color:#f4f0d8; font:11px/1.2 system-ui,sans-serif; opacity:0; | |
| text-shadow:0 1px 3px #000; transition:opacity .2s; | |
| } | |
| .beholder:hover .hint { opacity:.85; } | |
| @keyframes hover { 0%,100%{transform:translateY(0) rotate(-1deg)} 50%{transform:translateY(-10px) rotate(1deg)} } | |
| @keyframes sway { from{rotate:-5deg} to{rotate:7deg} } | |
| @keyframes blink { 0%,44%,48%,100%{height:0} 46%{height:88px} } | |
| @keyframes sparkle { 0%{opacity:0;transform:scale(.4) rotate(0)} 35%{opacity:1;transform:scale(1.3) rotate(25deg)} 100%{opacity:0;transform:scale(.6) rotate(55deg)} } | |
| @keyframes bounce { 0%,100%{translate:0 0} 35%{translate:0 -17px} 65%{translate:0 4px} } | |
| @keyframes sleepy { to{height:34px} } | |
| @media (prefers-reduced-motion: reduce) { .beholder,.stalk,.lid { animation:none !important } } | |
| `; | |
| shadow.appendChild(style); | |
| const creature = document.createElement("div"); | |
| creature.className = "beholder"; | |
| creature.setAttribute("role", "button"); | |
| creature.setAttribute("aria-label", "Injection Beholder. Click to wake it up. Press Escape to dismiss."); | |
| creature.innerHTML = ` | |
| <div class="stalk s1"><i class="tiny-pupil"></i></div> | |
| <div class="stalk s2"><i class="tiny-pupil"></i></div> | |
| <div class="stalk s3"><i class="tiny-pupil"></i></div> | |
| <div class="stalk s4"><i class="tiny-pupil"></i></div> | |
| <div class="stalk s5"><i class="tiny-pupil"></i></div> | |
| <div class="body"></div> | |
| <div class="main-eye"><div class="pupil"></div><div class="lid"></div></div> | |
| <div class="cheek c1"></div><div class="cheek c2"></div> | |
| <div class="mouth"><div class="teeth"></div></div> | |
| <div class="spark">✦</div> | |
| <div class="hint">click me · drag me · Esc to dismiss</div>`; | |
| shadow.appendChild(creature); | |
| const pupil = creature.querySelector(".pupil"); | |
| const tinyPupils = [...creature.querySelectorAll(".tiny-pupil")]; | |
| let targetX = 0, targetY = 0, dragging = false, offsetX = 0, offsetY = 0; | |
| const look = (x, y) => { | |
| const r = creature.getBoundingClientRect(); | |
| const cx = r.left + r.width / 2, cy = r.top + 70; | |
| const dx = Math.max(-9, Math.min(9, (x - cx) / 22)); | |
| const dy = Math.max(-9, Math.min(9, (y - cy) / 22)); | |
| targetX = dx; targetY = dy; | |
| pupil.style.transform = `translate(${dx}px,${dy}px)`; | |
| tinyPupils.forEach((p, i) => p.style.transform = `translate(calc(-50% + ${dx * (i%2 ? .35 : -.35)}px), ${dy*.35}px)`); | |
| }; | |
| const onMove = e => { | |
| if (dragging) { | |
| creature.style.left = `${e.clientX - offsetX}px`; | |
| creature.style.top = `${e.clientY - offsetY}px`; | |
| creature.style.right = "auto"; creature.style.bottom = "auto"; | |
| } | |
| look(e.clientX, e.clientY); | |
| }; | |
| const onDown = e => { | |
| if (!creature.contains(e.target)) return; | |
| dragging = true; | |
| const r = creature.getBoundingClientRect(); | |
| offsetX = e.clientX - r.left; offsetY = e.clientY - r.top; | |
| creature.setPointerCapture?.(e.pointerId); | |
| e.preventDefault(); | |
| }; | |
| const onUp = () => { dragging = false; }; | |
| const onClick = () => { | |
| creature.classList.remove("sleepy"); | |
| creature.classList.add("excited"); | |
| setTimeout(() => creature.classList.remove("excited"), 1300); | |
| }; | |
| const onKey = e => { if (e.key === "Escape") cleanup(); }; | |
| const cleanup = () => { | |
| document.removeEventListener("pointermove", onMove, true); | |
| document.removeEventListener("pointerup", onUp, true); | |
| document.removeEventListener("keydown", onKey, true); | |
| host.remove(); | |
| }; | |
| document.addEventListener("pointermove", onMove, true); | |
| document.addEventListener("pointerup", onUp, true); | |
| document.addEventListener("keydown", onKey, true); | |
| creature.addEventListener("pointerdown", onDown); | |
| creature.addEventListener("click", onClick); | |
| // A gentle idle drift in the gaze when the pointer is away. | |
| const idle = setInterval(() => { | |
| if (!host.isConnected) { clearInterval(idle); return; } | |
| if (!dragging && !creature.matches(":hover")) { | |
| const t = Date.now() / 900; | |
| pupil.style.transform = `translate(${Math.sin(t)*3}px,${Math.cos(t*.7)*2}px)`; | |
| } | |
| }, 900); | |
| host.addEventListener("DOMNodeRemoved", () => clearInterval(idle), { once: true }); | |
| })(); | |