Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/main/windowManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const { BrowserWindow, screen } = require('electron');
const path = require('path');

let overlayWindows = [];
let refocusInterval = null;

function createOverlayForDisplay(display, session) {
const { x, y, width, height } = display.bounds;
Expand Down Expand Up @@ -56,9 +57,22 @@ function showOverlay(session) {
const overlay = createOverlayForDisplay(display, session);
overlayWindows.push(overlay);
});

refocusInterval = setInterval(() => {
overlayWindows.forEach((w) => {
if (!w.isDestroyed() && !w.isFocused()) {
w.setAlwaysOnTop(true, 'screen-saver');
w.focus();
}
});
}, 500);
}

function closeOverlay() {
if (refocusInterval) {
clearInterval(refocusInterval);
refocusInterval = null;
}
overlayWindows.forEach((w) => {
if (!w.isDestroyed()) w.destroy();
});
Expand Down
105 changes: 89 additions & 16 deletions src/renderer/overlay.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@
--bg: #0a0a0a;
--text: #e8e8e8;
--text-dim: #666;
--text-muted: #444;
--accent: #ff4d00;
--accent-glow: rgba(255, 77, 0, 0.08);
--danger: #e81123;
--font: 'Segoe UI', -apple-system, system-ui, sans-serif;
--mono: 'Cascadia Code', 'Fira Code', monospace;
}

html, body {
Expand All @@ -32,7 +36,7 @@
align-items: center;
justify-content: center;
height: 100vh;
gap: 32px;
gap: 24px;
animation: fadeIn 0.3s ease;
}

Expand All @@ -59,8 +63,72 @@
color: var(--text-dim);
}

.overlay__countdown {
font-size: 12px;
color: var(--text-dim);
font-variant-numeric: tabular-nums;
height: 18px;
}

.overlay__ack {
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
margin-top: 8px;
opacity: 0;
pointer-events: none;
transition: opacity 0.3s ease;
}

.overlay__ack.active {
opacity: 1;
pointer-events: auto;
}

.overlay__ack-hint {
font-size: 11px;
color: var(--text-muted);
letter-spacing: 0.5px;
}

.overlay__type-input {
width: 200px;
padding: 10px 14px;
background: transparent;
border: 2px solid var(--text-muted);
border-radius: 6px;
color: var(--text);
font-family: var(--mono);
font-size: 18px;
font-weight: 700;
text-align: center;
letter-spacing: 4px;
text-transform: uppercase;
outline: none;
transition: border-color 0.2s ease;
}

.overlay__type-input::placeholder {
color: var(--text-muted);
letter-spacing: 4px;
font-size: 14px;
}

.overlay__type-input:focus {
border-color: var(--accent);
}

.overlay__type-input.error {
border-color: var(--danger);
animation: shake 0.3s ease;
}

.overlay__type-input.success {
border-color: var(--accent);
}

.overlay__dismiss {
margin-top: 16px;
padding: 14px 48px;
border: 2px solid var(--accent);
background: transparent;
Expand All @@ -73,31 +141,30 @@
cursor: pointer;
border-radius: 6px;
transition: all 0.2s ease;
opacity: 0;
pointer-events: none;
}

.overlay__dismiss.active {
opacity: 1;
pointer-events: auto;
}

.overlay__dismiss.active:hover {
.overlay__dismiss:hover {
background: var(--accent);
color: #fff;
}

.overlay__countdown {
font-size: 12px;
color: var(--text-dim);
font-variant-numeric: tabular-nums;
height: 18px;
.overlay__or {
font-size: 11px;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 2px;
}

@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}

@keyframes shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-6px); }
75% { transform: translateX(6px); }
}
</style>
</head>
<body>
Expand All @@ -106,7 +173,13 @@
<p class="overlay__message" id="message"></p>
<span class="overlay__session" id="session-name"></span>
<span class="overlay__countdown" id="countdown"></span>
<button class="overlay__dismiss" id="btn-dismiss">I'm on it</button>

<div class="overlay__ack" id="ack-area">
<span class="overlay__ack-hint">Type <strong>DONE</strong> to dismiss</span>
<input class="overlay__type-input" id="type-input" type="text" placeholder="DONE" maxlength="4" autocomplete="off" spellcheck="false">
<span class="overlay__or">or</span>
<button class="overlay__dismiss" id="btn-dismiss">I'm on it</button>
</div>
</div>
<script src="overlayApp.js"></script>
</body>
Expand Down
35 changes: 34 additions & 1 deletion src/renderer/overlayApp.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
const DISMISS_DELAY = 5;
const CONFIRM_WORD = 'DONE';

const messageEl = document.getElementById('message');
const sessionNameEl = document.getElementById('session-name');
const countdownEl = document.getElementById('countdown');
const ackArea = document.getElementById('ack-area');
const typeInput = document.getElementById('type-input');
const btnDismiss = document.getElementById('btn-dismiss');

let remaining = DISMISS_DELAY;
let unlocked = false;

window.overlay.onData((data) => {
messageEl.textContent = data.message;
Expand All @@ -18,13 +22,42 @@ window.overlay.onData((data) => {
} else {
clearInterval(tick);
countdownEl.textContent = '';
btnDismiss.classList.add('active');
unlocked = true;
ackArea.classList.add('active');
typeInput.focus();
}
}, 1000);

countdownEl.textContent = `Dismiss available in ${remaining}s`;
});

typeInput.addEventListener('input', () => {
if (!unlocked) return;

typeInput.classList.remove('error');

if (typeInput.value.toUpperCase() === CONFIRM_WORD) {
typeInput.classList.add('success');
window.overlay.dismiss();
}
});

typeInput.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
e.preventDefault();
}
});

btnDismiss.addEventListener('click', () => {
if (!unlocked) return;
window.overlay.dismiss();
});

document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' || (e.altKey && e.key === 'F4')) {
e.preventDefault();
}
if (e.altKey && e.key === 'Tab') {
e.preventDefault();
}
});
Loading