{"id":13431927,"url":"https://github.com/trainyard/choo-codemirror","last_synced_at":"2025-06-22T14:20:30.978Z","repository":{"id":86384448,"uuid":"64702755","full_name":"trainyard/choo-codemirror","owner":"trainyard","description":"An example showing codemirror and choo","archived":false,"fork":false,"pushed_at":"2017-05-27T20:12:32.000Z","size":86,"stargazers_count":13,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-16T22:36:36.106Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://eight-skirt.surge.sh/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/trainyard.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"license","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2016-08-01T21:27:02.000Z","updated_at":"2019-07-08T21:59:12.000Z","dependencies_parsed_at":null,"dependency_job_id":"70a7cd4e-3876-4700-81a8-dd6d64604c3b","html_url":"https://github.com/trainyard/choo-codemirror","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/trainyard/choo-codemirror","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trainyard%2Fchoo-codemirror","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trainyard%2Fchoo-codemirror/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trainyard%2Fchoo-codemirror/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trainyard%2Fchoo-codemirror/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/trainyard","download_url":"https://codeload.github.com/trainyard/choo-codemirror/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trainyard%2Fchoo-codemirror/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261304271,"owners_count":23138307,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T02:01:07.089Z","updated_at":"2025-06-22T14:20:25.969Z","avatar_url":"https://github.com/trainyard.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# choo-codemirror\n\nHow to wrap codemirror with choo\n\n## The Portal\n\nBecause codemirror has its own state management system, we need to be careful not to clobber ours.\nWhat we can do is create a portal, this is basically a concept that puts in place strict `message passing`\n\n### lib/codemirror-portal\n\n```javascript\nconst CodeMirror = require('codemirror')\nconst defer = fn =\u003e setTimeout(fn, 0)\nconst subscribers = []\nconst broadcast = type =\u003e (...payload) =\u003e\n    subscribers.forEach(sub =\u003e {\n      defer(sub(type, {\n        evt: payload,\n        doc: payload[0] \u0026\u0026 payload[0].doc || null,\n        change: payload[1] || null\n      }))\n    })\n\nexports.subscribe = listener =\u003e subscribers.push(listener)\nexports.create = ({namespace}, options) =\u003e {\n  const node = document.createElement('div')\n\n  defer(() =\u003e {\n    const editor = CodeMirror(node, options)\n    editor.on('change', broadcast('change'))\n    editor.on('focus', broadcast('focus'))\n    editor.on('blur', broadcast('blur'))\n    editor.on('scroll', broadcast('scroll'))\n  })\n  return node\n}\n```\n\nHere we can use `create` to return a new node that asynchronously turns into a codemirror instance,\nand `subscribe` which allows us to use a choo `model` to listen to state changes.\n\n## Model\n\n```javascript\n\nconst { subscribe } = require('../lib/codemirror-portal')\n\nmodule.exports = {\n  /* namespace the model so that it cannot access any properties and handlers in other models */\n  namespace: 'codemirror',\n  state: {\n    value: '',\n    isFocused: true,\n    scroll: 0\n  },\n  reducers: {\n    update: (action, state) =\u003e ({ value: action.value }),\n    focusChange: (action, state) =\u003e ({ isFocused: action.focused }),\n    scrollChange: (action, state) =\u003e ({ scroll: action.scroll })\n  },\n  effects: {\n    change: (data, state, send, done) =\u003e {\n      if (data.change \u0026\u0026 data.change.origin !== 'setValue') {\n        send('codemirror:update', { value: data.doc.getValue() }, done)\n      }\n    },\n    focus: (data, state, send, done) =\u003e {\n      send('codemirror:focusChange', { focused: true }, done)\n    },\n    blur: (data, state, send, done) =\u003e {\n      send('codemirror:focusChange', { focused: false }, done)\n    },\n    scroll: (data, state, send, done) =\u003e {\n      send('codemirror:scrollChange', { scroll: data.doc.cm.getScrollInfo() }, done)\n    }\n  },\n  subscriptions: [\n    (send, done) =\u003e\n      subscribe((type, payload) =\u003e {\n        send(`codemirror:${type}`, payload, done)\n      })\n  ]\n}\n```\n\nAs you can see we are using `subscriptions` to subscribe to the `portal`, and we use `effects` to\ncarefully route the changes to the `reducers`\n\n## Rendering\n\nRendering was really tricky at first, because the `morphdom` does not see the modified `codemirror`, but\nrather the simple `div` that was created originally.  To get around this, we need to grab the innerHTML\nof the `codemirror` and pass it to `bel`\n\n```javascript\nconst codemirror = require('../lib/codemirror-portal').create({\n  namespace: 'codemirror'\n}, {\n  lineNumbers: true,\n  autofocus: true\n})\n\nmodule.exports = () =\u003e {\n  if (codemirror.innerHTML) {\n    const element = document.createElement('div')\n    element.innerHTML = codemirror.innerHTML\n    return element\n  } else {\n    return codemirror\n  }\n}\n```\n\nThis ensures the morphDOM is capable of seeing the entire lsit of nodes so when things update it doesnt wipe out\nthe whole thing.\n\nFinally we can see our codemirror here:\n\n```javascript\nconst html = require('choo/html')\nconst codemirror = require('../elements/codemirror')\n\nmodule.exports = (state, prev, send) =\u003e {\n  return html`\n    \u003cmain\u003e\n      ${codemirror()}\n      ${state.codemirror.value}\n    \u003c/main\u003e\n  `\n}\n```\n\nI hope this was helpful.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftrainyard%2Fchoo-codemirror","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftrainyard%2Fchoo-codemirror","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftrainyard%2Fchoo-codemirror/lists"}