diff --git a/userstuff/discord/Highlight hovered message.user.css b/userstuff/discord/Highlight hovered message.user.css
new file mode 100644
index 0000000..fe9d89b
--- /dev/null
+++ b/userstuff/discord/Highlight hovered message.user.css
@@ -0,0 +1,10 @@
+/* ==UserStyle==
+@name Highlight hovered message
+@description Adds a background and underline to the Discord message under the mouse cursor
+@match https://discord.com/*
+==/UserStyle== */
+
+div[class*=cozyMessage]:hover {
+ background-color: #ccc1;
+ box-shadow: 0 1px 0px #eee5;
+}
diff --git a/userstuff/e621/Search their favorites!.user.js b/userstuff/e621/Search their favorites!.user.js
new file mode 100644
index 0000000..e34f4ee
--- /dev/null
+++ b/userstuff/e621/Search their favorites!.user.js
@@ -0,0 +1,152 @@
+// ==UserScript==
+// @name Search their favorites!
+// @description This is your new file, start writing code
+// @match https://e621.net/posts/*/favorites
+// ==/UserScript==
+
+const settings = {
+ normalHoverInfoDelay: 400,
+ fastHoveringInfoDelay: 150,
+ endFastHoveringDelay: 500,
+
+ focusInfoDelay: 750,
+
+ hideTooltipDelay: 500,
+};
+
+const state = {
+ hoverTimeout: null,
+ focusTimeout: null,
+
+ fastHovering: false,
+ endFastHoveringTimeout: false,
+
+ showing: false,
+ justHidden: false,
+
+ row: null,
+};
+
+const thumb = document.querySelector('#a-index .thumbnail');
+
+const container = document.createElement('div');
+
+const tagLabel = document.createElement('label');
+const tagInput = document.createElement('input');
+tagLabel.appendChild(document.createTextNode('Search these tags: '));
+tagLabel.appendChild(tagInput);
+container.appendChild(tagLabel);
+
+let hoverTimeout = null;
+for (const a of document.querySelectorAll('#a-index td a[href^="/favorites"]')) {
+ a.addEventListener('mouseover', event => {
+ const hoverTimeoutDelay =
+ (state.fastHovering
+ ? settings.fastHoveringInfoDelay
+ : settings.normalHoverInfoDelay);
+
+ state.hoverTimeout = setTimeout(() => {
+ state.hoverTimeout = null;
+ state.fastHovering = true;
+ show(a);
+ }, hoverTimeoutDelay);
+
+ if (state.endFastHoveringTimeout) {
+ clearTimeout(state.endFastHoveringTimeout);
+ state.endFastHoveringTimeout = null;
+ }
+
+ if (state.hideTimeout) {
+ clearTimeout(state.hideTimeout);
+ state.hideTimeout = null;
+ }
+ });
+
+ a.addEventListener('mouseleave', event => {
+ if (state.hoverTimeout) {
+ clearTimeout(state.hoverTimeout);
+ state.hoverTimeout = null;
+ }
+
+ if (state.fastHovering && !state.endFastHoveringTimeout) {
+ state.endFastHoveringTimeout = setTimeout(() => {
+ state.endFastHoveringTimeout = null;
+ state.fastHovering = false;
+ }, settings.endFastHoveringDelay);
+ }
+ });
+
+ a.closest('tr').addEventListener('mouseleave', event => {
+ if (state.showing && !state.hideTimeout) {
+ state.hideTimeout = setTimeout(() => {
+ state.hideTimeout = null;
+ hide();
+ }, settings.hideTooltipDelay);
+ }
+ });
+
+ a.addEventListener('focus', event => {
+ state.focusTimeout = setTimeout(() => {
+ state.focusTimeout = null;
+ show(a);
+ }, settings.focusInfoDelay);
+
+ if (state.justHidden) {
+ clearTimeout(state.focusTimeout);
+ state.focusTimeout = null;
+ show(a);
+ }
+ })
+}
+
+function show(a) {
+ hide();
+
+ const tr = document.createElement('tr');
+ const td = document.createElement('td');
+ const aa = document.createElement('a');
+ aa.href = '#';
+ aa.appendChild(document.createTextNode('Hiya'));
+ td.appendChild(aa);
+ tr.appendChild(td);
+
+ state.showing = true;
+ state.row = tr;
+
+ tr.addEventListener('mouseenter', event => {
+ if (state.hideTimeout) {
+ clearTimeout(state.hideTimeout);
+ state.hideTimeout = null;
+ }
+ });
+
+ tr.addEventListener('focusin', event => {
+ if (state.hideTimeout) {
+ clearTimeout(state.hideTimeout);
+ state.hideTimeout = null;
+ }
+ });
+
+ tr.addEventListener('focusout', event => {
+ hide();
+ });
+
+ a.closest('tr').after(tr);
+}
+
+function hide() {
+ if (!state.showing) return;
+ if (!state.row) return;
+
+ state.row.remove();
+
+ state.showing = false;
+ state.row = null;
+
+ state.justHidden = true;
+ setTimeout(() => {
+ state.justHiden = false;
+ });
+}
+
+thumb.after(container);
diff --git a/userstuff/google-docs/Banish Gemini.user.css b/userstuff/google-docs/Banish Gemini.user.css
new file mode 100644
index 0000000..04f78cc
--- /dev/null
+++ b/userstuff/google-docs/Banish Gemini.user.css
@@ -0,0 +1,9 @@
+/* ==UserStyle==
+@name Banish Gemini
+@description Removes Gemini promo button from Google Docs, etc
+@match https://docs.google.com/*
+==/UserStyle== */
+
+#docs-sidekick-gen-ai-promo-button-container {
+ display: none;
+}
\ No newline at end of file
diff --git a/userstuff/youtube/Copy YouTube chat messages.user.js b/userstuff/youtube/Copy YouTube chat messages.user.js
new file mode 100644
index 0000000..9665e12
--- /dev/null
+++ b/userstuff/youtube/Copy YouTube chat messages.user.js
@@ -0,0 +1,18 @@
+// ==UserScript==
+// @name Copy YouTube chat messages
+// @description Copies the text of a message when you click it (no emotes, sorry)
+// @match https://www.youtube.com/live_chat_replay*
+// ==/UserScript==
+
+document.body.addEventListener('click', event => {
+ const mouseElem = document.elementFromPoint(event.clientX, event.clientY);
+ if (!mouseElem) return;
+ const rendererElem = mouseElem.closest('yt-live-chat-text-message-renderer');
+ if (!rendererElem) return;
+ const messageElem = rendererElem.querySelector('#message');
+ if (!messageElem) {
+ console.warn(`Couldn't find message in message renderer`);
+ return;
+ }
+ navigator.clipboard.writeText(messageElem.innerText.trim());
+});
|