+ export const FILE_INFO: DirectoryInfo = {"isFile":false,"name":"vanilla-prosemirror","path":"/","children":[{"isFile":false,"name":"src","path":"/src","children":[{"isFile":true,"isOpen":false,"language":"typescript","name":"main.ts","path":"/src/main.ts","content":"import yorkie from '@yorkie-js/sdk';\nimport { EditorState } from 'prosemirror-state';\nimport { EditorView } from 'prosemirror-view';\nimport { Schema } from 'prosemirror-model';\nimport { schema as basicSchema } from 'prosemirror-schema-basic';\nimport { addListNodes } from 'prosemirror-schema-list';\nimport { exampleSetup } from 'prosemirror-example-setup';\nimport {\n YorkieProseMirrorBinding,\n remoteSelectionPlugin,\n} from '@yorkie-js/prosemirror';\nimport './style.css';\n\nconst statusEl = document.getElementById('status')!;\nconst editorEl = document.getElementById('editor')!;\nconst cursorOverlayEl = document.getElementById('cursor-overlay')!;\nconst editorWrapperEl = document.getElementById('editor-wrapper')!;\n\n// Extend basic schema with list nodes\nconst mySchema = new Schema({\n nodes: addListNodes(basicSchema.spec.nodes, 'paragraph block*', 'block'),\n marks: basicSchema.spec.marks,\n});\n\n// Document key from URL query param or date-based fallback\nconst params = new URLSearchParams(window.location.search);\nconst docKey =\n params.get('key') ||\n `pm-vanilla-${new Date().toISOString().substring(0, 10).replace(/-/g, '')}`;\n\nfunction setStatus(text: string, type: 'connecting' | 'connected' | 'error') {\n statusEl.textContent = text;\n statusEl.className = `status ${type === 'connecting' ? '' : type}`;\n}\n\n// Initial document\nconst initialDoc = mySchema.node('doc', null, [\n mySchema.node('heading', { level: 2 }, [\n mySchema.text('Collaborative ProseMirror'),\n ]),\n mySchema.node('paragraph', null, [\n mySchema.text('Start editing to collaborate in real time.'),\n ]),\n]);\n\nasync function main() {\n setStatus(`Connecting to Yorkie server... (doc: ${docKey})`, 'connecting');\n\n try {\n const client = new yorkie.Client({\n rpcAddr:\n (import.meta as any).env?.VITE_YORKIE_API_ADDR ||\n 'http://localhost:8080',\n apiKey: (import.meta as any).env?.VITE_YORKIE_API_KEY,\n });\n await client.activate();\n\n const doc = new yorkie.Document<Record<string, any>, Record<string, any>>(\n docKey,\n { enableDevtools: true },\n );\n await client.attach(doc, { initialPresence: {} });\n\n setStatus(`Connected — doc: ${docKey}`, 'connected');\n\n const state = EditorState.create({\n doc: initialDoc,\n plugins: [\n ...exampleSetup({ schema: mySchema }),\n remoteSelectionPlugin(),\n ],\n });\n\n const view = new EditorView(editorEl, { state });\n\n const binding = new YorkieProseMirrorBinding(view, doc, 'tree', {\n cursors: {\n enabled: true,\n overlayElement: cursorOverlayEl,\n wrapperElement: editorWrapperEl,\n },\n });\n binding.initialize();\n\n view.focus();\n } catch (e) {\n setStatus(`Connection failed: ${(e as Error).message}`, 'error');\n console.error(e);\n }\n}\n\nmain();\n"},{"isFile":true,"isOpen":false,"language":"css","name":"style.css","path":"/src/style.css","content":"body {\n font-family: system-ui, -apple-system, sans-serif;\n margin: 1rem;\n background: white;\n}\n\n.status {\n margin-bottom: 0.5rem;\n font-size: 0.9rem;\n color: #666;\n}\n.status.connected {\n color: green;\n}\n.status.error {\n color: red;\n}\n\n#editor-wrapper {\n border: 1px solid #ccc;\n border-radius: 4px;\n}\n\n/* ── ProseMirror base styles ───────────────────────────────── */\n.ProseMirror {\n position: relative;\n word-wrap: break-word;\n white-space: pre-wrap;\n white-space: break-spaces;\n -webkit-font-variant-ligatures: none;\n font-variant-ligatures: none;\n font-feature-settings: 'liga' 0;\n padding: 8px 14px;\n line-height: 1.4;\n outline: none;\n min-height: 300px;\n}\n\n.ProseMirror p:first-child,\n.ProseMirror h1:first-child,\n.ProseMirror h2:first-child,\n.ProseMirror h3:first-child {\n margin-top: 10px;\n}\n\n.ProseMirror p {\n margin-bottom: 1em;\n}\n\n.ProseMirror pre {\n white-space: pre-wrap;\n}\n\n.ProseMirror li {\n position: relative;\n}\n\n.ProseMirror ul,\n.ProseMirror ol {\n padding-left: 30px;\n}\n\n.ProseMirror blockquote {\n padding-left: 1em;\n border-left: 3px solid #eee;\n margin-left: 0;\n margin-right: 0;\n}\n\n.ProseMirror-hideselection *::selection {\n background: transparent;\n}\n.ProseMirror-hideselection *::-moz-selection {\n background: transparent;\n}\n.ProseMirror-hideselection {\n caret-color: transparent;\n}\n\n.ProseMirror-selectednode {\n outline: 2px solid #8cf;\n}\n\nli.ProseMirror-selectednode {\n outline: none;\n}\n\nli.ProseMirror-selectednode:after {\n content: '';\n position: absolute;\n left: -32px;\n right: -2px;\n top: -2px;\n bottom: -2px;\n border: 2px solid #8cf;\n pointer-events: none;\n}\n\nimg.ProseMirror-separator {\n display: inline !important;\n border: none !important;\n margin: 0 !important;\n}\n\n.ProseMirror-gapcursor {\n display: none;\n pointer-events: none;\n position: absolute;\n}\n\n.ProseMirror-gapcursor:after {\n content: '';\n display: block;\n position: absolute;\n top: -2px;\n width: 20px;\n border-top: 1px solid black;\n animation: ProseMirror-cursor-blink 1.1s steps(2, start) infinite;\n}\n\n@keyframes ProseMirror-cursor-blink {\n to {\n visibility: hidden;\n }\n}\n\n.ProseMirror-focused .ProseMirror-gapcursor {\n display: block;\n}\n\n/* ── Remote cursor overlay ─────────────────────────────────── */\n#cursor-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n pointer-events: none;\n z-index: 10;\n}\n\n/* ── ProseMirror menubar (from prosemirror-menu / example-setup) ── */\n.ProseMirror-menubar {\n border-top-left-radius: inherit;\n border-top-right-radius: inherit;\n position: relative;\n min-height: 1em;\n color: #666;\n padding: 1px 6px;\n top: 0;\n left: 0;\n right: 0;\n border-bottom: 1px solid silver;\n background: white;\n z-index: 10;\n box-sizing: border-box;\n overflow: visible;\n}\n\n.ProseMirror-icon {\n display: inline-block;\n line-height: 0.8;\n vertical-align: -2px;\n padding: 2px 8px;\n cursor: pointer;\n}\n\n.ProseMirror-menu-disabled.ProseMirror-icon {\n cursor: default;\n}\n\n.ProseMirror-icon svg {\n fill: currentColor;\n height: 1em;\n}\n\n.ProseMirror-icon span {\n vertical-align: text-top;\n}\n\n.ProseMirror-menu {\n margin: 0 -4px;\n line-height: 1;\n}\n\n.ProseMirror-menuitem {\n margin-right: 3px;\n display: inline-block;\n}\n\n.ProseMirror-menuseparator {\n border-right: 1px solid #ddd;\n margin-right: 3px;\n}\n\n.ProseMirror-menu-dropdown,\n.ProseMirror-menu-dropdown-menu {\n font-size: 90%;\n white-space: nowrap;\n}\n\n.ProseMirror-menu-dropdown {\n vertical-align: 1px;\n cursor: pointer;\n position: relative;\n padding-right: 15px;\n}\n\n.ProseMirror-menu-dropdown-wrap {\n padding: 1px 0 1px 4px;\n display: inline-block;\n position: relative;\n}\n\n.ProseMirror-menu-dropdown:after {\n content: '';\n border-left: 4px solid transparent;\n border-right: 4px solid transparent;\n border-top: 4px solid currentColor;\n opacity: 0.6;\n position: absolute;\n right: 4px;\n top: calc(50% - 2px);\n}\n\n.ProseMirror-menu-dropdown-menu,\n.ProseMirror-menu-submenu {\n position: absolute;\n background: white;\n color: #666;\n border: 1px solid #aaa;\n padding: 2px;\n}\n\n.ProseMirror-menu-dropdown-menu {\n z-index: 15;\n min-width: 6em;\n}\n\n.ProseMirror-menu-dropdown-item {\n cursor: pointer;\n padding: 2px 8px 2px 4px;\n}\n\n.ProseMirror-menu-dropdown-item:hover {\n background: #f2f2f2;\n}\n\n.ProseMirror-menu-submenu-wrap {\n position: relative;\n margin-right: -4px;\n}\n\n.ProseMirror-menu-submenu-label:after {\n content: '';\n border-top: 4px solid transparent;\n border-bottom: 4px solid transparent;\n border-left: 4px solid currentColor;\n opacity: 0.6;\n position: absolute;\n right: 4px;\n top: calc(50% - 4px);\n}\n\n.ProseMirror-menu-submenu {\n display: none;\n min-width: 4em;\n left: 100%;\n top: -3px;\n}\n\n.ProseMirror-menu-active {\n background: #eee;\n border-radius: 4px;\n}\n\n.ProseMirror-menu-disabled {\n opacity: 0.3;\n}\n\n.ProseMirror-menu-submenu-wrap:hover .ProseMirror-menu-submenu,\n.ProseMirror-menu-submenu-wrap-active .ProseMirror-menu-submenu {\n display: block;\n}\n\n.ProseMirror-textblock-dropdown {\n min-width: 3em;\n}\n\n.ProseMirror-tooltip .ProseMirror-menu {\n width: fit-content;\n white-space: pre;\n}\n\n/* example-setup specific */\n.ProseMirror-example-setup-style hr {\n padding: 2px 10px;\n border: none;\n margin: 1em 0;\n}\n\n.ProseMirror-example-setup-style hr:after {\n content: '';\n display: block;\n height: 1px;\n background-color: silver;\n line-height: 2px;\n}\n\n.ProseMirror-example-setup-style img {\n cursor: default;\n}\n\n.ProseMirror-prompt {\n background: white;\n padding: 5px 10px 5px 15px;\n border: 1px solid silver;\n position: fixed;\n border-radius: 3px;\n z-index: 11;\n box-shadow: -0.5px 2px 5px rgba(0, 0, 0, 0.2);\n}\n\n.ProseMirror-prompt h5 {\n margin: 0;\n font-weight: normal;\n font-size: 100%;\n color: #444;\n}\n\n.ProseMirror-prompt input[type='text'],\n.ProseMirror-prompt textarea {\n background: #eee;\n border: none;\n outline: none;\n}\n\n.ProseMirror-prompt input[type='text'] {\n padding: 0 4px;\n}\n\n.ProseMirror-prompt-close {\n position: absolute;\n left: 2px;\n top: 1px;\n color: #666;\n border: none;\n background: transparent;\n padding: 0;\n}\n\n.ProseMirror-prompt-close:after {\n content: '\\00d7';\n font-size: 12px;\n}\n\n.ProseMirror-invalid {\n background: #ffc;\n border: 1px solid #cc7;\n border-radius: 4px;\n padding: 5px 10px;\n position: absolute;\n min-width: 10em;\n}\n\n.ProseMirror-prompt-buttons {\n margin-top: 5px;\n display: none;\n}\n"},{"isFile":true,"isOpen":false,"language":"typescript","name":"vite-env.d.ts","path":"/src/vite-env.d.ts","content":"/// <reference types=\"vite/client\" />\n"}]},{"isFile":true,"isOpen":false,"language":"","name":".env","path":"/.env","content":"VITE_YORKIE_API_ADDR='http://localhost:8080'\nVITE_YORKIE_API_KEY=''\n"},{"isFile":true,"isOpen":false,"language":"production","name":".env.production","path":"/.env.production","content":""},{"isFile":true,"isOpen":false,"language":"","name":".gitignore","path":"/.gitignore","content":"# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"},{"isFile":true,"isOpen":false,"language":"markdown","name":"README.md","path":"/README.md","content":"# Yorkie Vanilla ProseMirror Example\n\n<p>\n <a href=\"https://yorkie.dev/yorkie-js-sdk/examples/vanilla-prosemirror/\" target=\"_blank\">\n <img src=\"https://img.shields.io/badge/preview-message?style=flat-square&logo=data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMTUiIHZpZXdCb3g9IjAgMCAyNCAxNSIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTYuODU3MTcgMi43ODE5OUwxMS4yNzUxIDkuMTI2NzhDMTEuNTU0NCA5LjUyODAxIDEyLjEwNjIgOS42MjY3NiAxMi41MDc0IDkuMzQ3NDRDMTIuNTkzNCA5LjI4NzUgMTIuNjY4MSA5LjIxMjggMTIuNzI4MSA5LjEyNjc4TDE3LjE0NiAyLjc4MTk5QzE3LjcwNDggMS45Nzk1NCAxNy41MDcyIDAuODc2MTMxIDE2LjcwNDggMC4zMTc0OTRDMTYuNDA4IDAuMTEwODM3IDE2LjA1NSAwIDE1LjY5MzIgMEg4LjMxMDAxQzcuMzMyMiAwIDYuNTM5NTUgMC43OTI2NTQgNi41Mzk1NSAxLjc3MDQ2QzYuNTM5NjggMi4xMzIxMSA2LjY1MDUxIDIuNDg1MTEgNi44NTcxNyAyLjc4MTk5WiIgZmlsbD0iIzUxNEM0OSIvPgo8cGF0aCBkPSJNMTMuODA4OSAxNC4yMzg4QzE0LjEyMzEgMTQuNDE4IDE0LjQ4NDcgMTQuNDk2NiAxNC44NDUgMTQuNDY0MkwyMi45MjYgMTMuNzM1QzIzLjU3NTMgMTMuNjc2NSAyNC4wNTQgMTMuMTAyNyAyMy45OTU1IDEyLjQ1MzVDMjMuOTkyNCAxMi40MTkyIDIzLjk4NzggMTIuMzg1MSAyMy45ODE3IDEyLjM1MTNDMjMuNzM4OSAxMC45OTY4IDIzLjI2MTEgOS42OTUyNyAyMi41Njk5IDguNTA1NDZDMjEuODc4NiA3LjMxNTY1IDIwLjk4NDggNi4yNTU3NyAxOS45Mjg2IDUuMzczOTFDMTkuNDI4MiA0Ljk1NjE0IDE4LjY4MzkgNS4wMjMwNyAxOC4yNjYyIDUuNTIzNTZDMTguMjQ0MiA1LjU0OTkgMTguMjIzMyA1LjU3NzI2IDE4LjIwMzYgNS42MDU1MUwxMy41NjcgMTIuMjY0MUMxMy4zNjAzIDEyLjU2MSAxMy4yNDk1IDEyLjkxNCAxMy4yNDk1IDEzLjI3NThWMTMuMjUzN0MxMy4yNDk1IDEzLjQ1NjIgMTMuMzAxNiAxMy42NTU0IDEzLjQwMDggMTMuODMxOUMxMy41MDUgMTQuMDA1NCAxMy42NTIxIDE0LjE0OTMgMTMuODI4MSAxNC4yNDk2IiBmaWxsPSIjRkRDNDMzIi8+CjxwYXRoIGQ9Ik0xMC42NDE2IDEzLjc0MzRDMTAuNTM3NSAxMy45NTU5IDEwLjM3MiAxNC4xMzIyIDEwLjE2NjUgMTQuMjQ5NEwxMC4xOTE1IDE0LjIzNTFDOS44NzczNCAxNC40MTQzIDkuNTE1NjkgMTQuNDkyOSA5LjE1NTQ0IDE0LjQ2MDVMMS4wNzQ0MSAxMy43MzEzQzEuMDQwMTggMTMuNzI4MyAxLjAwNjA3IDEzLjcyMzcgMC45NzIyMjUgMTMuNzE3NkMwLjMzMDYyIDEzLjYwMjUgLTAuMDk2MzExOSAxMi45ODkyIDAuMDE4NzI0MiAxMi4zNDc2QzAuMjYxNTIyIDEwLjk5MyAwLjczOTM1NCA5LjY5MTU2IDEuNDMwNDYgOC41MDE2M0MyLjEyMTU3IDcuMzExNjkgMy4wMTU1MSA2LjI1MjA2IDQuMDcxODQgNS4zNzAwOEM0LjA5ODE4IDUuMzQ4MDYgNC4xMjU1NCA1LjMyNzE5IDQuMTUzNzkgNS4zMDc0N0M0LjY4ODc2IDQuOTM1IDUuNDI0MjcgNS4wNjY3MSA1Ljc5Njg3IDUuNjAxNjhMMTAuNDMzNCAxMi4yNjA0QzEwLjY0MDEgMTIuNTU3MyAxMC43NTA5IDEyLjkxMDMgMTAuNzUwOSAxMy4yNzIxVjEzLjI0MzJDMTAuNzUwOSAxMy40Nzk3IDEwLjY3OTggMTMuNzExIDEwLjU0NjggMTMuOTA2NyIgZmlsbD0iI0ZEQzQzMyIvPgo8L3N2Zz4K&color=FEF3D7\" alt=\"Live Preview\" />\n </a>\n</p>\n\n## How to run demo\n\nAt project root, run below command to start Yorkie server.\n\n```bash\n$ docker compose -f docker/docker-compose.yml up --build -d\n```\n\nThen install dependencies and run the demo.\n\n```bash\n# In the root directory of the repository.\n$ pnpm install\n```\n\nNow you can run the demo.\n\n```bash\n# In the root directory of the repository.\n$ pnpm vanilla-prosemirror dev\n\n# Or in the directory of the example.\n$ pnpm dev\n```\n"},{"isFile":true,"isOpen":false,"language":"markup","name":"index.html","path":"/index.html","content":"<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Yorkie + ProseMirror Example</title>\n </head>\n <body>\n <div id=\"status\" class=\"status\">Connecting...</div>\n <div id=\"editor-wrapper\" style=\"position: relative;\">\n <div id=\"editor\"></div>\n <div id=\"cursor-overlay\"></div>\n </div>\n <script type=\"module\" src=\"/src/main.ts\"></script>\n </body>\n</html>\n"},{"isFile":true,"isOpen":false,"language":"json","name":"package.json","path":"/package.json","content":"{\n \"name\": \"vanilla-prosemirror\",\n \"private\": true,\n \"version\": \"0.0.0\",\n \"type\": \"module\",\n \"scripts\": {\n \"dev\": \"vite\",\n \"build\": \"tsc && vite build\",\n \"preview\": \"vite preview\"\n },\n \"devDependencies\": {\n \"typescript\": \"^5.9.3\",\n \"vite\": \"^7.2.6\"\n },\n \"dependencies\": {\n \"@yorkie-js/sdk\": \"^0.6.47\",\n \"@yorkie-js/prosemirror\": \"^0.6.47\",\n \"prosemirror-commands\": \"^1.6.2\",\n \"prosemirror-example-setup\": \"^1.2.2\",\n \"prosemirror-history\": \"^1.4.1\",\n \"prosemirror-keymap\": \"^1.2.2\",\n \"prosemirror-model\": \"^1.23.0\",\n \"prosemirror-schema-basic\": \"^1.2.3\",\n \"prosemirror-schema-list\": \"^1.5.0\",\n \"prosemirror-state\": \"^1.4.3\",\n \"prosemirror-view\": \"^1.37.0\"\n }\n}\n"},{"isFile":true,"isOpen":false,"language":"json","name":"tsconfig.json","path":"/tsconfig.json","content":"{\n \"compilerOptions\": {\n \"target\": \"ESNext\",\n \"useDefineForClassFields\": true,\n \"module\": \"ESNext\",\n \"lib\": [\"ESNext\", \"DOM\"],\n \"moduleResolution\": \"Node\",\n \"strict\": true,\n \"sourceMap\": true,\n \"resolveJsonModule\": true,\n \"isolatedModules\": true,\n \"esModuleInterop\": true,\n \"noEmit\": true,\n \"skipLibCheck\": true,\n \"paths\": {\n \"@yorkie-js/sdk/src/*\": [\"../../packages/sdk/src/*\"],\n \"@yorkie-js/prosemirror\": [\"../../packages/prosemirror/src\"]\n }\n },\n \"include\": [\"src\"]\n}\n"},{"isFile":true,"isOpen":false,"language":"javascript","name":"vite.config.js","path":"/vite.config.js","content":"import { defineConfig } from 'vite';\nimport path, { dirname } from 'path';\nimport { fileURLToPath } from 'url';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n base: '',\n resolve: {\n alias: [\n {\n find: '@yorkie-js/sdk/src',\n replacement: path.resolve(__dirname, '../../packages/sdk/src'),\n },\n {\n find: '@yorkie-js/prosemirror',\n replacement: path.resolve(__dirname, '../../packages/prosemirror/src'),\n },\n ],\n },\n});\n"}]}
0 commit comments