I was thinking of having a go at an implementation for y-textarea. I’m pretty new to Yjs. I see that there was a similar implementation for old y-js here: GitHub - y-js/y-text: Text Type for Yjs. So I was thinking of basing the implementation on that. Any advice?.
I took a first pass at implementing using new yjs here:
I would have forked y-textarea but it was an empty repo. Will fork and raise PR if the repo gets populated with something.
Implementation based on y-text implementation, doesn’t have ‘presence’, but does support shared cursors.
In-case anybody finds this and is interested I released put this on npm,
y-textarea - npm.
1 Like
Here’s a very simple standalone textarea binding. (Unbinding is left as an exercise for the reader )
function yTextArea(ta: HTMLTextAreaElement, text: Y.Text, localTransactionOrigin = 'local') {
let oldContent = '';
text.observe((e, txn) => {
if (txn.origin === localTransactionOrigin) return;
let pos = 0;
e.changes.delta.forEach(d => {
if ('retain' in d) {
pos += d.retain ?? 0;
} else if ('delete' in d) {
ta.setRangeText('', pos, pos + (d.delete ?? 0));
} else if ('insert' in d) {
const s = d.insert as string;
ta.setRangeText(s, pos, pos);
pos += s.length;
}
});
oldContent = ta.value;
});
ta.addEventListener('input', () => {
const newContent = ta.value;
let start = 0;
while ((start < oldContent.length)
&& (start < newContent.length)
&& (oldContent.charCodeAt(start) === newContent.charCodeAt(start)))
{
start++;
}
const delta = newContent.length - oldContent.length;
let end = newContent.length;
while ((end > start)
&& (end - delta > start)
&& (oldContent.charCodeAt(end - delta - 1) === newContent.charCodeAt(end - 1)))
{
end--;
}
// This technique isn't perfect: consider inserting 'x' into
// the middle of 'AxxxxB' - we will detect an 'x' added at the
// *end* of the run of 'x's, rather than in the middle.
const oldLength = (end - delta) - start;
text.doc!.transact(() => {
if (oldLength > 0) text.delete(start, oldLength);
if (end > start) text.insert(start, newContent.substring(start, end));
}, localTransactionOrigin);
oldContent = newContent;
});
}