Browse Source

dev: automated commit - 2026-03-11 15:02:11

Mariano Z. 2 weeks ago
parent
commit
ffcd07c5d4

+ 4 - 0
niri/.config/niri/config.d/binds.kdl

@@ -9,6 +9,9 @@ binds {
     Mod+N { spawn-sh "~/.local/bin/niri-launch-or-focus --class sdm-connect alacritty -e /home/mzunino/.local/share/go/bin/sdm-tui "; }
     Mod+Shift+k { spawn-sh "~/.local/bin/niri-launch-or-focus --class k9s alacritty -e k9s "; }
     Mod+E { spawn-sh "~/.local/bin/niri-launch-or-focus thunar"; }
+    Mod+Shift+P { spawn-sh "niri msg action set-dynamic-cast-window --id $(niri msg --json pick-window | jq .id)";}
+
+
     Mod+Shift+N { spawn-sh "~/.local/bin/dotedit"; }
     Super+Alt+L hotkey-overlay-title="Lock the Screen: swaylock" { spawn "swaylock"; }
     Super+Alt+S allow-when-locked=true hotkey-overlay-title=null { spawn-sh "pkill orca || exec orca"; }
@@ -141,6 +144,7 @@ binds {
 
     Mod+P { spawn-sh "~/.local/bin/screenshot"; }
 
+
     // Mod+Escape allow-inhibiting=false { toggle-keyboard-shortcuts-inhibit; }
 }
 

+ 5 - 0
niri/.config/niri/noctalia/binds.kdl

@@ -55,6 +55,11 @@ binds {
     Mod+Shift+R hotkey-overlay-title="Screen Recorder" {
         spawn "qs" "-c" "noctalia-shell" "ipc" "call" "screenRecorder" "toggle";
     }
+
+    // Idle Inhibitor
+    Mod+I hotkey-overlay-title="Idle Inhibitor" {
+        spawn "qs" "-c" "noctalia-shell" "ipc" "call" "idleInhibitor" "toggle";
+    }
 }
 
 

+ 10 - 0
web-apps/.local/share/applications/screencast.desktop

@@ -0,0 +1,10 @@
+[Desktop Entry]
+Name=Screen Cast
+GenericName=Screen Sharing
+Comment=Share or present your screen
+Exec=chromium --app=file:///home/mzunino/.local/share/screencast/app.html --start-fullscreen
+Icon=/home/mzunino/.local/share/screencast/icon.png
+Type=Application
+Categories=Utility;Video;
+Keywords=screen;cast;share;present;mirror;
+StartupWMClass=screencast.html

+ 349 - 0
web-apps/.local/share/screencast/app.html

@@ -0,0 +1,349 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Screen Cast</title>
+<style>
+* { box-sizing: border-box; margin: 0; padding: 0; }
+
+html, body {
+  width: 100%; height: 100%;
+  background: #000;
+  overflow: hidden;
+  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+}
+
+video {
+  width: 100%; height: 100%;
+  object-fit: contain;
+  display: block;
+}
+
+/* ── Controls bar ── */
+#controls {
+  position: fixed;
+  top: 16px;
+  left: 50%;
+  transform: translateX(-50%);
+  display: flex;
+  align-items: center;
+  gap: 4px;
+  padding: 6px;
+  background: rgba(18, 18, 18, 0.82);
+  backdrop-filter: blur(24px);
+  -webkit-backdrop-filter: blur(24px);
+  border: 1px solid rgba(255,255,255,0.09);
+  border-radius: 12px;
+  z-index: 100;
+  transition: opacity 0.25s ease;
+  white-space: nowrap;
+  cursor: grab;
+  user-select: none;
+}
+
+#controls.dragging { cursor: grabbing; transition: none; }
+
+#controls.hidden {
+  opacity: 0;
+  pointer-events: none;
+}
+
+.sep {
+  width: 1px;
+  height: 18px;
+  background: rgba(255,255,255,0.1);
+  margin: 0 2px;
+  flex-shrink: 0;
+}
+
+button {
+  background: transparent;
+  color: rgba(255,255,255,0.78);
+  border: none;
+  padding: 5px 13px;
+  font-size: 13px;
+  font-weight: 500;
+  border-radius: 7px;
+  cursor: pointer;
+  transition: background 0.12s, color 0.12s;
+  letter-spacing: 0.01em;
+  line-height: 1.4;
+}
+
+button:hover  { background: rgba(255,255,255,0.1);  color: #fff; }
+button:active { background: rgba(255,255,255,0.18); color: #fff; }
+button:disabled { opacity: 0.3; cursor: default; pointer-events: none; }
+button.on { background: rgba(255,255,255,0.14); color: #fff; }
+
+/* ── Placeholder ── */
+#placeholder {
+  position: fixed;
+  inset: 0;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  gap: 14px;
+  color: rgba(255,255,255,0.35);
+  z-index: 50;
+  transition: opacity 0.3s;
+  user-select: none;
+}
+
+#placeholder.hidden { opacity: 0; pointer-events: none; }
+
+#placeholder svg { opacity: 0.25; }
+
+#placeholder .label {
+  font-size: 15px;
+  font-weight: 400;
+  letter-spacing: 0.01em;
+}
+
+#placeholder .hint {
+  font-size: 12px;
+  opacity: 0.6;
+}
+
+kbd {
+  font-family: inherit;
+  font-size: 11px;
+  background: rgba(255,255,255,0.08);
+  border: 1px solid rgba(255,255,255,0.14);
+  border-radius: 4px;
+  padding: 1px 6px;
+}
+
+/* ── Black overlay ── */
+#overlay {
+  position: fixed;
+  inset: 0;
+  background: #000;
+  z-index: 80;
+  opacity: 0;
+  pointer-events: none;
+  transition: opacity 0.2s;
+}
+#overlay.on { opacity: 1; pointer-events: auto; }
+
+/* ── Keyboard shortcut hint (fades after a few seconds) ── */
+#kbd-hint {
+  position: fixed;
+  bottom: 20px;
+  left: 50%;
+  transform: translateX(-50%);
+  font-size: 11px;
+  color: rgba(255,255,255,0.22);
+  z-index: 100;
+  letter-spacing: 0.04em;
+  white-space: nowrap;
+  transition: opacity 0.6s;
+  pointer-events: none;
+}
+#kbd-hint.hidden { opacity: 0; }
+
+/* ── Connecting pulse ── */
+.pulse { display: inline-flex; gap: 4px; align-items: center; }
+.pulse i {
+  display: block;
+  width: 5px; height: 5px;
+  border-radius: 50%;
+  background: rgba(255,255,255,0.6);
+  animation: p 1.1s ease-in-out infinite;
+  font-style: normal;
+}
+.pulse i:nth-child(2) { animation-delay: 0.18s; }
+.pulse i:nth-child(3) { animation-delay: 0.36s; }
+@keyframes p {
+  0%,80%,100% { transform: scale(0.65); opacity: 0.35; }
+  40%          { transform: scale(1);    opacity: 1; }
+}
+</style>
+</head>
+<body>
+
+<div id="controls">
+  <button id="btn-select">Select Stream</button>
+  <button id="btn-reconnect" disabled>Reconnect</button>
+  <div class="sep"></div>
+  <button id="btn-black">Black</button>
+  <button id="btn-fs">Fullscreen</button>
+</div>
+
+<div id="placeholder">
+  <svg width="56" height="56" viewBox="0 0 24 24" fill="none" stroke="currentColor"
+       stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round">
+    <rect x="2" y="3" width="20" height="14" rx="2.5"/>
+    <line x1="8"  y1="21" x2="16" y2="21"/>
+    <line x1="12" y1="17" x2="12" y2="21"/>
+  </svg>
+  <p class="label">No stream selected</p>
+  <p class="hint">Press <kbd>S</kbd> to pick a screen or window</p>
+</div>
+
+<video id="video" autoplay playsinline></video>
+
+<div id="overlay"></div>
+
+<div id="kbd-hint">S · select &nbsp;·&nbsp; R · reconnect &nbsp;·&nbsp; B · black &nbsp;·&nbsp; F · fullscreen &nbsp;·&nbsp; Esc · un-black</div>
+
+<script>
+const video    = document.getElementById("video")
+const overlay  = document.getElementById("overlay")
+const controls = document.getElementById("controls")
+const ph       = document.getElementById("placeholder")
+const kbdHint  = document.getElementById("kbd-hint")
+const btnSel   = document.getElementById("btn-select")
+const btnRec   = document.getElementById("btn-reconnect")
+const btnBlack = document.getElementById("btn-black")
+const btnFs    = document.getElementById("btn-fs")
+
+let stream      = null
+let isBlack     = false
+let connecting  = false
+let hideTimer   = null
+
+// ── Auto-hide controls + cursor ────────────────────────────────
+
+function showControls() {
+  controls.classList.remove("hidden")
+  document.body.style.cursor = ""
+  clearTimeout(hideTimer)
+  if (stream) hideTimer = setTimeout(hideControls, 2500)
+}
+
+function hideControls() {
+  controls.classList.add("hidden")
+  document.body.style.cursor = "none"
+}
+
+document.addEventListener("mousemove", showControls)
+document.addEventListener("mousedown", showControls)
+
+setTimeout(() => kbdHint.classList.add("hidden"), 4000)
+
+// ── Stream ─────────────────────────────────────────────────────
+
+async function connect() {
+  if (connecting) return
+  connecting = true
+  btnSel.innerHTML = `<span class="pulse"><i></i><i></i><i></i></span>`
+  btnSel.disabled  = true
+
+  const saved = localStorage.getItem("displaySurface")
+  const constraints = { video: saved ? { displaySurface: saved } : true, audio: false }
+
+  try {
+    const s = await navigator.mediaDevices.getDisplayMedia(constraints)
+    const surface = s.getVideoTracks()[0].getSettings().displaySurface
+    if (surface) localStorage.setItem("displaySurface", surface)
+    attach(s)
+  } catch(e) {
+    console.error(e)
+    reset()
+  }
+}
+
+function attach(s) {
+  stream = s
+  video.srcObject = s
+  ph.classList.add("hidden")
+  btnRec.disabled = false
+  reset()
+
+  s.getVideoTracks()[0].onended = () => {
+    stream = null
+    ph.classList.remove("hidden")
+    btnRec.disabled = true
+    showControls()
+    clearTimeout(hideTimer)
+    setTimeout(connect, 1000)
+  }
+
+  showControls()
+}
+
+function reset() {
+  connecting      = false
+  btnSel.disabled = false
+  btnSel.textContent = "Select Stream"
+}
+
+function reconnect() {
+  if (stream) { stream.getTracks().forEach(t => t.stop()); stream = null }
+  connect()
+}
+
+function toggleBlack() {
+  isBlack = !isBlack
+  overlay.classList.toggle("on", isBlack)
+  btnBlack.classList.toggle("on", isBlack)
+}
+
+function toggleFs() {
+  if (!document.fullscreenElement) document.documentElement.requestFullscreen()
+  else document.exitFullscreen()
+}
+
+// ── Buttons ────────────────────────────────────────────────────
+
+btnSel.onclick   = connect
+btnRec.onclick   = reconnect
+btnBlack.onclick = toggleBlack
+btnFs.onclick    = toggleFs
+
+// ── Keyboard ───────────────────────────────────────────────────
+
+document.addEventListener("keydown", e => {
+  if (e.target.tagName === "INPUT") return
+  const k = e.key.toLowerCase()
+  if (k === "s")      connect()
+  if (k === "r")      reconnect()
+  if (k === "b")      toggleBlack()
+  if (k === "f")      toggleFs()
+  if (k === "escape" && isBlack) toggleBlack()
+})
+
+document.addEventListener("fullscreenchange", () => {
+  btnFs.textContent = document.fullscreenElement ? "Exit Fullscreen" : "Fullscreen"
+})
+
+// ── Drag to reposition controls ────────────────────────────────
+
+;(function () {
+  let dragging = false, ox = 0, oy = 0
+
+  function applyPos(x, y) {
+    // Clamp within viewport
+    const r = controls.getBoundingClientRect()
+    x = Math.max(0, Math.min(window.innerWidth  - r.width,  x))
+    y = Math.max(0, Math.min(window.innerHeight - r.height, y))
+    controls.style.left      = x + "px"
+    controls.style.top       = y + "px"
+    controls.style.transform = "none"
+  }
+
+  controls.addEventListener("mousedown", e => {
+    if (e.target.tagName === "BUTTON") return
+    dragging = true
+    controls.classList.add("dragging")
+    const r = controls.getBoundingClientRect()
+    ox = e.clientX - r.left
+    oy = e.clientY - r.top
+    e.preventDefault()
+  })
+
+  document.addEventListener("mousemove", e => {
+    if (!dragging) return
+    applyPos(e.clientX - ox, e.clientY - oy)
+  })
+
+  document.addEventListener("mouseup", () => {
+    if (!dragging) return
+    dragging = false
+    controls.classList.remove("dragging")
+  })
+})()
+</script>
+</body>
+</html>

BIN
web-apps/.local/share/screencast/icon.png