Playing with Y.js
This commit is contained in:
Generated
+66
@@ -8,7 +8,12 @@
|
|||||||
"name": "story-editor",
|
"name": "story-editor",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"lodash": "^4.17.21",
|
||||||
|
"yjs": "^13.6.27"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/lodash": "^4.17.20",
|
||||||
"prettier": "^3.6.2",
|
"prettier": "^3.6.2",
|
||||||
"typescript": "^5.9.2",
|
"typescript": "^5.9.2",
|
||||||
"vite": "^7.1.1"
|
"vite": "^7.1.1"
|
||||||
@@ -743,6 +748,13 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/lodash": {
|
||||||
|
"version": "4.17.20",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.20.tgz",
|
||||||
|
"integrity": "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/esbuild": {
|
"node_modules/esbuild": {
|
||||||
"version": "0.25.8",
|
"version": "0.25.8",
|
||||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz",
|
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz",
|
||||||
@@ -815,6 +827,43 @@
|
|||||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/isomorphic.js": {
|
||||||
|
"version": "0.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.5.tgz",
|
||||||
|
"integrity": "sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "GitHub Sponsors ❤",
|
||||||
|
"url": "https://github.com/sponsors/dmonad"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lib0": {
|
||||||
|
"version": "0.2.114",
|
||||||
|
"resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.114.tgz",
|
||||||
|
"integrity": "sha512-gcxmNFzA4hv8UYi8j43uPlQ7CGcyMJ2KQb5kZASw6SnAKAf10hK12i2fjrS3Cl/ugZa5Ui6WwIu1/6MIXiHttQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"isomorphic.js": "^0.2.4"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"0ecdsa-generate-keypair": "bin/0ecdsa-generate-keypair.js",
|
||||||
|
"0gentesthtml": "bin/gentesthtml.js",
|
||||||
|
"0serve": "bin/0serve.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "GitHub Sponsors ❤",
|
||||||
|
"url": "https://github.com/sponsors/dmonad"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lodash": {
|
||||||
|
"version": "4.17.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/nanoid": {
|
"node_modules/nanoid": {
|
||||||
"version": "3.3.11",
|
"version": "3.3.11",
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
||||||
@@ -1054,6 +1103,23 @@
|
|||||||
"optional": true
|
"optional": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"node_modules/yjs": {
|
||||||
|
"version": "13.6.27",
|
||||||
|
"resolved": "https://registry.npmjs.org/yjs/-/yjs-13.6.27.tgz",
|
||||||
|
"integrity": "sha512-OIDwaflOaq4wC6YlPBy2L6ceKeKuF7DeTxx+jPzv1FHn9tCZ0ZwSRnUBxD05E3yed46fv/FWJbvR+Ud7x0L7zw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"lib0": "^0.2.99"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.0.0",
|
||||||
|
"npm": ">=8.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "GitHub Sponsors ❤",
|
||||||
|
"url": "https://github.com/sponsors/dmonad"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,8 +13,13 @@
|
|||||||
"format": "prettier --write ."
|
"format": "prettier --write ."
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/lodash": "^4.17.20",
|
||||||
"prettier": "^3.6.2",
|
"prettier": "^3.6.2",
|
||||||
"typescript": "^5.9.2",
|
"typescript": "^5.9.2",
|
||||||
"vite": "^7.1.1"
|
"vite": "^7.1.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"lodash": "^4.17.21",
|
||||||
|
"yjs": "^13.6.27"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+65
@@ -1,8 +1,73 @@
|
|||||||
|
import _ from 'lodash';
|
||||||
import './style.css';
|
import './style.css';
|
||||||
|
import * as Y from 'yjs';
|
||||||
|
|
||||||
const app = document.querySelector<HTMLDivElement>('#app')!;
|
const app = document.querySelector<HTMLDivElement>('#app')!;
|
||||||
|
|
||||||
app.innerHTML = `
|
app.innerHTML = `
|
||||||
<h1>Story Editor</h1>
|
<h1>Story Editor</h1>
|
||||||
<p>Your TypeScript + Vite app is ready!</p>
|
<p>Your TypeScript + Vite app is ready!</p>
|
||||||
|
<textarea id="ta1" rows=10 cols=80></textarea>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<textarea id="ta2" rows=10 cols=80></textarea>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const ta1 = document.getElementById('ta1') as HTMLTextAreaElement;
|
||||||
|
const ta2 = document.getElementById('ta2') as HTMLTextAreaElement;
|
||||||
|
|
||||||
|
function bind(text: Y.Text, el: HTMLTextAreaElement) {
|
||||||
|
el.value = text.toString();
|
||||||
|
|
||||||
|
const handle = _.debounce(() => {
|
||||||
|
let prev = text.toString();
|
||||||
|
let curr = el.value;
|
||||||
|
let i = 0;
|
||||||
|
for (i = 0; prev[i] && prev[i] == curr[i]; i++) {}
|
||||||
|
const offset = i;
|
||||||
|
prev = prev.slice(offset);
|
||||||
|
curr = curr.slice(offset);
|
||||||
|
for (
|
||||||
|
i = 0;
|
||||||
|
prev[prev.length - 1 - i] &&
|
||||||
|
prev[prev.length - 1 - i] === curr[curr.length - 1 - i];
|
||||||
|
i++
|
||||||
|
) {}
|
||||||
|
prev = prev.slice(0, prev.length - i);
|
||||||
|
curr = curr.slice(0, curr.length - i);
|
||||||
|
|
||||||
|
text.doc!.transact(() => {
|
||||||
|
text.delete(offset, prev.length);
|
||||||
|
text.insert(offset, curr);
|
||||||
|
});
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
el.addEventListener('input', (_ev) => {
|
||||||
|
handle();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const doc1 = new Y.Doc();
|
||||||
|
bind(doc1.getText(), ta1);
|
||||||
|
const doc2 = new Y.Doc();
|
||||||
|
bind(doc2.getText(), ta2);
|
||||||
|
|
||||||
|
doc1.on('update', async (update) => {
|
||||||
|
await new Promise((res, _rej) =>
|
||||||
|
setTimeout(res, 3000 * Math.random() + 2000)
|
||||||
|
);
|
||||||
|
Y.applyUpdate(doc2, update);
|
||||||
|
ta2.value = doc2.getText().toString();
|
||||||
|
});
|
||||||
|
doc2.on('update', async (update) => {
|
||||||
|
await new Promise((res, _rej) =>
|
||||||
|
setTimeout(res, 3000 * Math.random() + 2000)
|
||||||
|
);
|
||||||
|
Y.applyUpdate(doc1, update);
|
||||||
|
ta1.value = doc1.getText().toString();
|
||||||
|
});
|
||||||
|
|
||||||
|
doc1.getText().insert(0, 'foo bar baz');
|
||||||
|
ta1.value = doc1.getText().toString();
|
||||||
|
|
||||||
|
(window as any).Y = Y;
|
||||||
|
|||||||
Reference in New Issue
Block a user