[
  {
    "path": ".github/FUNDING.yml",
    "content": "patreon: marijn\ncustom: ['https://www.paypal.com/paypalme/marijnhaverbeke', 'https://marijnhaverbeke.nl/fund/']\ngithub: marijnh"
  },
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "content": "<!--\n\nPlease provide:\n\n - Necessary steps to reproduce the issue. If the editor has to be set up in a specific way, running code (minimal code, not a dump of your project) is very useful.\n\n - If this might be browser-related, tell us the browsers and platforms you tested on.\n\n-->\n"
  },
  {
    "path": ".gitignore",
    "content": ".tern-port\n/node_modules\n/model\n/transform\n/state\n/view\n/history\n/collab\n/commands\n/inputrules\n/keymap\n/search\n/schema-basic\n/schema-list\n/schema-table\n/menu\n/markdown\n/dropcursor\n/test-builder\n/gapcursor\n/changeset\n/website\n/example-setup\n/tables\n/notes.txt\n/yarn.lock\n/bin/.pm-dev.pid\n/demo/demo.js\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# How to contribute\n\n- [Getting help](#getting-help)\n- [Submitting bug reports](#submitting-bug-reports)\n- [Contributing code](#contributing-code)\n\n## Getting help\n\nCommunity discussion, questions, and informal bug reporting is done on the\n[discuss.ProseMirror forum](http://discuss.prosemirror.net).\n\n## Submitting bug reports\n\nReport bugs on the\n[GitHub issue tracker](http://github.com/prosemirror/prosemirror/issues).\nBefore reporting a bug, please read these pointers.\n\n- The issue tracker is for *bugs*, not requests for help. Questions\n  should be asked on the [forum](http://discuss.prosemirror.net).\n\n- Include information about the version of the code that exhibits the\n  problem. For browser-related issues, include the browser and browser\n  version on which the problem occurred.\n\n- Mention very precisely what went wrong. \"X is broken\" is not a good\n  bug report. What did you expect to happen? What happened instead?\n  Describe the exact steps a maintainer has to take to make the\n  problem occur. A screencast can be useful, but is no substitute for\n  a textual description.\n\n- A great way to make it easy to reproduce your problem, if it can not\n  be trivially reproduced on the website demos, is to submit a script\n  that triggers the issue.\n\n## Contributing code\n\nCode written by \"AI\" language models (either partially or fully) is\n**not welcome**. Both because you cannot guarantee it's not parroting\ncopyrighted content, and because it tends to be of low quality and a\nwaste of time to review.\n\n- Make sure you have a [GitHub Account](https://github.com/signup/free)\n\n- Fork the relevant repository\n  ([how to fork a repo](https://help.github.com/articles/fork-a-repo))\n\n- Create a local checkout of the code. You can use the\n  [main repository](https://github.com/prosemirror/prosemirror) to\n  easily check out all core modules.\n\n- Make your changes, and commit them\n\n- Follow the code style of the rest of the project (see below).\n\n- If your changes are easy to test or likely to regress, add tests in\n  the relevant `test/` directory. Either put them in an existing\n  `test-*.js` file, if they fit there, or add a new file.\n\n- Make sure all tests pass. Run `npm run test` to verify tests pass.\n\n- Submit a pull request ([how to create a pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork)).\n  Don't put more than one feature/fix in a single pull request.\n\nBy contributing code to ProseMirror you\n\n - Agree to license the contributed code under the project's [MIT\n   license](https://github.com/ProseMirror/prosemirror/blob/master/LICENSE).\n\n - Confirm that you have the right to contribute and license the code\n   in question. (Either you hold all rights on the code, or the rights\n   holder has explicitly granted the right to use it like this,\n   through a compatible open source license or through a direct\n   agreement with you.)\n\n### Coding standards\n\n- TypeScript, targeting an ES5 runtime (i.e. don't use library\n  elements added by ES6, don't use ES7/ES.next syntax).\n\n- 2 spaces per indentation level, no tabs.\n\n- No semicolons except when necessary.\n\n- Follow the surrounding code when it comes to spacing, brace\n  placement, etc.\n\n- Brace-less single-statement bodies are encouraged (whenever they\n  don't impact readability).\n\n- [getdocs](https://github.com/marijnh/getdocs-ts)-style doc comments\n  above items that are part of the public API.\n\n- ProseMirror does *not* follow JSHint or JSLint prescribed style.\n  Patches that try to 'fix' code to pass one of these linters will not\n  be accepted.\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (C) 2015-2017 by Marijn Haverbeke <marijn@haverbeke.berlin> and others\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# prosemirror\n\n[ [**WEBSITE**](https://prosemirror.net) | [**ISSUES**](https://github.com/prosemirror/prosemirror/issues) | [**FORUM**](https://discuss.prosemirror.net) ]\n\nProseMirror is a well-behaved rich semantic content editor based on\ncontentEditable, with support for collaborative editing and custom\ndocument schemas.\n\nThe ProseMirror library consists of a number of separate\n[modules](https://github.com/prosemirror/). This repository just\nserves as a central issue tracker, and holds a script to help easily\ncheck out all the core modules for development.\n\nThe [project page](https://prosemirror.net) has more information, a\nnumber of [examples](https://prosemirror.net/examples/) and the\n[documentation](https://prosemirror.net/docs/).\n\nThis code is released under an\n[MIT license](https://github.com/prosemirror/prosemirror/tree/master/LICENSE).\nThere's a [forum](http://discuss.prosemirror.net) for general\ndiscussion and support requests, and the\n[Github bug tracker](https://github.com/prosemirror/prosemirror/issues)\nis the place to report issues.\n\n**STOP READING HERE IF YOU'RE SIMPLY _USING_ PROSEMIRROR. YOU CAN\nINSTALL THE SEPARATE [NPM\nMODULES](https://www.npmjs.com/search?q=prosemirror-) FOR THAT. THE\nINSTRUCTIONS BELOW ONLY APPLY WHEN _DEVELOPING_ PROSEMIRROR!**\n\n## Setting up a dev environment\n\nClone this repository, and make sure you have\n[node](https://nodejs.org/en/) installed.\nNext, from the cloned directory run:\n\n    bin/pm install\n\nThis will fetch the submodules, install their dependencies, and build\nthem.\n\nThe `bin/pm` script in this repository provides functionality for\nworking with the repositories:\n\n * `bin/pm build` rebuilds all the modules\n\n * `bin/pm watch` sets up a process that automatically rebuilds the\n   modules when they change\n\n * `bin/pm status` prints the git status of all submodules\n\n * `bin/pm commit <args>` runs `git commit` with the given arguments\n   in all submodules that have pending changes\n\n * `bin/pm test` runs the (non-browser) tests in all modules\n\n * `bin/pm push` runs `git push` in all modules\n\n * `bin/pm grep <pattern>` greps through the source code for the\n   modules for the given pattern\n\n * `bin/pm dev-start` starts a server that rebuilds the packages\n   whenever their sources change, and exposes the demo (`demo/*`)\n   under a webserver on port 8080\n\n(Functionality for managing releases will be added in the future.)\n\n## Community\n\nDevelopment of ProseMirror happens in the various repositories exposed\nunder the [ProseMirror](https://github.com/ProseMirror) organization\non GitHub. Bugs for core packages are tracked in the [bug\ntracker](https://github.com/prosemirror/prosemirror/issues) for the\nmeta repository.\n\nWe aim to be an inclusive, welcoming community. To make that explicit,\nwe have a [code of\nconduct](http://contributor-covenant.org/version/1/1/0/) that applies\nto communication around the project.\n"
  },
  {
    "path": "bin/pm.js",
    "content": "#!/usr/bin/env node\n\n// NOTE: Don't require anything from node_modules here, since the\n// install script has to be able to run _before_ that exists.\nlet child = require(\"child_process\"), fs = require(\"fs\"), path = require(\"path\")\n\nconst {join} = path\n\nlet main = [\"model\", \"transform\", \"state\", \"view\",\n            \"keymap\", \"inputrules\", \"history\", \"collab\", \"commands\", \"gapcursor\",\n            \"schema-basic\", \"schema-list\"]\nlet mods = main.concat([\"menu\", \"example-setup\", \"markdown\", \"dropcursor\", \"test-builder\", \"changeset\", \"search\"])\nlet modsAndWebsite = mods.concat(\"website\")\n\nlet projectDir = join(__dirname, \"..\")\n\nfunction joinP(...args) {\n  return join(projectDir, ...args)\n}\n\nfunction mainFile(pkg) {\n  let index = joinP(pkg, \"src\", \"index.ts\"), self = joinP(pkg, \"src\", pkg + \".ts\")\n  if (fs.existsSync(index)) return index\n  if (fs.existsSync(self)) return self\n  throw new Error(\"Couldn't find a main file for \" + pkg)\n}\n\nfunction start() {\n  let command = process.argv[2]\n  if (command && ![\"install\", \"--help\", \"modules\"].includes(command)) assertInstalled()\n  let args = process.argv.slice(3)\n  let cmdFn = {\n    \"status\": status,\n    \"commit\": commit,\n    \"install\": install,\n    \"build\": build,\n    \"test\": test,\n    \"push\": push,\n    \"pull\": pull,\n    \"grep\": grep,\n    \"run\": runCmd,\n    \"watch\": watch,\n    \"changes\": changes,\n    \"modules\": listModules,\n    \"release\": release,\n    \"unreleased\": unreleased,\n    \"dev-start\": devStart,\n    \"dev-stop\": devStop,\n    \"mass-change\": massChange,\n    \"--help\": showHelp\n  }[command]\n  if (!cmdFn || cmdFn.length > args.length) help(1)\n  cmdFn.apply(null, args)\n}\n\nfunction showHelp() {\n  help(0)\n}\n\nfunction help(status) {\n  console.log(`Usage:\n  pm install [--ssh]      Clone and symlink the packages, install dependencies, build\n  pm build                Build all modules\n  pm status               Print out the git status of packages\n  pm commit <args>        Run git commit in all packages that have changes\n  pm push                 Run git push in packages that have new commits\n  pm pull                 Run git pull in all packages\n  pm test                 Run the tests from all packages\n  pm watch                Set up a process that rebuilds the packages on change\n  pm grep <pattern>       Grep through the source code for all packages\n  pm run <command>        Run the given command in each of the package dirs\n  pm changes              Show commits since the last release for all packages\n  pm mass-change <files> <pattern> <replacement>\n                          Run a regexp-replace on the matching files in each package\n  pm release <module>     Generate a new release for the given module.\n  pm unreleased           List committed but unreleased changes.\n  pm modules [--core]     Emit a list of all package names\n  pm dev-start            Start development server\n  pm dev-stop             Stop development server, if running\n  pm --help`)\n  process.exit(status)\n}\n\nfunction assertInstalled() {\n  modsAndWebsite.forEach(repo => {\n    if (!fs.existsSync(joinP(repo))) {\n      console.error(\"module `%s` is missing. Did you forget to run `pm install`?\", repo)\n      process.exit(1)\n    }\n  })\n}\n\nfunction run(cmd, args, pkg) {\n  return child.execFileSync(cmd, args, {\n    cwd: pkg === null ? undefined : pkg ? joinP(pkg) : projectDir,\n    encoding: \"utf8\",\n    stdio: [\"ignore\", \"pipe\", process.stderr]\n  })\n}\n\nfunction status() {\n  modsAndWebsite.forEach(repo => {\n    let output = run(\"git\", [\"status\", \"-sb\"], repo)\n    if (output != \"## master...origin/master\\n\" && output != \"## main...origin/main\\n\")\n      console.log(repo + \":\\n\" + run(\"git\", [\"status\"], repo))\n  })\n}\n\nfunction commit(...args) {\n  modsAndWebsite.forEach(repo => {\n    if (run(\"git\", [\"diff\"], repo) || run(\"git\", [\"diff\", \"--cached\"], repo))\n      console.log(repo + \":\\n\" + run(\"git\", [\"commit\"].concat(args), repo))\n  })\n}\n\nfunction install(arg = null) {\n  let base = \"https://github.com/prosemirror/\"\n  if (arg == \"--ssh\") { base = \"git@github.com:ProseMirror/\" }\n  else if (arg != null) help(1)\n\n  modsAndWebsite.forEach(repo => {\n    if (fs.existsSync(joinP(repo))) {\n      console.warn(\"Skipping cloning of \" + repo + \" (directory exists)\")\n      return\n    }\n    let origin = base + (repo == \"website\" ? \"\" : \"prosemirror-\") + repo + \".git\"\n    run(\"git\", [\"clone\", origin, repo])\n  })\n\n  console.log(\"Running npm install\")\n  run(\"npm\", [\"install\"])\n  console.log(\"Building modules\")\n  build()\n}\n\nasync function build() {\n  console.info(\"Building...\")\n  let t0 = Date.now()\n  await require(\"@marijn/buildtool\").build(mods.map(mainFile), buildOptions)\n  console.info(`Done in ${((Date.now() - t0) / 1000).toFixed(2)}s`)\n}\n\nfunction test(...args) {\n  let runTests = require(\"@marijn/testtool\")\n  let {tests, browserTests} = runTests.gatherTests(mods.map(m => joinP(m)))\n  let browsers = [], grep, noBrowser = false\n  for (let i = 0; i < args.length; i++) {\n    if (args[i] == \"--firefox\") browsers.push(\"firefox\")\n    if (args[i] == \"--chrome\") browser.push(\"chrome\")\n    if (args[i] == \"--no-browser\") noBrowser = true\n    if (args[i] == \"--grep\") grep = args[++i]\n  }\n  if (!browsers.length && !noBrowser) browsers.push(\"chrome\")\n  runTests.runTests({tests, browserTests, browsers, grep}).then(failed => process.exit(failed ? 1 : 0))\n}\n\nfunction push() {\n  modsAndWebsite.forEach(repo => {\n    if (/\\bahead\\b/.test(run(\"git\", [\"status\", \"-sb\"], repo)))\n      run(\"git\", [\"push\"], repo)\n  })\n}\n\nfunction pull() {\n  modsAndWebsite.forEach(repo => run(\"git\", [\"pull\"], repo))\n}\n\nfunction grep(pattern) {\n  let files = []\n  const {globSync: glob} = require(\"glob\")\n  mods.forEach(repo => {\n    files = files.concat(glob(joinP(repo, \"src\", \"*.ts\"))).concat(glob(joinP(repo, \"test\", \"*.ts\")))\n  })\n  files = files.concat(glob(joinP(\"website\", \"src\", \"**\", \"*.js\")))\n    .concat(glob(joinP(\"website\", \"pages\", \"examples\", \"*\", \"*.js\")))\n  try {\n    console.log(run(\"grep\", [\"--color\", \"-nH\", \"-e\", pattern].concat(files.map(f => path.relative(process.cwd(), f))), null))\n  } catch(e) {\n    process.exit(1)\n  }\n}\n\nfunction runCmd(cmd, ...args) {\n  mods.forEach(repo => {\n    console.log(repo + \":\")\n    try {\n      console.log(run(cmd, args, repo))\n    } catch (e) {\n      console.log(e.toString())\n      process.exit(1)\n    }\n  })\n}\n\nfunction changes() {\n  mods.forEach(repo => {\n    let lastTag = run(\"git\", [\"describe\", \"HEAD\", \"--tags\", \"--abbrev=0\"], repo).trim()\n    if (!lastTag) return console.log(\"No previous tag for \" + repo + \"\\n\")\n    let history = run(\"git\", [\"log\", lastTag + \"..HEAD\"], repo).trim()\n    if (history) console.log(repo + \":\\n\" + \"=\".repeat(repo.length + 1) + \"\\n\\n\" + history + \"\\n\")\n  })\n}\n\nfunction editReleaseNotes(notes) {\n  let noteFile = join(projectDir, \"notes.txt\")\n  fs.writeFileSync(noteFile, notes.head + notes.body)\n  run(process.env.EDITOR || \"emacs\", [noteFile], null)\n  let edited = fs.readFileSync(noteFile)\n  fs.unlinkSync(noteFile)\n  if (!/\\S/.test(edited)) process.exit(0)\n  let split = /^(.*)\\n+([^]*)/.exec(edited)\n  return {head: split[1] + \"\\n\\n\", body: split[2]}\n}\n\nfunction version(mod) {\n  return require(join(\"..\", mod, \"package.json\")).version\n}\n\nfunction release(mod, ...args) {\n  let currentVersion = version(mod)\n  let noteArg = args.indexOf(\"--notes\")\n  let extra = noteArg > -1 ? args[noteArg + 1] : null\n  let changes = changelog(mod, currentVersion, extra)\n  let newVersion = bumpVersion(currentVersion, changes)\n  console.log(`Creating prosemirror-${mod} ${newVersion}`)\n\n  let notes = releaseNotes(mod, changes, newVersion)\n  if (args.indexOf(\"--edit\") > -1) nodes = editReleaseNotes(notes)\n\n  setModuleVersion(mod, newVersion)\n  if (changes.breaking.length) setDepVersion(mod, newVersion)\n  fs.writeFileSync(joinP(mod, \"CHANGELOG.md\"), notes.head + notes.body + fs.readFileSync(joinP(mod, \"CHANGELOG.md\"), \"utf8\"))\n  run(\"git\", [\"add\", \"package.json\"], mod)\n  run(\"git\", [\"add\", \"CHANGELOG.md\"], mod)\n  run(\"git\", [\"commit\", \"-m\", `Mark version ${newVersion}`], mod)\n  run(\"git\", [\"tag\", newVersion, \"-m\", `Version ${newVersion}\\n\\n${notes.body}`, \"--cleanup=verbatim\"], mod)\n}\n\nfunction unreleased() {\n  mods.forEach(mod => {\n    let ver = version(mod), changes = changelog(mod, ver)\n    if (changes.fix.length || changes.feature.length || changes.breaking.length)\n      console.log(mod + \":\\n\\n\", releaseNotes(mod, changes, \"xxx\").body)\n  })\n}\n\nfunction changelog(repo, since, extra) {\n  let commits = run(\"git\", [\"log\", \"--format=%B\", \"--reverse\", since + \"..HEAD\"], repo)\n  if (extra) commits += \"\\n\\n\" + extra\n  let result = {fix: [], feature: [], breaking: []}\n  let re = /\\n\\r?\\n(BREAKING|FIX|FEATURE):\\s*([^]*?)(?=\\r?\\n\\r?\\n|\\r?\\n?$)/g, match\n  while (match = re.exec(commits)) result[match[1].toLowerCase()].push(match[2].replace(/\\r?\\n/g, \" \"))\n  return result\n}\n\nfunction bumpVersion(version, changes) {\n  let [major, minor, patch] = version.split(\".\")\n  if (changes.breaking.length) return `${Number(major) + 1}.0.0`\n  if (changes.feature.length) return `${major}.${Number(minor) + 1}.0`\n  if (changes.fix.length) return `${major}.${minor}.${Number(patch) + 1}`\n  throw new Error(\"No new release notes!\")\n}\n\nfunction releaseNotes(mod, changes, version) {\n  let pad = n => n < 10 ? \"0\" + n : n\n  let d = new Date, date = d.getFullYear() + \"-\" + pad(d.getMonth() + 1) + \"-\" + pad(d.getDate())\n\n  let types = {breaking: \"Breaking changes\", fix: \"Bug fixes\", feature: \"New features\"}\n\n  let refTarget = \"https://prosemirror.net/docs/ref/\"\n  let head = `## ${version} (${date})\\n\\n`, body = \"\"\n  for (let type in types) {\n    let messages = changes[type]\n    if (messages.length) body += `### ${types[type]}\\n\\n`\n    messages.forEach(message => body += message.replace(/\\]\\(##/g, \"](\" + refTarget + \"#\") + \"\\n\\n\")\n  }\n  return {head, body}\n}\n\nfunction setModuleVersion(mod, version) {\n  let file = joinP(mod, \"package.json\")\n  fs.writeFileSync(file, fs.readFileSync(file, \"utf8\").replace(/\"version\":\\s*\".*?\"/, `\"version\": \"${version}\"`))\n}\n\nfunction setDepVersion(mod, version) {\n  modsAndWebsite.forEach(repo => {\n    if (repo == mod) return\n    let file = joinP(repo, \"package.json\"), text = fs.readFileSync(file, \"utf8\")\n    let result = text.replace(/\"prosemirror-(.*?)\":\\s*\".*?\"/g, (match, dep) => {\n      return dep == mod ? `\"prosemirror-${mod}\": \"^${version}\"` : match\n    })\n    if (result != text) {\n      fs.writeFileSync(file, result)\n      run(\"git\", [\"add\", \"package.json\"], repo)\n      run(\"git\", [\"commit\", \"-m\", `Upgrade prosemirror-${mod} dependency`], repo)\n    }\n  })\n}\n\nfunction listModules() {\n  console.log((process.argv.includes(\"--core\") ? main : mods).join(\"\\n\"))\n}\n\nconst buildOptions = {}\n\nfunction watch() {\n  require(\"@marijn/buildtool\").watch(mods.map(mainFile), [join(__dirname, \"..\", \"demo\", \"demo.ts\")], buildOptions)\n}\n\nconst pidFile = join(__dirname, \".pm-dev.pid\")\nfunction devPID() {\n  try { return JSON.parse(fs.readFileSync(pidFile, \"utf8\")) }\n  catch(_) { return null }\n}\n\nfunction startServer() {\n  let serve = path.resolve(join(__dirname, \"..\", \"demo\"))\n  let port = +(process.env.PORT || 8080)\n  let moduleserver = new (require(\"esmoduleserve/moduleserver\"))({root: serve, maxDepth: 2})\n  let serveStatic = require(\"serve-static\")(serve)\n  require(\"http\").createServer((req, resp) => {\n    if (/^\\/test\\/?($|\\?)/.test(req.url)) {\n      let runTests = require(\"@marijn/testtool\")\n      let {browserTests} = runTests.gatherTests(mods.map(m => joinP(m)))\n      resp.writeHead(200, {\"content-type\": \"text/html\"})\n      resp.end(runTests.testHTML(browserTests.map(f => path.relative(serve, f)), false))\n    } else {\n      moduleserver.handleRequest(req, resp) || serveStatic(req, resp, _err => {\n        resp.statusCode = 404\n        resp.end('Not found')\n      })\n    }\n  }).listen(port, process.env.OPEN ? undefined : \"127.0.0.1\")\n  console.log(`Dev server listening on ${port}`)\n}\n\n\nfunction devStart() {\n  let pid = devPID()\n  if (pid != null) {\n    try { run(\"ps\", [\"-p\", String(pid)]) }\n    catch (_) { pid = null }\n  }\n  if (pid != null) {\n    console.log(\"Dev server already running at pid \" + pid)\n    return\n  }\n\n  fs.writeFileSync(pidFile, process.pid + \"\\n\")\n  function del() { fs.unlink(pidFile, () => {}); console.log(\"Stop\") }\n  function delAndExit() { del(); process.exit() }\n  process.on(\"exit\", del)\n  process.on(\"SIGINT\", delAndExit)\n  process.on(\"SIGTERM\", delAndExit)\n\n  startServer()\n  watch()\n}\n\nfunction devStop() {\n  let pid = devPID()\n  if (pid == null) {\n    console.log(\"Dev server not running\")\n  } else {\n    process.kill(pid, \"SIGTERM\")\n    console.log(\"Killed dev server with pid \" + pid)\n  }\n}\n\nfunction massChange(file, pattern, replacement = \"\") {\n  let re = new RegExp(pattern, \"g\")\n  modsAndWebsite.forEach(repo => {\n    let {globSync: glob} = require(\"glob\")\n    glob(joinP(repo, file)).forEach(file => {\n      let content = fs.readFileSync(file, \"utf8\"), changed = content.replace(re, replacement)\n      if (changed != content) {\n        console.log(\"Updated \" + file)\n        fs.writeFileSync(file, changed)\n      }\n    })\n  })\n}\n\nstart()\n"
  },
  {
    "path": "demo/bench/example.js",
    "content": "const {schema, doc, p, ol, ul, li, h1, h2, blockquote, em, code, a} = require(\"prosemirror-model/test/build\")\n\nexports.schema = schema\n\nlet example = doc(\n  h1(\"Collaborative Editing in ProseMirror\"),\n  p(\"This post describes the algorithm used to make collaborative editing work in \", a(\"ProseMirror\"), \". For an introduction to ProseMirror, see \", a(\"another post\"), \" here.\"),\n  h2(\"The Problem\"),\n  p(\"A real-time collaborative editing system is one where multiple people may work on a document at the same time. The system ensures that the documents stay synchronized—changes made by individual users are sent to other users, and show up in their representation of the document.\"),\n  p(\"Since transmitting changes over any kind of network is going to take time, the complexity of such systems lies in the way they handle concurrent updates. One solution is to allow users to lock the document (or parts of it) and thus prevent concurrent changes from happening at all. But this forces users to think about locks, and to wait when the lock they need is not available. We'd prefer not to do that.\"),\n  p(\"If we allow concurrent updates, we get situations where user A and user B both did something, unaware of the other user's actions, and now those things they did have to be reconciled. The actions might not interact at all—when they are editing different parts of the document—or interact very much—when they are trying to change the same word.\"),\n  h2(\"Operational Transformation\"),\n  p(\"A lot of research has gone into this problem. And I must admit that, though I did read a bunch of papers, I definitely do not have a deep knowledge of this research, and if you find that I misrepresent something or am missing an interesting reference, I am very interested in an \", a(\"email\"), \" that tells me about it.\"),\n  p(\"A lot of this research is about truly distributed systems, where a group of nodes exchange messages among themselves, without a central point of control. The classical approach to the problem, which is called \", a(\"Operational Transformation\"), \", is such a distributed algorithm. It defines a way to describe changes that has two properties:\"),\n  ol(li(p(\"You can transform changes relative to other changes. So if user A inserted an “O” at offset 1, and user B concurrently inserted a “T” at offset 10, user A can transform B's change relative to its own change, an insert the “T” at offset 11, because an extra character was added in front of the change's offset.\")),\n     li(p(\"No matter in which order concurrent changes are applied, you end up with the same document. This allows A to transform B's change relative to its own change, and B to transform A's change similarly, without the two users ending up with different documents.\"))),\n  p(\"An Operational Transformation (OT) based system applies local changes to the local document immediately, and broadcasts them to other users. Those users will transform and apply them when they get them. In order to know exactly which local changes a remote change should be transformed through, such a system also has to send along some representation of the state of the document at the time the change was made.\"),\n  p(\"That sounds relatively simple. But it is a nightmare to implement. Once you support more than a few trivial types of changes (things like “insert” and “delete”), ensuring that applying changes in any order produces the same document becomes very hard.\"),\n  p(\"Joseph Gentle, one of the engineers who worked on Google Wave, \", a(\"stated\"), \"...\"), blockquote(p(\"Unfortunately, implementing OT sucks. There's a million algorithms with different trade-offs, mostly trapped in academic papers. The algorithms are really hard and time consuming to implement correctly.\")),\n  h2(\"Centralization\"),\n  p(\"The design decisions that make the OT mechanism complex largely stem from the need to have it be truly distributed. Distributed systems have nice properties, both practically and politically, and they tend to be interesting to work on.\"),\n  p(\"But you can save oh so much complexity by introducing a central point. I am, to be honest, extremely bewildered by Google's decision to use OT for their Google Docs—a centralized system.\"),\n  p(\"ProseMirror's algorithm is centralized, in that it has a single node (that all users are connected to) making decisions about the order in which changes are applied. This makes it relatively easy to implement and to reason about.\"),\n  p(\"And I don't actually believe that this property represents a huge barrier to actually running the algorithm in a distributed way. Instead of a central server calling the shots, you could use a consensus algorithm like \", a(\"Raft\"), \" to pick an arbiter. (But note that I have not actually tried this.)\"),\n  h2(\"The Algorithm\"),\n  p(\"Like OT, ProseMirror uses a change-based vocabulary and transforms changes relative to each other. Unlike OT, it does not try to guarantee that applying changes in a different order will produce the same document.\"),\n  p(\"By using a central server, it is possible—easy even—to have clients all apply changes in the same order. You can use a mechanism much like the one used in code versioning systems. When a client has made a change, they try to \", em(\"push\"), \" that change to the server. If the change was based on the version of the document that the server considers current, it goes through. If not, the client must \", em(\"pull\"), \" the changes that have been made by others in the meantime, and \", em(\"rebase\"), \" their own changes on top of them, before retrying the push.\"),\n  p(\"Unlike in git, the history of the document is linear in this model, and a given version of the document can simply be denoted by an integer.\"),\n  p(\"Also unlike git, all clients are constantly pulling (or, in a push model, listening for) new changes to the document, and track the server's state as quickly as the network allows.\"),\n  p(\"The only hard part is rebasing changes on top of others. This is very similar to the transforming that OT does. But it is done with the client's \", em(\"own\"), \" changes, not remote changes.\"),\n  p(\"Because applying changes in a different order might create a different document, rebasing isn't quite as easy as transforming all of our own changes through all of the remotely made changes.\"),\n  h2(\"Position Mapping\"),\n  p(\"Whereas OT transforms changes relative to \", em(\"other changes\"), \", ProseMirror transforms them using a derived data structure called a \", em(\"position map\"), \". Whenever you apply a change to a document, you get a new document and such a map, which you can use to convert positions in the old document to corresponding positions in the new document. The most obvious use case of such a map is adjusting the cursor position so that it stays in the same conceptual place—if a character was inserted before it, it should move forward along with the surrounding text.\"),\n  p(\"Transforming changes is done entirely in terms of mapping positions. This is nice—it means that we don't have to write change-type-specific transformation code. Each change has one to three positions associated with it, labeled \", code(\"from\"), \", \", code(\"to\"), \", and \", code(\"at\"), \". When transforming the change relative to a given other change, those positions get mapped through the other change's position map.\"),\n  p(\"For example, if a character is inserted at position 5, the change “delete from 10 to 14” would become “delete from 11 to 15” when transformed relative to that insertion.\"),\n  p(\"Every change's positions are meaningful only in the exact document version that it was originally applied to. A position map defines a mapping between positions in the two document versions before and after a change. To be able to apply a change to a different version, it has to be mapped, step by step, through the changes that lie between its own version and the target version.\"),\n  p(\"(For simplicity, examples will use integers for positions. Actual positions in ProseMirror consist of an integer offset in a paragraph plus the path of that paragraph in the document tree.)\"),\n  h2(\"Rebasing Positions\"),\n  p(\"An interesting case comes up when a client has multiple unpushed changes buffered. If changes from a peer come in, all of the locally buffered changes have to be moved on top of those changes. Say we have local changes \", em(\"L1\"), \" and \", em(\"L2\"), \", and are rebasing them onto remote changes \", em(\"R1\"), \" and \", em(\"R2\"), \", where \", em(\"L1\"), \" and \", em(\"R1\"), \" start from the same version of the document.\"),\n  p(\"First, we apply R1 and R2 to our representation of that original version (clients must track both the document version they are currently displaying, which includes unsent changes, and the version that does not yet include those changes). This creates two position maps \", em(\"mR1\"), \" and \", em(\"mR2\"), \".\"),\n  p(\"We can simply map \", em(\"L1\"), \" forward through those maps to arrive at \", em(\"L1⋆\"), \", the remapped version of \", em(\"L1\"), \". But \", em(\"L2\"), \" was based on the document that existed after applying \", em(\"L1\"), \", so we first have to map it \", em(\"backwards\"), \" through \", em(\"mL1\"), \", the original map created by applying \", em(\"L1\"), \". Now it refers to the same version that \", em(\"R1\"), \" starts in, so we can map it forward through \", em(\"mR1\"), \" and \", em(\"mR2\"), \", and then finally though \", em(\"mL1⋆\"), \", the map created by applying \", em(\"L1⋆\"), \". Now we have \", em(\"L2⋆\"), \", and can apply it to the output of applying \", em(\"L1⋆\"), \", and \", em(\"voila\"), \", we have rebased two changes onto two other changes.\"),\n  p(\"Except that mapping through deletions or backwards through insertions loses information. If you insert two characters at position 5, and then another one at position 6 (between the two previously inserted characters), mapping backwards and then forward again through the first insertion will leave you before or after the characters, because the position between them could not be expressed in the coordinate space of a document that did not yet have these characters.\"),\n  p(\"To fix this, the system uses mapping pipelines that are not just a series of maps, but also keep information about which of those maps are mirror images of each other. When a position going through such a pipeline encounters a map that deletes the content around the position, the system scans ahead in the pipeline looking for a mirror images of that map. If such a map is found, we skip forward to it, and restore the position in the content that is inserted by this map, using the relative offset that the position had in the deleted content. A mirror image of a map that deletes content must insert content with the same shape.\"),\n  h2(\"Mapping Bias\"),\n  p(\"Whenever content gets inserted, a position at the exact insertion point can be meaningfully mapped to two different positions: before the inserted content, or after it. Sometimes the first is appropriate, sometimes the second. The system allows code that maps a position to choose what bias it prefers.\"),\n  p(\"This is also why the positions associated with a change are labeled. If a change with \", code(\"from\"), \" and \", code(\"to\"), \" positions, such as deleting or styling a piece of the document, has content inserted directly before or after it, that content should not be included in the change. So \", code(\"from\"), \" positions get mapped with a forward bias, and \", code(\"to\"), \" positions with a backward bias.\"),\n  p(\"When a change is mapped through a map that completely contains it, for example when inserting a character at position 5 is mapped through the map created by deleting from position 2 to 10, the whole change is simply dropped, since the context in which it was made no longer exists.\"),\n  h2(\"Types of Changes\"),\n  p(\"An atomic change in ProseMirror is called a \", em(\"step\"), \". Some things that look like single changes from a user interface perspective are actually decomposed into several steps. For example, if you select text and press enter, the editor will generate a \", em(\"delete\"), \" step that removes the selected text, and then a \", em(\"split\"), \" step that splits the current paragraph.\"),\n  p(\"These are the step types that exist in ProseMirror:\"),\n  ul(li(p(\"\", code(\"addStyle\"), \" and \", code(\"removeStyle\"), \" add and remove inline styling to or from a piece of the document. They take \", code(\"from\"), \" and \", code(\"to\"), \" positions.\")),\n     li(p(\"\", code(\"split\"), \" splits a node in two. It can be used, for example, to split a paragraph when the user presses enter. It takes a single \", code(\"at\"), \" position.\")),\n     li(p(\"\", code(\"join\"), \" joins two adjacent nodes. This only works if they contain the same type of content. It takes \", code(\"from\"), \" and \", code(\"to\"), \" positions that should refer to the end and start of the nodes to be joined. (This is to make sure that the nodes that were actually intended are being joined. The step is ignored when another node has been inserted between them in the meantime.)\")),\n     li(p(\"\", code(\"ancestor\"), \" is used to change the type of a node and to add or remove nodes above it. It can be used to wrap something in a list, or to convert from a paragraph to a heading. It takes \", code(\"from\"), \" and \", code(\"to\"), \" positions pointing at the start and end of the node.\")),\n     li(p(\"\", code(\"replace\"), \" replaces a piece of the document with zero or more replacement nodes, and optionally stitches up compatible nodes at the edges of the cut. Its \", code(\"from\"), \" and \", code(\"to\"), \" positions define the range that should be deleted, and its \", code(\"at\"), \" position gives the place where the new nodes should be inserted.\"))),\n  p(\"The last type is more complex than the other ones, and my initial impulse was to split it up into steps that remove and insert content. But because the position map created by a replace step needs to treat the step as atomic (positions have to be pushed out of \", em(\"all\"), \" replaced content), I got better results with making it a single step.\"),\n  h2(\"Intention\"),\n  p(\"An essential property of real-time collaborative systems is that they try to preserve the \", em(\"intention\"), \" of a change. Because “merging” of changes happens automatically, without user interaction, it would get very annoying when the changes you make are, through rebasing, reinterpreted in a way that does not match what you were trying to do.\"),\n  p(\"I've tried to define the steps and the way in which they are rebased in so that rebasing yields unsurprising behavior. Most of the time, changes don't overlap, and thus don't really interact. But when they overlap, we must make sure that their combined effect remains sane.\"),\n  p(\"Sometimes a change must simply be dropped. When you type into a paragraph, but another user deleted that paragraph before your change goes through, the context in which your input made sense is gone, and inserting it in the place where the paragraph used to be would create a meaningless fragment.\"),\n  p(\"If you tried to join two lists together, but somebody has added a paragraph between them, your change becomes impossible to execute (you can't join nodes that aren't adjacent), so it is dropped.\"),\n  p(\"In other cases, a change is modified but stays meaningful. If you made characters 5 to 10 strong, and another user inserted a character at position 7, you end up making characters 5 to 11 strong.\"),\n  p(\"And finally, some changes can overlap without interacting. If you make a word a link and another user makes it emphasized, both of your changes to that same word can happen in their original form.\"),\n  h2(\"Offline Work\"),\n  p(\"Silently reinterpreting or dropping changes is fine for real-time collaboration, where the feedback is more or less immediate—you see the paragraph that you were editing vanish, and thus know that someone deleted it, and your changes are gone.\"),\n  p(\"For doing offline work (where you keep editing when not connected) or for a branching type of work flow, where you do a bunch of work and \", em(\"then\"), \" merge it with whatever other people have done in the meantime, the model I described here is useless (as is OT). It might silently throw away a lot of work (if its context was deleted), or create a strange mishmash of text when two people edited the same sentence in different ways.\"),\n  p(\"In cases like this, I think a diff-based approach is more appropriate. You probably can't do automatic merging—you have to identify conflicts had present them to the user to resolve. I.e. you'd do what git does.\"),\n  h2(\"Undo History\"),\n  p(\"How should the undo history work in a collaborative system? The widely accepted answer to that question is that it definitely should \", em(\"not\"), \" use a single, shared history. If you undo, the last edit that \", em(\"you\"), \" made should be undone, not the last edit in the document.\"),\n  p(\"This means that the easy way to implement history, which is to simply roll back to a previous state, does not work. The state that is created by undoing your change, if other people's changes have come in after it, is a new one, not seen before.\"),\n  p(\"To be able to implement this, I had to define changes (steps) in such a way that they can be inverted, producing a new step that represents the change that cancels out the original step.\"),\n  p(\"ProseMirror's undo history accumulates inverted steps, and also keeps track of all position maps between them and the current document version. These are needed to be able to map the inverted steps to the current document version.\"),\n  p(\"A downside of this is that if a user has made a change but is now idle while other people are editing the document, the position maps needed to move this user's change to the current document version pile up without bound. To address this, the history periodically \", em(\"compacts\"), \" itself, mapping the inverted changes forward so that they start at the current document again. It can then discard the intermediate position maps.\")\n)\nexports.example = example\n"
  },
  {
    "path": "demo/bench/index.html",
    "content": "<!doctype html>\n<meta charset=utf8>\n<title>ProseMirror benchmarks</title>\n\n<div id=\"buttons\"></div>\n\n<p><label><input type=checkbox id=profile> Profile</label></p>\n\n<div id=\"workspace\" style=\"height: 0; overflow: hidden\"></div>\n\n<script src=\"/moduleserve/load.js\" data-module=\"./index\" data-require></script>\n"
  },
  {
    "path": "demo/bench/index.js",
    "content": "const {Fragment} = require(\"prosemirror-model\")\nconst {doc, blockquote, p} = require(\"prosemirror-model/test/build\")\nconst {EditorState} = require(\"prosemirror-state\")\nconst {EditorView} = require(\"prosemirror-view\")\nconst {history} = require(\"prosemirror-history\")\n\nconst {example} = require(\"./example\")\nconst {typeDoc} = require(\"./type\")\nconst {mutateDoc} = require(\"./mutate\")\n\nfunction button(name, run) {\n  var dom = document.createElement(\"button\")\n  dom.textContent = name\n  dom.addEventListener(\"click\", run)\n  return dom\n}\n\nfunction group(name, ...buttons) {\n  var wrap = document.querySelector(\"#buttons\").appendChild(document.createElement(\"p\"))\n  wrap.textContent = name\n  wrap.append(document.createElement(\"br\"))\n  buttons.forEach(b => wrap.append(\" \", b))\n}\n\nfunction run(bench, options) {\n  var t0 = Date.now(), steps = 0\n  var startState = (options.state || options.view) && EditorState.create({doc: options.doc, plugins: options.plugins})\n  var view = options.view && new EditorView(document.querySelector(\"#workspace\"), {state: startState})\n  var state, callback = tr => {\n    ++steps\n    if (state) {\n      state = state.applyAction({type: \"transform\", time: Date.now(), transform: tr})\n      if (view) view.updateState(state)\n    }\n  }\n  var profile = document.querySelector(\"#profile\").checked\n  if (profile) console.profile(options.name)\n  for (var i = 0, e = options.repeat || 1; i < e; i++) {\n    state = startState\n    bench(options, callback)\n  }\n  if (profile) console.profileEnd(options.name)\n  console.log(\"'\" + options.name + \"' took \" + (Date.now() - t0) + \"ms for \" + steps + \" steps\")\n}\n\ngroup(\"Type out a document\", button(\"Plain\", () => {\n  run(typeDoc, {doc: example, name: \"Type plain\", profile: true, repeat: 6})\n}), button(\"State\", () => {\n  run(typeDoc, {doc: example, name: \"Type with state\", profile: true, repeat: 6, state: true})\n}), button(\"State + History\", () => {\n  run(typeDoc, {doc: example, name: \"Type with state + history\", profile: true, repeat: 6, state: true, plugins: [history()]})\n}), button(\"View\", () => {\n  run(typeDoc, {doc: example, name: \"Type with view\", profile: true, repeat: 6, state: true, view: true})\n}))\n\ngroup(\"Mutate inside a document\", button(\"small + shallow\", () => {\n  run(mutateDoc, {doc: doc(p(\"a\"), p(\"b\"), p(\"c\")),\n                  pos: 4, n: 100000, name: \"Mutate small + shallow\"})\n}), button(\"small + deep\", () => {\n  run(mutateDoc, {doc: doc(p(\"a\"), blockquote(blockquote(blockquote(blockquote(blockquote(blockquote(p(\"b\"))))))), p(\"c\")),\n                  pos: 10, n: 100000, name: \"Mutate small + deep\"})\n}), button(\"large + shallow\", () => {\n  var d = doc(p(\"a\")), many = []\n  for (var i = 0; i < 1000; i++) many.push(d.firstChild)\n  run(mutateDoc, {doc: d.copy(Fragment.from(many)),\n                  pos: 4, n: 100000, name: \"Mutate large + shallow\"})\n}))\n"
  },
  {
    "path": "demo/bench/mutate.js",
    "content": "const {Slice, Fragment} = require(\"prosemirror-model\")\nconst {Transform} = require(\"prosemirror-transform\")\n\nfunction mutateDoc(options, callback) {\n  var doc = options.doc, pos = options.pos, slice = new Slice(Fragment.from(doc.type.schema.text(\"X\")), 0, 0)\n  for (var i = 0; i < options.n; i++) {\n    var add = new Transform(doc).replace(pos, pos, slice)\n    callback(add)\n    var rem = new Transform(add.doc).delete(pos, pos + 1)\n    callback(rem)\n    doc = rem.doc\n  }\n}\nexports.mutateDoc = mutateDoc\n"
  },
  {
    "path": "demo/bench/type.js",
    "content": "const {Transform} = require(\"prosemirror-transform\")\n\nfunction typeDoc(options, callback) {\n  var example = options.doc, schema = example.type.schema\n  var doc = schema.nodes.doc.createAndFill(), pos = 0\n\n  function scan(node, depth) {\n    if (node.isText) {\n      for (var i = 0; i < node.text.length; i++) {\n        var tr = new Transform(doc).replaceRangeWith(pos, pos, schema.text(node.text.charAt(i), node.marks))\n        callback(tr)\n        doc = tr.doc\n        pos++\n      }\n    } else if (pos < doc.content.size - depth) {\n      pos++\n      scanContent(node, depth + 1)\n      pos++\n    } else {\n      if (node.isLeaf) {\n        var tr = new Transform(doc).replaceRangeWith(pos, pos, node)\n        callback(tr)\n        doc = tr.doc\n        pos += node.nodeSize\n      } else {\n        var tr = new Transform(doc).replaceRangeWith(pos, pos, node.type.createAndFill())\n        callback(tr)\n        doc = tr.doc\n        pos++\n        scanContent(node, depth + 1)\n        pos++\n      }\n    }\n  }\n  function scanContent(node, depth) {\n    node.forEach(child => scan(child, depth))\n  }\n  scanContent(example, 0)\n}\nexports.typeDoc = typeDoc\n"
  },
  {
    "path": "demo/demo.css",
    "content": "body {\n  font-family: Georgia;\n  margin: 0 1em 2em;\n}\n\ntextarea {\n  width: 100%;\n  border: 1px solid silver;\n  min-height: 40em;\n  padding: 4px 8px;\n}\n\n.left, .right {\n  width: 50%;\n  float: left;\n}\n\n.full {\n  max-width: 50em;\n}\n\n.marked {\n  background: #ff6\n}\n\n.ProseMirror-menubar-wrapper {\n  border: 1px solid silver;\n}\n\n.ProseMirror {\n  padding: 4px 8px 4px 14px;\n  line-height: 1.2;\n}\n"
  },
  {
    "path": "demo/demo.ts",
    "content": "import {Schema, DOMParser} from \"prosemirror-model\"\nimport {EditorView} from \"prosemirror-view\"\nimport {EditorState} from \"prosemirror-state\"\nimport {schema} from \"prosemirror-schema-basic\"\nimport {addListNodes} from \"prosemirror-schema-list\"\nimport {exampleSetup} from \"prosemirror-example-setup\"\n\nconst demoSchema = new Schema({\n  nodes: addListNodes(schema.spec.nodes as any, \"paragraph block*\", \"block\"),\n  marks: schema.spec.marks\n})\n\nlet state = EditorState.create({doc: DOMParser.fromSchema(demoSchema).parse(document.querySelector(\"#content\")!),\n                                plugins: exampleSetup({schema: demoSchema})})\n\n;(window as any).view = new EditorView(document.querySelector(\".full\"), {state})\n"
  },
  {
    "path": "demo/index.html",
    "content": "<!doctype html>\n\n<meta charset=\"utf-8\"/>\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>ProseMirror demo page</title>\n<link rel=stylesheet href=\"demo.css\">\n<link rel=stylesheet href=\"parent/view/style/prosemirror.css\">\n<link rel=stylesheet href=\"parent/menu/style/menu.css\">\n<link rel=stylesheet href=\"parent/example-setup/style/style.css\">\n<link rel=stylesheet href=\"parent/gapcursor/style/gapcursor.css\">\n\n<h1>ProseMirror demo page</h1>\n\n<div class=\"full\"></div>\n\n<div id=content style=\"display: none\">\n<h2>Demonstration Text</h2>\n\n<p>A ProseMirror document is based on a schema, which determines the\nkind of elements that may occur in it, and the relation they have to\neach other. This one is based on the basic schema, with lists and\ntables added. It allows the usual <strong>strong</strong>\nand <em>emphasized</em> text, <code>code font</code>,\nand <a href=\"http://marijnhaverbeke.nl\">links</a>. There are also\nimages: <img alt=\"demo picture\" src=\"img.png\">.</p>\n\n<p>On the block level you can have:</p>\n\n<ol>\n  <li>Ordered lists (such as this one)</li>\n  <li>Bullet lists</li>\n  <li>Blockquotes</li>\n  <li>Code blocks</li>\n  <li>Tables</li>\n  <li>Horizontal rules</li>\n</ol>\n\n<p>It isn't hard to define your own custom elements, and include them\nin your schema. These can be opaque 'leaf' nodes, that the user\nmanipulates through extra interfaces you provide, or nodes with\nregular editable child nodes.</p>\n\n<hr>\n\n<h2>The Model</h2>\n\n<p>Nodes can nest arbitrarily deep. Thus, the document forms a tree,\nnot dissimilar to the browser's DOM tree.</p>\n\n<p>At the inline level, the model works differently. Each block of\ntext is a single node containing a flat series of inline elements.\nThese are serialized as a tree structure when outputting HTML.</p>\n\n<p>Positions in the document are represented as a path (an array of\noffsets) through the block tree, and then an offset into the inline\ncontent of the block. Blocks that have no inline content (such as\nhorizontal rules and HTML blocks) can not have the cursor inside of\nthem. User-exposed operations on the document preserve the invariant\nthat there is always at least a single valid cursor position.</p>\n\n<hr>\n\n<h2>Examples</h2>\n\n<blockquote><blockquote><p>We did not see a nested blockquote\nyet.</p></blockquote></blockquote>\n\n<pre><code class=\"lang-markdown\">Nor did we see a code block\n\nNote that the content of a code block can't be styled.</code></pre>\n\n<p>This paragraph has<br>a hard break inside of it.</p>\n</div>\n\n<script type=module src=\"_m/demo.js\"></script>\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"prosemirror\",\n  \"version\": \"0.0.0\",\n  \"description\": \"Structured WYSIWYM editor\",\n  \"license\": \"MIT\",\n  \"maintainers\": [\n    {\n      \"name\": \"Marijn Haverbeke\",\n      \"email\": \"marijn@haverbeke.berlin\",\n      \"web\": \"http://marijnhaverbeke.nl\"\n    }\n  ],\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git://github.com/prosemirror/prosemirror.git\"\n  },\n  \"dependencies\": {\n    \"glob\": \"^10.3.0\",\n    \"esmoduleserve\": \"^0.2.0\",\n    \"serve-static\": \"^1.14.1\",\n    \"@marijn/buildtool\": \"^1.0.0\",\n    \"@marijn/testtool\": \"^0.1.0\"\n  },\n  \"scripts\": {\n    \"test\": \"bin/pm test\"\n  },\n  \"workspaces\": [\n    \"*\"\n  ],\n  \"private\": true\n}\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"lib\": [\"es2015\", \"dom\", \"scripthost\"],\n    \"types\": [\"mocha\"],\n    \"stripInternal\": true,\n    \"typeRoots\": [\"./node_modules/@types\"],\n    \"noUnusedLocals\": true,\n    \"strict\": true,\n    \"target\": \"es6\",\n    \"module\": \"es2020\",\n    \"newLine\": \"lf\",\n    \"moduleResolution\": \"node\",\n    \"noEmit\": true,\n    \"paths\": {\n      \"prosemirror-model\": [\"./model/src/index.ts\"],\n      \"prosemirror-schema-basic\": [\"./schema-basic/src/schema-basic.ts\"],\n      \"prosemirror-schema-list\": [\"./schema-list/src/schema-list.ts\"],\n      \"prosemirror-test-builder\": [\"./test-builder/src/index.ts\"],\n      \"prosemirror-transform\": [\"./transform/src/index.ts\"],\n      \"prosemirror-view\": [\"./view/src/index.ts\"],\n      \"prosemirror-state\": [\"./state/src/index.ts\"],\n      \"prosemirror-commands\": [\"./commands/src/commands.ts\"],\n      \"prosemirror-history\": [\"./history/src/history.ts\"],\n      \"prosemirror-dropcursor\": [\"./dropcursor/src/dropcursor.ts\"],\n      \"prosemirror-inputrules\": [\"./inputrules/src/index.ts\"],\n      \"prosemirror-keymap\": [\"./keymap/src/keymap.ts\"],\n      \"prosemirror-search\": [\"./search/src/search.ts\"],\n      \"prosemirror-changeset\": [\"./changeset/src/changeset.ts\"],\n      \"prosemirror-markdown\": [\"./markdown/src/markdown.ts\"],\n      \"prosemirror-collab\": [\"./collab/src/collab.ts\"],\n      \"prosemirror-menu\": [\"./menu/src/index.ts\"],\n      \"prosemirror-gapcursor\": [\"./gapcursor/src/index.ts\"],\n      \"prosemirror-example-setup\": [\"./example-setup/src/index.ts\"]\n    }\n  },\n  \"include\": [\"*/src/*.ts\", \"*/test/*.ts\", \"demo/demo.ts\"]\n}\n"
  }
]