Skip to content

Instantly share code, notes, and snippets.

@hotsphink
Created February 21, 2026 01:53
Show Gist options
  • Select an option

  • Save hotsphink/259f12c8e02b08730fbd07c857b57020 to your computer and use it in GitHub Desktop.

Select an option

Save hotsphink/259f12c8e02b08730fbd07c857b57020 to your computer and use it in GitHub Desktop.
mkgist-created gist
// ==UserScript==
// @name phabricator unsubmitted indicator
// @namespace Violentmonkey Scripts
// @match https://phabricator.services.mozilla.com/D*
// @run-at document-end
// @version 1.0
// @author Steve Fink <sfink@mozilla.com>
// @description Make it obvious when there are unsubmitted comments.
// @grant none
// ==/UserScript==
var pending = false;
var line;
// There seems to be some timing issue with this one. It seems simple enough:
// rely on whether this is visible or not. But I seem to be seeing it visible
// after it should be gone (yet the next time I check, it's gone.)
var main_preview = document.querySelector(".phui-comment-preview-view");
// For now, rely on comment contents.
var main_comments = document.querySelector("textarea[name='comment']");
var favicon;
var orig_favicon;
var tinted_favicon =
"data:image/png;base64,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";
function updateIndicator() {
pending = 0;
console.log("Checking...");
const hasInlineComments = [
...document.querySelectorAll(".inline-state-is-draft"),
].filter((elt) => elt.offsetParent).length;
const hasInlineCommentEditor = !!document.querySelector(
".differential-inline-comment-edit",
);
//const hasMainComment = !!main_preview.offsetParent;
const hasMainComment = main_comments.value != "";
const hasUnsubmitted =
hasInlineComments || hasInlineCommentEditor || hasMainComment;
const title = document.title;
let newTitle = title.replace("** ", "").replace(" **", "");
if (hasUnsubmitted) {
newTitle = "** " + newTitle + " **";
line.hidden = false;
favicon.href = tinted_favicon;
} else {
line.hidden = true;
favicon.href = orig_favicon;
}
if (newTitle !== title) {
document.title = newTitle;
}
console.log("Checked", {
hasInlineComments,
hasInlineCommentEditor,
hasMainComment,
});
}
// This can be called frequently, and will internally debounce the update using rAF.
function bouncy_update() {
if (pending) return;
pending = true;
requestAnimationFrame(() => {
pending = false;
updateIndicator();
});
}
const watcher = new MutationObserver((mutations, observer) => {
bouncy_update();
});
function init() {
line = document.createElement("div");
Object.assign(line.style, {
position: "fixed",
top: 0,
left: 0,
width: "100%",
height: "10px",
background:
"linear-gradient(to bottom, rgba(255,0,0,1) 0%, rgba(255,0,0,0.4) 40%, rgba(255,0,0,0) 100%)",
zIndex: 99999,
pointerEvents: "none",
});
document.body.appendChild(line);
// TODO: Be more specific
watcher.observe(document.body, {
subtree: true,
childList: true,
});
document
.querySelector("textarea[name='comment']")
.addEventListener("input", bouncy_update);
favicon = document.querySelector("link#favicon");
orig_favicon = favicon.href;
}
init();
console.log("phabricator unsubmitted indicator user script init complete.");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment