Rewrite to TidalLuna of All Plugins

This commit is contained in:
2025-05-31 23:44:22 +10:00
parent 20698aea3a
commit 5d433ce4d1
49 changed files with 1668 additions and 2867 deletions
+138
View File
@@ -0,0 +1,138 @@
import { LunaUnload, Tracer } from "@luna/core";
export const { trace } = Tracer("[Copy Lyrics]");
// clean up resources
export const unloads = new Set<LunaUnload>();
const unlockSelection = `
[class^="_lyricsText"]>div>span {
user-select: text;
cursor: text;
}
::selection {
background: rgb(0, 0, 0);
color: rgb(255, 255, 255);
}
`;
function ApplyCSS(style: string): HTMLStyleElement {
const styleElement = document.createElement("style");
styleElement.type = "text/css";
styleElement.textContent = style;
document.head.appendChild(styleElement);
return styleElement;
}
function SetClipboard(text: string): void {
const textarea = document.createElement("textarea");
textarea.value = text;
textarea.style.position = "fixed"; // Avoid scrolling to bottom
document.body.appendChild(textarea);
textarea.select();
try {
const success = document.execCommand("copy");
if (!success) throw new Error("Failed to copy text.");
} catch (err) {
trace.msg.err(err instanceof Error ? err.message : String(err));
} finally {
document.body.removeChild(textarea);
}
}
const styleElement = ApplyCSS(unlockSelection);
let isSelecting = false;
const onMouseDown = function (): void {
isSelecting = true;
};
const onMouseUp = function (event: MouseEvent): void {
if (isSelecting) {
const selection = window.getSelection();
if (selection && selection.toString().length > 0) {
const selectedSpans: HTMLSpanElement[] = [];
const range = selection.getRangeAt(0);
let container = range.commonAncestorContainer;
// If the container is NOT an element and a document, adjust it.
if (
container.nodeType !== Node.ELEMENT_NODE &&
container.nodeType !== Node.DOCUMENT_NODE
) {
// Get the parent element if it's a text node
const parentElement = container.parentElement;
if (parentElement && parentElement.hasAttribute("data-current")) {
let text_ = selection.toString().trim();
SetClipboard(text_);
trace.msg.log("Copied to clipboard!");
return;
}
}
// Get all the spans inside the container.
const spans = (container as Element).getElementsByTagName("span");
for (let span of spans) {
if (selection.containsNode(span, true)) {
selectedSpans.push(span as HTMLSpanElement);
}
}
// Concat the text of the selected spans.
let hasCorrectAttribute = false;
let text = "";
selectedSpans.forEach((span) => {
if (span.hasAttribute("data-current")) {
hasCorrectAttribute = true;
text += span.textContent + "\n";
if ([...span.classList].some((className) => className.startsWith("endOfStanza--"))) {
text += "\n";
}
}
});
text = text.trim();
if (hasCorrectAttribute) {
SetClipboard(text);
trace.msg.log("Copied to clipboard!");
selection.removeAllRanges();
}
}
isSelecting = false;
}
};
const onClickHooked = function (event: MouseEvent): boolean | void {
if (!isSelecting) return;
const target = event.target as HTMLElement;
if (target.tagName.toLowerCase() === "span" && target.hasAttribute("data-current")) {
// Prevent default behavior and stop event propagation
event.preventDefault();
event.stopPropagation();
event.stopImmediatePropagation();
return false;
}
};
// Add event listener with capture phase to intercept events before they reach other handlers
document.addEventListener("click", onClickHooked, true);
document.addEventListener("mousedown", onMouseDown);
document.addEventListener("mouseup", onMouseUp);
// Add cleanup to unloads
unloads.add(() => {
if (styleElement.parentNode) {
styleElement.parentNode.removeChild(styleElement);
}
// Remove event listeners
document.removeEventListener("click", onClickHooked, true);
document.removeEventListener("mousedown", onMouseDown);
document.removeEventListener("mouseup", onMouseUp);
});