{"id":18398116,"url":"https://github.com/ousc/ng-prosemirror-adapter","last_synced_at":"2025-10-13T09:10:36.491Z","repository":{"id":211052198,"uuid":"726319090","full_name":"ousc/ng-prosemirror-adapter","owner":"ousc","description":"prosemirror adapter for angular 17+","archived":false,"fork":false,"pushed_at":"2025-02-13T15:37:49.000Z","size":3539,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-07T04:34:02.779Z","etag":null,"topics":["angular","javascript","prosemirror","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/ousc.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,"publiccode":null,"codemeta":null}},"created_at":"2023-12-02T03:16:50.000Z","updated_at":"2025-03-13T15:13:25.000Z","dependencies_parsed_at":"2023-12-25T02:41:44.758Z","dependency_job_id":"e5aa8a52-8499-4234-bbe5-574c360c4085","html_url":"https://github.com/ousc/ng-prosemirror-adapter","commit_stats":null,"previous_names":["ousc/ng-prosemirror-adaptor","ousc/ng-prosemirror-adapter"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/ousc/ng-prosemirror-adapter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ousc%2Fng-prosemirror-adapter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ousc%2Fng-prosemirror-adapter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ousc%2Fng-prosemirror-adapter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ousc%2Fng-prosemirror-adapter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ousc","download_url":"https://codeload.github.com/ousc/ng-prosemirror-adapter/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ousc%2Fng-prosemirror-adapter/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279014484,"owners_count":26085535,"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","status":"online","status_checked_at":"2025-10-13T02:00:06.723Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["angular","javascript","prosemirror","typescript"],"created_at":"2024-11-06T02:19:55.152Z","updated_at":"2025-10-13T09:10:36.461Z","avatar_url":"https://github.com/ousc.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://ousc.github.io/ng-prosemirror-adapter\"\u003e\n    \u003cimg src=\"prosemirrorAdapterLogo.png\" width=\"230\" style=\"vertical-align: middle;\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003e\nNG-PROSEMIRROR-ADAPTER\n\u003c/h1\u003e\n\n[Angular](https://angular.dev/) adapter for [ProseMirror](https://prosemirror.net/), only supports Angular **17**+.\n\nMore detail please move to [**prosemirror-adapter**](https://github.com/Saul-Mirone/prosemirror-adapter) by [**@Saul-Mirone**](https://github.com/Saul-Mirone).**This project is just adding support for Angular.**\n\n## Example\n\nYou can run this example by:\n\n```bash\ngit clone https://github.com/ousc/ng-prosemirror-adapter.git\ncd ng-prosemirror-adapter\nnpm install\nnpm run start\n```\n\n## Versions\n\n| Angular | Prosemirror-adapter |\n|---------|---------------------|\n| 18.0.0  | 0.0.8               |\n| 17.0.0  | 0.0.6               |\n\n## Online Demo\n[https://ousc.github.io/ng-prosemirror-adapter](https://ousc.github.io/ng-prosemirror-adapter/)\n\n## Getting Started\n\n### Install the package\n\n```bash\nnpm install ng-prosemirror-adapter\n```\n\n### Wrap your component with provider\n\n```html\n\u003cng-prosemirror-adapter-provider\u003e\n  \u003cYourAwesomeEditor/\u003e\n\u003c/ng-prosemirror-adapter-provider\u003e\n```\n\n\u003cdetails\u003e\n\n\u003csummary\u003e\n\n### Play with node view\n\n\u003c/summary\u003e\n\nIn this section we will implement a node view for paragraph node.\n\n#### Build component for [node view](https://prosemirror.net/docs/ref/#view.NodeView)\n\n```ts\nimport {Component} from '@angular/core';\nimport {NgProsemirrorNode} from 'ng-prosemirror-adapter';\n\n\n@Component({\n  selector: 'paragraph',\n  template: `\n      \u003cdiv role=\"presentation\" [class.selected]=\"selected\"\u003e\u003c/div\u003e\n  `,\n  styles: [`\n    :host .selected {\n      outline: blue solid 1px;\n    }\n  `],\n  standalone: true\n})\nexport class Paragraph extends NgProsemirrorNode {}\n```\n\n#### Bind node view components with prosemirror\n\n```ts\nimport {AfterViewInit, Component, ElementRef, forwardRef, ViewChild} from '@angular/core';\nimport {Paragraph} from \"../paragraph.component\";\nimport {NgProsemirrorEditor} from 'ng-prosemirror-adapter';\n\n@Component({\n  selector: 'editor',\n  standalone: true,\n  template: `\u003cdiv class=\"editor\" #editorRef\u003e\u003c/div\u003e`,\n  providers: [{provide: NgProsemirrorEditor, useExisting: forwardRef(() =\u003e EditorComponent)}],\n})\nexport class EditorComponent extends NgProsemirrorEditor implements AfterViewInit {\n  @ViewChild('editorRef') editorRef: ElementRef;\n\n  async ngAfterViewInit(): Promise\u003cvoid\u003e {\n    const element = this.editorRef.nativeElement;\n    if (!element || element.firstChild)\n      return\n\n    const editorView = new EditorView(element, {\n      state: YourProsemirrorEditorState,\n      nodeViews: {\n        paragraph: this.provider.createNodeView({\n          component: Paragraph,\n          as: 'div',\n          contentAs: 'p',\n        }),\n      }\n    })\n  }\n}\n\n```\n\n🚀 Congratulations! You have built your first angular node view with prosemirror-adapter.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\n\u003csummary\u003e\n\n### Play with plugin view\n\n\u003c/summary\u003e\n\nIn this section we will implement a plugin view that will display the size of the document.\n\n#### Build component for [plugin view](https://prosemirror.net/docs/ref/#state.PluginView)\n\n```ts\nimport {Component} from '@angular/core';\nimport {NgProsemirrorPlugin} from 'ng-prosemirror-adapter';\n\n@Component({\n  selector: 'size',\n  template: `\u003cdiv\u003eSize for document: {{ size }}\u003c/div\u003e`,\n  styles: [],\n  standalone: true\n})\nexport class Size extends NgProsemirrorPlugin {\n\n  get size() {\n    return this.state?.doc?.nodeSize\n  }\n}\n\n```\n\n#### Bind plugin view components with prosemirror\n\n```tsx\nimport {AfterViewInit, Component, ElementRef, forwardRef, ViewChild} from '@angular/core';\nimport {Size} from \"../size.component\";\nimport {NgProsemirrorEditor} from 'ng-prosemirror-adapter';\n\n@Component({\n  selector: 'editor',\n  standalone: true,\n  template: `\u003cdiv class=\"editor\" #editorRef\u003e\u003c/div\u003e`,\n  providers: [{provide: NgProsemirrorEditor, useExisting: forwardRef(() =\u003e EditorComponent)}],\n})\nexport class EditorComponent extends NgProsemirrorEditor implements AfterViewInit {\n  @ViewChild('editorRef') editorRef: ElementRef;\n\n  async ngAfterViewInit(): Promise\u003cvoid\u003e {\n    const element = this.editorRef.nativeElement;\n    if (!element || element.firstChild)\n      return\n\n    const editorView = new EditorView(element, {\n      state: YourProsemirrorEditorState,\n      plugins: [\n        new Plugin({\n          view: await this.provider.createPluginView({ component: Size }),\n        }),\n      ]\n    })\n  }\n}\n```\n\n🚀 Congratulations! You have built your first angular plugin view with prosemirror-adapter.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\n\u003csummary\u003e\n\n### Play with widget view\n\n\u003c/summary\u003e\n\nIn this section we will implement a widget view that will add hashes for heading when selected.\n\n#### Build component for [widget decoration view](https://prosemirror.net/docs/ref/#view.Decoration%5Ewidget)\n\n```ts\nimport {Component} from '@angular/core';\nimport {NgProsemirrorWidget} from 'ng-prosemirror-adapter';\n\n@Component({\n  selector: 'hashes',\n  template: `\n      \u003cspan class=\"hash\"\u003e{{ hashes }}\u003c/span\u003e`,\n  styles: [`\n    .hash {\n      color: blue;\n      margin-right: 6px;\n    }`],\n  standalone: true\n})\nexport class Hashes extends NgProsemirrorWidget {\n  get level() {\n    return this.spec?.['level'];\n  }\n\n  get hashes() {\n    return Array(this.level || 0).fill('#').join('');\n  }\n}\n\n```\n\n#### Bind widget view components with prosemirror\n\n```ts\nimport {AfterViewInit, Component, ElementRef, forwardRef, ViewChild} from '@angular/core';\nimport {Hashes} from \"../hashes.component\";\nimport {NgProsemirrorEditor} from 'ng-prosemirror-adapter';\nimport {Plugin} from \"prosemirror-state\";\n\n@Component({\n  selector: 'editor',\n  standalone: true,\n  template: `\u003cdiv class=\"editor\" #editorRef\u003e\u003c/div\u003e`,\n  providers: [{provide: NgProsemirrorEditor, useExisting: forwardRef(() =\u003e EditorComponent)}],\n})\nexport class EditorComponent extends NgProsemirrorEditor implements AfterViewInit {\n  @ViewChild('editorRef') editorRef: ElementRef;\n\n  async ngAfterViewInit(): Promise\u003cvoid\u003e {\n    const element = this.editorRef.nativeElement;\n    if (!element || element.firstChild)\n      return\n\n    const editorView = new EditorView(element, {\n      state: YourProsemirrorEditorState,\n      plugins: [\n        new Plugin({\n          props: {\n            decorations: (state) =\u003e {\n              const getHashWidget = this.provider.createWidgetView({\n                as: 'i',\n                component: Hashes,\n              })\n              const {$from} = state.selection\n              const node = $from.node()\n              if (node.type.name !== 'heading')\n                return DecorationSet.empty\n\n              const widget = getHashWidget($from.before() + 1, {\n                side: -1,\n                level: node.attrs['level'],\n              })\n              return DecorationSet.create(state.doc, [widget])\n            },\n          },\n        }),\n      ]\n    })\n  }\n}\n```\n\n🚀 Congratulations! You have built your first angular widget view with prosemirror-adapter.\n\n\u003c/details\u003e\n\n## API\n\nif you properly wrap your component with `NgProsemirrorAdapterProvider`, and extends `NgProsemirrorEditor` or `NgProsemirrorNode` or `NgProsemirrorPlugin` or `NgProsemirrorWidget`, you can use `this.provider` to access the following APIs.\nelse you can use @ViewChild to access the NgProsemirrorAdapterProvider instance and use the following APIs.\n\n\u003cdetails\u003e\n\n\u003csummary\u003e\n\n### NgProsemirrorNode API\n\n\u003c/summary\u003e\n\n#### NgProsemirrorEditor.provider.createNodeView: NodeViewFactory =\u003e (options: NgNodeViewUserOptions) =\u003e NodeViewConstructor\n\n```ts\ntype NgNodeViewUserOptions = {\n  component: Type\u003cNgProsemirrorNode\u003e\n  as?: string | HTMLElement\n  contentAs?: string | HTMLElement\n  update?: (node: Node, decorations: readonly Decoration[], innerDecorations: DecorationSource) =\u003e boolean | void\n  ignoreMutation?: (mutation: MutationRecord) =\u003e boolean | void\n  selectNode?: () =\u003e void\n  deselectNode?: () =\u003e void\n  setSelection?: (anchor: number, head: number, root: Document | ShadowRoot) =\u003e void\n  stopEvent?: (event: Event) =\u003e boolean\n  destroy?: () =\u003e void\n\n  // Additional\n  onUpdate?: () =\u003e void\n  inputs?: {\n    [key: string]: any\n  },\n  key?: string\n}\n\ntype NodeViewFactory = (options: NgNodeViewUserOptions) =\u003e NodeViewConstructor\n```\n\n#### NgProsemirrorNode.context: NodeViewContext\n\n```ts\ninterface NodeViewContext {\n  // won't change\n  contentRef: NodeViewContentRef\n  view: EditorView\n  getPos: () =\u003e number | undefined\n  setAttrs: (attrs: Attrs) =\u003e void\n\n  // changes between updates\n  node: Node\n  selected: boolean\n  decorations: readonly Decoration[]\n  innerDecorations: DecorationSource\n}\n\ntype NodeViewContentRef = (node: HTMLElement | null) =\u003e void\n```\n\n### NgProsemirrorNode.view: EditorView\n\n### NgProsemirrorNode.contentRef: NodeViewContentRef\n\n### NgProsemirrorNode.getPos: () =\u003e number | undefined\n  \n### NgProsemirrorNode.setAttrs: (attrs: Attrs) =\u003e void\n\n### NgProsemirrorNode.node: Node\n\n### NgProsemirrorNode.selected: boolean\n\n### NgProsemirrorNode.decorations: readonly Decoration[]\n\n### NgProsemirrorNode.innerDecorations: DecorationSource\n\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\n\u003csummary\u003e\n\n### NgProsemirrorPlugin API\n\n\u003c/summary\u003e\n\n#### NgProsemirrorEditor.provider.createPluginView: NodeViewFactory =\u003e (options: NgNodeViewUserOptions) =\u003e NodeViewConstructor\n\n```ts\nexport type NgPluginViewUserOptions = {\n  component: Type\u003cNgProsemirrorPlugin\u003e\n  root?: (viewDOM: HTMLElement) =\u003e HTMLElement\n  update?: (view: EditorView, prevState: EditorState) =\u003e void\n  destroy?: () =\u003e void,\n  inputs?: {\n    [key: string]: any\n  },\n  key?: string\n}\n\nexport type PluginViewFactory = (options: NgPluginViewUserOptions) =\u003e Promise\u003cPluginViewSpec\u003e\n\n```\n\n#### NgProsemirrorPlugin.context: PluginViewContext\n\n```ts\nexport interface PluginViewContext {\n  view: EditorView\n  prevState: EditorState\n}\n```\n\n#### NgProsemirrorPlugin.view: EditorView\n\n#### NgProsemirrorPlugin.state: EditorState\n\n#### NgProsemirrorPlugin.prevState: EditorState\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\n\u003csummary\u003e\n\n### NgProsemirrorWidget API\n\n\u003c/summary\u003e\n\n#### NgProsemirrorEditor.provider.createWidgetView: WidgetViewFactory =\u003e (options: NgWidgetViewUserOptions) =\u003e WidgetViewConstructor\n\n```ts\nexport type NgWidgetUserOptions = {\n  as: string | HTMLElement\n  component: Type\u003cNgProsemirrorWidget\u003e,\n  inputs?: {\n    [key: string]: any\n  },\n  key?: string\n}\n\nexport type WidgetViewFactory = (options: NgWidgetUserOptions) =\u003e WidgetDecorationFactory\n```\n\n#### NgProsemirrorWidget.context: WidgetViewContext\n\n```ts\nexport interface WidgetViewContext {\n  view: EditorView\n  getPos: () =\u003e number | undefined\n  spec?: WidgetDecorationSpec\n}\n```\n\n#### NgProsemirrorWidget.view: EditorView\n\n#### NgProsemirrorWidget.getPos: () =\u003e number | undefined\n\n#### NgProsemirrorWidget.spec: WidgetDecorationSpec\n\n\u003c/details\u003e\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n## License\n\n[MIT License](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fousc%2Fng-prosemirror-adapter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fousc%2Fng-prosemirror-adapter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fousc%2Fng-prosemirror-adapter/lists"}