a proof of concept realtime collaborative text editor using atproto as a sync server jake.tngl.io/y-pds/
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

EVEN MORE style improvements

+55 -29
+31 -7
app.js
··· 8 8 import { EditorState } from "prosemirror-state"; 9 9 import { EditorView } from "prosemirror-view"; 10 10 import { exampleSetup, buildMenuItems } from "prosemirror-example-setup"; 11 + import { MenuItem, joinUpItem, liftItem, undoItem, redoItem } from "prosemirror-menu"; 11 12 import { configure, client, resolve, scope } from "./oauth.js"; 12 13 import { DOC_COLLECTION, YPdsProvider } from "./y-pds.js"; 13 14 import "actor-typeahead"; ··· 145 146 did: this.props.did, 146 147 }); 147 148 149 + const ownerDid = this.props.atUri.slice("at://".length).split("/")[0]; 150 + const isOwner = ownerDid === this.props.did; 151 + 152 + const menuItems = buildMenuItems(schema); 153 + const shareItem = new MenuItem({ 154 + title: "Share document", 155 + select: () => isOwner, 156 + run: () => this.shareDialogRef.current.open(this.provider), 157 + render: () => { 158 + const btn = document.createElement("button"); 159 + btn.textContent = "Share"; 160 + btn.className = "button share-menu-button"; 161 + return btn; 162 + }, 163 + }); 164 + const menuContent = [ 165 + ...menuItems.inlineMenu, 166 + [menuItems.typeMenu], 167 + [undoItem, redoItem], 168 + [ 169 + menuItems.wrapBulletList, 170 + menuItems.wrapOrderedList, 171 + menuItems.wrapBlockQuote, 172 + joinUpItem, 173 + liftItem, 174 + ].filter(Boolean), 175 + [shareItem], 176 + ]; 177 + 148 178 const state = EditorState.create({ 149 179 schema, 150 180 plugins: [ 151 - ...exampleSetup({ schema, history: false }), 181 + ...exampleSetup({ schema, history: false, menuContent }), 152 182 ySyncPlugin(yxml), 153 183 yUndoPlugin(), 154 184 yCursorPlugin(this.provider.awareness, { ··· 186 216 } 187 217 188 218 render() { 189 - const ownerDid = this.props.atUri.slice("at://".length).split("/")[0]; 190 - const isOwner = ownerDid === this.props.did; 191 219 return html` 192 220 <div id="editor" ref=${this.editorRef}></div> 193 - ${isOwner && 194 - html`<button id="share" onClick=${() => this.shareDialogRef.current.open(this.provider)}> 195 - Share 196 - </button>`} 197 221 <${ShareDialog} ref=${this.shareDialogRef} /> 198 222 `; 199 223 }
+1
index.html
··· 17 17 "prosemirror-view": "https://esm.sh/prosemirror-view", 18 18 "prosemirror-model": "https://esm.sh/prosemirror-model", 19 19 "prosemirror-schema-basic": "https://esm.sh/prosemirror-schema-basic", 20 + "prosemirror-menu": "https://esm.sh/prosemirror-menu", 20 21 "prosemirror-example-setup": "https://esm.sh/prosemirror-example-setup", 21 22 "yjs": "https://esm.sh/yjs", 22 23 "y-prosemirror": "https://esm.sh/y-prosemirror",
+23 -22
style.css
··· 1 - @import url("/reset.css"); 1 + @import url("./reset.css"); 2 2 3 3 @import url("https://esm.sh/prosemirror-view/style/prosemirror.css"); 4 4 @import url("https://esm.sh/prosemirror-menu/style/menu.css"); 5 5 @import url("https://esm.sh/prosemirror-example-setup/style/style.css"); 6 6 @import url("https://esm.sh/prosemirror-gapcursor/style/gapcursor.css"); 7 7 8 + html { 9 + --font: 10 + system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, 11 + "Open Sans", "Helvetica Neue", sans-serif; 12 + font-family: var(--font); 13 + } 14 + 8 15 input, 9 16 select, 10 17 textarea { ··· 27 34 flex: 1; 28 35 } 29 36 30 - button { 37 + button, 38 + .ProseMirror-menuitem .share-menu-button { 31 39 padding: 0.4rem 0.75rem; 32 40 border: 1px solid #ccc; 33 41 border-radius: 4px; 34 42 background: #fff; 35 43 font-size: 0.875rem; 44 + font-family: var(--font); 36 45 cursor: pointer; 37 46 transition: background 0.15s; 38 47 ··· 52 61 html, 53 62 body { 54 63 height: 100%; 55 - } 56 - 57 - html { 58 - font-family: 59 - system-ui, 60 - -apple-system, 61 - BlinkMacSystemFont, 62 - "Segoe UI", 63 - Roboto, 64 - Oxygen, 65 - Ubuntu, 66 - Cantarell, 67 - "Open Sans", 68 - "Helvetica Neue", 69 - sans-serif; 70 64 } 71 65 72 66 body { ··· 149 143 height: 100%; 150 144 } 151 145 152 - #share { 153 - position: fixed; 154 - top: 0.6rem; 155 - right: 0.75rem; 156 - z-index: 10; 146 + .ProseMirror-menubar { 147 + display: flex; 148 + padding-right: 0; 149 + } 150 + 151 + .ProseMirror-menuitem { 152 + align-self: center; 153 + } 154 + 155 + .ProseMirror-menuitem:has(.share-menu-button) { 156 + margin-left: auto; 157 + padding: 4px; 157 158 } 158 159 159 160 dialog {