mirror of
https://github.com/meowarex/TidaLuna-Plugins.git
synced 2026-06-18 03:43:10 +10:00
Rewrite to TidalLuna of All Plugins
This commit is contained in:
@@ -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);
|
||||
});
|
||||
Reference in New Issue
Block a user