fix formatting and add name to CONTRIBUTORS

This commit is contained in:
fcolona 2025-07-18 18:50:00 -03:00
parent e3e46764d7
commit d76ba13ceb
3 changed files with 58 additions and 56 deletions

View file

@ -234,6 +234,7 @@ Emmanuel Ferdman <https://github.com/emmanuel-ferdman>
Sunong2008 <https://github.com/Sunrongguo2008> Sunong2008 <https://github.com/Sunrongguo2008>
Marvin Kopf <marvinkopf@outlook.com> Marvin Kopf <marvinkopf@outlook.com>
Kevin Nakamura <grinkers@grinkers.net> Kevin Nakamura <grinkers@grinkers.net>
Felipe Colona <felipedev202@gmail.com>
******************** ********************

View file

@ -49,7 +49,7 @@ export interface InputHandlerAPI {
export function getMaxOffset(node: Node) { export function getMaxOffset(node: Node) {
if (node.nodeType === Node.TEXT_NODE) { if (node.nodeType === Node.TEXT_NODE) {
if(!node.textContent) return 0; if (!node.textContent) { return 0; }
return node.textContent.length; return node.textContent.length;
} else if (node.nodeType === Node.ELEMENT_NODE) { } else if (node.nodeType === Node.ELEMENT_NODE) {
return node.childNodes.length; return node.childNodes.length;
@ -57,35 +57,34 @@ export function getMaxOffset(node: Node) {
return 0; return 0;
} }
function getCaretPosition(element: Element) {
function getCaretPosition(element: Element){
const selection = getSelection(element)!; const selection = getSelection(element)!;
const range = getRange(selection); const range = getRange(selection);
if(!range) return 0; if (!range) { return 0; }
let startNode = range.startContainer; let startNode = range.startContainer;
let startOffset = range.startOffset; let startOffset = range.startOffset;
if(!range.collapsed){ if (!range.collapsed) {
if(selection.anchorNode) startNode = selection.anchorNode; if (selection.anchorNode) { startNode = selection.anchorNode; }
startOffset = selection.anchorOffset; startOffset = selection.anchorOffset;
} }
if(startNode.nodeType == Node.TEXT_NODE){ if (startNode.nodeType == Node.TEXT_NODE) {
let counter = 0; let counter = 0;
for(const node of element.childNodes){ for (const node of element.childNodes) {
if(node === startNode) break; if (node === startNode) { break; }
if(node.textContent && node.nodeType == Node.TEXT_NODE) counter += node.textContent.length; if (node.textContent && node.nodeType == Node.TEXT_NODE) { counter += node.textContent.length; }
if(node.nodeName === "BR") counter++; if (node.nodeName === "BR") { counter++; }
} }
counter += startOffset; counter += startOffset;
return counter; return counter;
} else { } else {
let counter = 0; let counter = 0;
for(let i = 0; (i < startOffset) && (i < element.childNodes.length); i++){ for (let i = 0; (i < startOffset) && (i < element.childNodes.length); i++) {
const node = element.childNodes[i]; const node = element.childNodes[i];
if(node.textContent && node.nodeType == Node.TEXT_NODE) counter += node.textContent.length; if (node.textContent && node.nodeType == Node.TEXT_NODE) { counter += node.textContent.length; }
if(node.nodeName === "BR") counter++; if (node.nodeName === "BR") { counter++; }
} }
return counter; return counter;
} }
@ -107,12 +106,12 @@ function useInputHandler(): [InputHandlerAPI, SetupInputHandlerAction] {
const config = { const config = {
attributes: true, attributes: true,
childList: true, childList: true,
subtree: true subtree: true,
}; };
const observer = new MutationObserver(onMutation); const observer = new MutationObserver(onMutation);
function onMutation(mutationsList: MutationRecord[], observer){ function onMutation(mutationsList: MutationRecord[]) {
const element = <Element>mutationsList[0].target; const element = <Element> mutationsList[0].target;
undoManager.register(element.innerHTML, getMaxOffset(element)); undoManager.register(element.innerHTML, getMaxOffset(element));
} }
@ -150,12 +149,12 @@ function useInputHandler(): [InputHandlerAPI, SetupInputHandlerAction] {
} }
async function onInput(this: Element, event: Event): Promise<void> { async function onInput(this: Element, event: Event): Promise<void> {
if(!hasSetupObserver) { if (!hasSetupObserver) {
observer.observe(this, config); observer.observe(this, config);
hasSetupObserver = true; hasSetupObserver = true;
} }
const position = getCaretPosition(this); const position = getCaretPosition(this);
undoManager.register(this.innerHTML, position-1); undoManager.register(this.innerHTML, position - 1);
undoManager.clearRedoStack(); undoManager.clearRedoStack();
await afterInput.dispatch({ event }); await afterInput.dispatch({ event });
} }
@ -186,12 +185,10 @@ function useInputHandler(): [InputHandlerAPI, SetupInputHandlerAction] {
specialKey.dispatch({ event, action: "enter" }); specialKey.dispatch({ event, action: "enter" });
} else if (event.code === "Tab") { } else if (event.code === "Tab") {
specialKey.dispatch({ event, action: "tab" }); specialKey.dispatch({ event, action: "tab" });
} } else if ((event.ctrlKey || event.metaKey) && event.key == "z") {
else if((event.ctrlKey || event.metaKey) && event.key == "z"){
event.preventDefault(); event.preventDefault();
undoManager.undo(this); undoManager.undo(this);
} } else if ((event.ctrlKey || event.metaKey) && event.key == "y") {
else if((event.ctrlKey || event.metaKey) && event.key == "y"){
event.preventDefault(); event.preventDefault();
undoManager.redo(this); undoManager.redo(this);
} }

View file

@ -4,7 +4,7 @@ import { getMaxOffset } from "./input-handler";
type State = { type State = {
content: string; content: string;
position: number; position: number;
} };
export class UndoManager { export class UndoManager {
private undoStack: State[] = []; private undoStack: State[] = [];
@ -13,34 +13,34 @@ export class UndoManager {
private transactionStart: number = 0; private transactionStart: number = 0;
public register = this.debounce(this.pushToUndo, 500, (position: number) => this.transactionStart = position); public register = this.debounce(this.pushToUndo, 500, (position: number) => this.transactionStart = position);
public clearRedoStack(){ public clearRedoStack() {
if(this.isUpdating) return; if (this.isUpdating) { return; }
this.redoStack = []; this.redoStack = [];
} }
public pushToUndo(content: string): void { public pushToUndo(content: string): void {
if(this.isUpdating) return; if (this.isUpdating) { return; }
if (this.undoStack.length > 0 && this.undoStack[this.undoStack.length-1].content === content) return; if (this.undoStack.length > 0 && this.undoStack[this.undoStack.length - 1].content === content) { return; }
const state = {content, position: this.transactionStart} const state = { content, position: this.transactionStart };
this.undoStack.push(state); this.undoStack.push(state);
} }
private pushToRedo(content: string): void { private pushToRedo(content: string): void {
if (this.redoStack.length > 0 && this.redoStack[this.redoStack.length-1].content === content) return; if (this.redoStack.length > 0 && this.redoStack[this.redoStack.length - 1].content === content) { return; }
const state = {content: content, position: this.transactionStart} const state = { content: content, position: this.transactionStart };
this.redoStack.push(state); this.redoStack.push(state);
} }
public undo(element: Element): void{ public undo(element: Element): void {
this.isUpdating = true; this.isUpdating = true;
const undoedState = this.undoStack.pop(); const undoedState = this.undoStack.pop();
if(undoedState) this.pushToRedo(undoedState.content); if (undoedState) { this.pushToRedo(undoedState.content); }
let last: State; let last: State;
if(this.undoStack.length <= 0) last = {content: "", position: 0}; if (this.undoStack.length <= 0) { last = { content: "", position: 0 }; }
else last = this.undoStack[this.undoStack.length-1]; else { last = this.undoStack[this.undoStack.length - 1]; }
element.innerHTML = last.content; element.innerHTML = last.content;
const selection = getSelection(element)!; const selection = getSelection(element)!;
@ -49,42 +49,44 @@ export class UndoManager {
let counter = this.transactionStart; let counter = this.transactionStart;
let nodeFound: Node | null = null; let nodeFound: Node | null = null;
let nodeOffset = 0; let nodeOffset = 0;
for(const node of element.childNodes){ for (const node of element.childNodes) {
let nodeLength = node.textContent?.length || 0; let nodeLength = node.textContent?.length || 0;
if (counter <= nodeLength) { if (counter <= nodeLength) {
nodeFound = node; nodeFound = node;
nodeOffset = counter; nodeOffset = counter;
break; break;
} }
if(node.nodeType !== Node.TEXT_NODE) counter--; if (node.nodeType !== Node.TEXT_NODE) { counter--; }
counter -= nodeLength; counter -= nodeLength;
} }
if(!range){ if (!range) {
this.isUpdating = false; this.isUpdating = false;
return; return;
} }
if(!nodeFound){ if (!nodeFound) {
if(element.lastChild) range?.setStart(element.lastChild as Node, (element.lastChild?.textContent?.length || 0)) if (element.lastChild) {
range?.setStart(element.lastChild as Node, element.lastChild?.textContent?.length || 0);
}
range.collapse(true); range.collapse(true);
selection.removeAllRanges() selection.removeAllRanges();
selection.addRange(range); selection.addRange(range);
this.isUpdating = false; this.isUpdating = false;
return; return;
} }
let finalOffset = Math.min(nodeOffset, nodeFound.textContent?.length || 0); let finalOffset = Math.min(nodeOffset, nodeFound.textContent?.length || 0);
if(finalOffset > getMaxOffset(nodeFound)) finalOffset = getMaxOffset(nodeFound); if (finalOffset > getMaxOffset(nodeFound)) { finalOffset = getMaxOffset(nodeFound); }
range.setStart(nodeFound, finalOffset); range.setStart(nodeFound, finalOffset);
range.collapse(true); range.collapse(true);
selection.removeAllRanges() selection.removeAllRanges();
selection.addRange(range); selection.addRange(range);
if(this.undoStack.length > 0) this.transactionStart = this.undoStack[this.undoStack.length-1].position; if (this.undoStack.length > 0) { this.transactionStart = this.undoStack[this.undoStack.length - 1].position; }
this.isUpdating = false; this.isUpdating = false;
} }
public redo(element: Element): void { public redo(element: Element): void {
const redoedState = this.redoStack.pop(); const redoedState = this.redoStack.pop();
if(!redoedState) return; if (!redoedState) { return; }
this.transactionStart = redoedState.position; this.transactionStart = redoedState.position;
this.pushToUndo(redoedState.content); this.pushToUndo(redoedState.content);
@ -97,36 +99,38 @@ export class UndoManager {
let counter = this.transactionStart; let counter = this.transactionStart;
let nodeFound: Node | null = null; let nodeFound: Node | null = null;
let nodeOffset = 0; let nodeOffset = 0;
for(const node of element.childNodes){ for (const node of element.childNodes) {
let nodeLength = node.textContent?.length || 0; let nodeLength = node.textContent?.length || 0;
if (counter <= nodeLength) { if (counter <= nodeLength) {
nodeFound = node; nodeFound = node;
nodeOffset = counter; nodeOffset = counter;
break; break;
} }
if(node.nodeName === "BR") counter--; if (node.nodeName === "BR") { counter--; }
counter -= nodeLength; counter -= nodeLength;
} }
if(!range){ if (!range) {
this.isUpdating = false; this.isUpdating = false;
return; return;
} }
if(!nodeFound){ if (!nodeFound) {
if(element.lastChild) range?.setStart(element.lastChild as Node, (element.lastChild?.textContent?.length || 0)) if (element.lastChild) {
range?.setStart(element.lastChild as Node, element.lastChild?.textContent?.length || 0);
}
range.collapse(true); range.collapse(true);
selection.removeAllRanges() selection.removeAllRanges();
selection.addRange(range); selection.addRange(range);
this.isUpdating = false; this.isUpdating = false;
return; return;
} }
let finalOffset = Math.min(nodeOffset, nodeFound.textContent?.length || 0); let finalOffset = Math.min(nodeOffset, nodeFound.textContent?.length || 0);
if(finalOffset > getMaxOffset(nodeFound)) finalOffset = getMaxOffset(nodeFound); if (finalOffset > getMaxOffset(nodeFound)) { finalOffset = getMaxOffset(nodeFound); }
range.setStart(nodeFound, finalOffset); range.setStart(nodeFound, finalOffset);
range.collapse(true); range.collapse(true);
selection.removeAllRanges() selection.removeAllRanges();
selection.addRange(range); selection.addRange(range);
if(this.redoStack.length > 0) this.transactionStart = this.redoStack[this.redoStack.length-1].position; if (this.redoStack.length > 0) { this.transactionStart = this.redoStack[this.redoStack.length - 1].position; }
this.isUpdating = false; this.isUpdating = false;
} }
@ -135,7 +139,7 @@ export class UndoManager {
return (...args) => { return (...args) => {
const isNewTransaction = timeout === undefined; const isNewTransaction = timeout === undefined;
clearTimeout(timeout); clearTimeout(timeout);
if(isNewTransaction) onTransactionStart.call(this, args[1]) if (isNewTransaction) { onTransactionStart.call(this, args[1]); }
timeout = setTimeout(() => { timeout = setTimeout(() => {
func.call(this, args[0]); func.call(this, args[0]);
@ -143,4 +147,4 @@ export class UndoManager {
}, delay); }, delay);
}; };
} }
} }