y-prosemirror-awork
v1.3.5-awork.5
Published
Prosemirror bindings for Yjs with string/integer attribute compatibility - Awork fork
Downloads
7,711
Maintainers
Readme
y-prosemirror-awork
🎉 Shoutout
This fork was created to solve real-world interoperability challenges between JavaScript and .NET CRDT implementations. Special thanks to the original y-prosemirror team for creating such a solid foundation that made this enhancement possible!
This is a fork of y-prosemirror with enhanced string/integer attribute compatibility for better interoperability with YDotNet and other CRDT implementations.
Why this fork?
The original y-prosemirror expects attributes to maintain their JavaScript types (e.g., level: 1 as a number). However, some CRDT implementations like YDotNet only support string attributes. This creates type mismatches when:
- YDotNet stores
level: "1"(string) - y-prosemirror expects
level: 1(number)
This fork adds intelligent type conversion and comparison to handle these mismatches gracefully.
What's Changed
Loose Equality Comparison: Attributes are compared using type coercion for common cases:
"1"equals1(string/number)"true"equalstrue(string/boolean)
Automatic Type Conversion: When creating ProseMirror nodes from Yjs data, attributes are automatically converted to match the schema's expected types based on default values.
No Unnecessary Updates: Prevents redundant syncing when only the type differs but the value is equivalent.
Installation
npm install y-prosemirror-aworkUsage
Use exactly like the original y-prosemirror:
import { ySyncPlugin, yCursorPlugin, yUndoPlugin, undo, redo } from 'y-prosemirror-awork'
// Same API as original y-prosemirror
const provider = new WebrtcProvider(/* ... */)
const type = ydoc.getXmlFragment('prosemirror')
const plugins = [
ySyncPlugin(type),
yCursorPlugin(provider.awareness),
yUndoPlugin(),
keymap({
'Mod-z': undo,
'Mod-y': redo,
'Mod-Shift-z': redo
})
]Detailed Modifications
1. looseEquals() Function (sync-plugin.js, lines 955-1000)
Added a new function that performs type-coerced equality comparison:
// Returns true if values are equal after type coercion
// Examples: 1 equals "1", true equals "true", [279] equals "279"
function looseEquals(a, b) {
if (a === b) return true
// Handle array/string comparison for colwidth
if (Array.isArray(a) && typeof b === 'string') {
const num = Number(b)
if (!isNaN(num) && a.length === 1 && a[0] === num) {
return true
}
}
if (typeof a === 'string' && Array.isArray(b)) {
const num = Number(a)
if (!isNaN(num) && b.length === 1 && b[0] === num) {
return true
}
}
// Handle number/string comparison
if (typeof a === 'number' && typeof b === 'string') {
return a === Number(b)
}
if (typeof a === 'string' && typeof b === 'number') {
return Number(a) === b
}
// Handle boolean/string comparison
if (typeof a === 'boolean' && typeof b === 'string') {
return a === (b === 'true')
}
if (typeof a === 'string' && typeof b === 'boolean') {
return (a === 'true') === b
}
// Fallback to string comparison
return String(a) === String(b)
}2. Type Conversion in createNodeFromYElement() (sync-plugin.js, lines 808-848)
Automatically converts string attributes to match ProseMirror schema expectations:
- Converts
"1"→1when schema expects a number - Converts
"true"→truewhen schema expects a boolean - Special handling for
colwidthattribute:- Single value:
"279"→[279] - Multiple values:
"100,200,300"→[100, 200, 300] - Matches TipTap's TableCell parseHTML behavior
- Single value:
- Uses schema default values to determine expected types
3. Modified equalAttrs() (sync-plugin.js, line 991)
Changed from strict equality to use looseEquals():
eq = key === 'ychange' || looseEquals(l, r) || // AWORK CUSTOM4. Modified updateYFragment() (sync-plugin.js, line 1231)
Uses looseEquals() when comparing attributes to prevent unnecessary updates:
if (!looseEquals(yDomAttrs[key], pAttrs[key]) && key !== 'ychange') {All modifications are marked with // AWORK CUSTOM comments for easy identification.
Compatibility
This fork maintains full API compatibility with y-prosemirror v1.3.5. You can use it as a drop-in replacement.
Publishing a New Version
To publish a new version of y-prosemirror-awork:
1. Prepare the Package
# Run the publish preparation script
./publish-awork.shThis script will:
- Copy
package-awork.jsontopackage.json - Copy
README-awork.mdtoREADME.md - Install dependencies with
npm install - Build the package with
npm run dist - Run tests with
npm test
2. Test Locally
Before publishing, test the package locally:
# Create a local package
npm pack
# In your test project, install the local package
npm install path/to/y-prosemirror-awork-1.3.5-awork.X.tgz3. Update Version
Update the version in package-awork.json:
"version": "1.3.5-awork.X" // Increment X for each release4. Publish to npm
# Login to npm (if not already logged in)
npm login
# Publish the package
npm publish5. Create GitHub Release (Optional)
- Push code to GitHub repository: https://github.com/awork/y-prosemirror-awork
- Create a new release with the version tag
- Document the changes in the release notes
License
MIT (same as original y-prosemirror)
Credits
Original y-prosemirror by Kevin Jahns and the Yjs team. Fork modifications for Awork by the Awork team.
