GithubHelp home page GithubHelp logo

saul-mirone / prosemirror-adapter Goto Github PK

View Code? Open in Web Editor NEW
96.0 3.0 6.0 851 KB

Universal adapter to create prosemirror nodeview from modern UI frameworks.

License: MIT License

JavaScript 1.64% Shell 0.06% TypeScript 84.93% HTML 4.73% CSS 1.08% Vue 3.72% Svelte 3.83%
prosemirror javascript react typescript vue

prosemirror-adapter's Introduction

prosemirror-adapter





Universal adapter for ProseMirror to use it with modern UI frameworks.

npm ci

What is this?

Prosemirror is a toolkit to build modern rich text editors. But it is not a good fit for a modern UI framework like React or Vue. This adapter is a low level tool to make it work with them without pain. You'll need this adapter if you want to use Prosemirror to build a rich text editor with complex UI in modern UI frameworks we support.

What's on the plan?

What's not on the plan?

This package should only take care about building a bridge between prosemirror and UI frameworks. It should be kept as an low level binding. So something out of this scope will not be considered. For example:

  • โŽ We won't provide UI components.
  • โŽ We won't provide key bindings.
  • โŽ We won't provide schema for special modules like table or math.

Getting Started

react vue svelte lit
react vue svelte lit

Contributing

PR welcome! Follow our contribution guide to learn how to contribute to prosemirror-adapter.

License

MIT

prosemirror-adapter's People

Contributors

github-actions[bot] avatar makcyd avatar ocavue avatar renovate[bot] avatar saul-mirone avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

prosemirror-adapter's Issues

[Feature request] Support for Angular

Is there any chance you could support angular? I love milkdown and your work around it. As I am forced to use angular for my project, I would love to see this adapter for angular.

Thank you!

Svelte: Can't get useNodeViewContext('contentRef') to work with the diagram plugin

@prosemirror-adapter/svelte 0.2.5
svelte 3.58.0
Milkdown 7.2

I created a Milkdown editor with the diagram plugin. The editor content defaults to a string starting with ```mermaid

This works fine. Milkdown displays the Mermaid diagram text (Mermaid code).

<div data-type="diagram" data-id="gfceeaae" data-value="  graph TD;
    A-->B;
    A-->C;
    B-->D;
    C-->D;" contenteditable="false">  graph TD;
    A--&gt;B;
    A--&gt;C;
    B--&gt;D;
    C--&gt;D;</div>

I want to implement something similar to the way the Milkdown playground behaves (edit diagram / display diagram as svg), but using Svelte. I added a nodeview plugin:

use( [diagram, $view(diagramSchema.node, () =>
    nodeViewFactory({
      component: MyComponent,
    }))]);

MyComponent.svelte looks like:

<script>
const contentRef = useNodeViewContext('contentRef');
</script>

<div class='text-red-500' use:contentRef/>

The produced HTML looks like the following. The Mermaid diagram code does not appear. I have tried:

  1. contentAs: 'div'
  2. as: 'div'
  3. as: 'div', contentAs: 'div'
<div class="milkdown">
<div contenteditable="true" translate="no" class="ProseMirror editor" role="textbox">
<div data-node-view-root="true">
<div class="text-red-500">
<div data-node-view-content="true" style="white-space: inherit;">
<br class="ProseMirror-trailingBreak"></div></div></div></div></div>

contentRef is a Svelte action that acts on the div that I provided.

(t4) => {
          t4 && t4 instanceof HTMLElement && 
             this.contentDOM && t4.firstChild !== this.contentDOM && 
             t4.appendChild(this.contentDOM);
}

this.contentDOM appears to be:

<div data-node-view-content="true" style="white-space: inherit;">
<br class="ProseMirror-trailingBreak"></div>

Apparently I need to call useNodeViewContext('node') instead and interact with the prose mirror node.

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

This repository currently has no open or pending branches.

Detected dependencies

github-actions
.github/workflows/ci.yml
  • actions/checkout v4
  • actions/setup-node v4
  • pnpm/action-setup v4.0.0
  • actions/cache v4
  • cypress-io/github-action v6
  • cypress-io/github-action v6
  • changesets/action v1
  • actions/upload-artifact v4.3.6
  • actions/upload-artifact v4.3.6
npm
e2e/package.json
  • @lit-labs/context ^0.5.0
  • @sveltejs/vite-plugin-svelte ^3.0.0
  • @types/react ^18.0.30
  • @types/react-dom ^18.0.11
  • @vitejs/plugin-react ^4.0.0
  • @vitejs/plugin-vue ^5.0.0
  • cypress ^13.2.0
  • lit ^3.0.0
  • prosemirror-example-setup ^1.2.1
  • prosemirror-menu ^1.2.1
  • prosemirror-schema-basic ^1.2.1
  • react ^18.2.0
  • react-dom ^18.2.0
  • start-server-and-test ^2.0.0
  • svelte ^4.0.0
  • vue 3.4.38
package.json
  • @antfu/eslint-config ^2.19.0
  • @changesets/cli ^2.26.1
  • @commitlint/cli ^19.0.0
  • @commitlint/config-conventional ^19.0.0
  • @eslint/compat ^1.0.1
  • @eslint/eslintrc ^3.1.0
  • @type-config/strict ^1.2.1
  • @types/rollup-plugin-auto-external ^2.0.2
  • concurrently ^8.0.0
  • cross-env ^7.0.3
  • eslint ^9.3.0
  • eslint-plugin-react ^7.34.1
  • eslint-plugin-react-hooks ^4.6.2
  • git-cz ^4.9.0
  • husky ^9.0.11
  • lint-staged ^15.0.0
  • prosemirror-keymap ^1.2.1
  • prosemirror-model ^1.19.0
  • prosemirror-state ^1.4.2
  • prosemirror-view ^1.30.2
  • tslib ^2.5.0
  • typescript ^5.4.5
  • vite ^5.2.11
  • node >=20
  • pnpm 9.8.0
packages/core/package.json
  • tslib ^2.5.0
packages/lit/package.json
  • nanoid ^5.0.0
  • tslib ^2.5.0
  • @lit-labs/context ^0.5.0
  • lit ^3.0.0
  • @lit-labs/context ^0.5.0
  • lit ^3.0.0
packages/react/package.json
  • nanoid ^5.0.0
  • tslib ^2.5.0
  • @types/react ^18.0.30
  • @types/react-dom ^18.0.11
  • react ^18.2.0
  • react-dom ^18.2.0
  • react *
  • react-dom *
packages/svelte/package.json
  • nanoid ^5.0.0
  • tslib ^2.5.0
  • @sveltejs/vite-plugin-svelte ^3.0.0
  • svelte ^4.0.0
  • svelte ^4.0.0
packages/vue/package.json
  • nanoid ^5.0.0
  • tslib ^2.5.0
  • @vitejs/plugin-vue-jsx ^4.0.0
  • vue ^3.4.31
  • vue ^3.0.0

  • Check this box to trigger a request for Renovate to run again on this repository

[Svelte] Is there a way to restore the cursor location when a node's value is modified via setAttrs?

When combining Milkdown and prosemirror-adapter/svelte, setAttrs({ value: 5 }) causes the Svelte component (declared via $view) to be removed and a new component takes its place. In my case, the Svelte component consists of a textarea. setAtrrs() moves the cursor out of the textarea to the top of the Milkdown editor. I guess I could capture the cursor location in update() but I'm not sure what I could do to find it later when the new Svelte component is created. I think I'm supposed to use a Svelte store for this. Is that correct? I realize that a better solution is to use a Prosemirror Editor instead of textarea and use transactions (which has better support for undo/redo and collaboration), but that's what the Milkdown Playground does eg with the diagram plugin.

Version incompatibility in @prosemirror-adapter/svelte vs SvelteKit

When using the code in examples/svelte I get error in this code (Editor.svelte, lines 32, 27, etc.):

 // line 29:
  function editor(element: HTMLElement) {
    editorView = createEditorView(element, {
      paragraph: nodeViewFactory({
        component: Paragraph,
        as: 'div',
        contentAs: 'p'
      }),
      heading: nodeViewFactory({
        component: Heading
      })
    }, [
      new Plugin({
        view: pluginViewFactory({
          component: Size
        })
      }),
...

The error reads:

Type 'typeof Paragraph__SvelteComponent_' is not assignable to type 'SvelteNodeViewComponent'.
Types of parameters 'options' and 'options' are incompatible.
Type 'ComponentConstructorOptions' is not assignable to type 'ComponentConstructorOptions<Record<string, never>>'.
Type 'EmptyProps' is not assignable to type 'Record<string, never>'.
Index signature for type 'string' is missing in type 'EmptyProps'.ts(2322)

Not sure how exactly approach a fix... My guess is that changing type in the adapter code should suffice, but did not have the courage to look inside, yet.

"@sveltejs/kit": "^1.5.0",
"@sveltejs/adapter-auto": "^2.0.0",
"svelte": "^3.54.0",
"svelte-check": "^3.0.1",
"@prosemirror-adapter/svelte": "^0.2.4",

[Svelte] setAttrs doesn't send changes to collaborators

Basic collaboration is working. Adding text to the document is sent to collaborators. However, I'm having trouble with a custom node view.

A custom node view calls setAttrs({value: ...}) to modify the value (Mermaid code) of a diagram node, similar to the way the Milkdown Playground does it.

It gets setAttrs via const setAttrs = useNodeViewContext("setAttrs");

I don't see any errors in the JavaScript console or elsewhere. Collaborators connected to the same document don't seem to be receiving the changes. Is there something else that I need to do to modify the Y doc?

I can also guess that if I'm able to get that working, the custom nodeview won't know its node was changed. Can the store returned by useNodeViewContext("node") be used to re-render the component or does Prosemirror handle that?

Here's my code

Svelte 4 support?

npm install @prosemirror-adapter/svelte
npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR! 
npm ERR! While resolving: [email protected]
npm ERR! Found: [email protected]
npm ERR! node_modules/svelte
npm ERR!   dev svelte@"^4.0.5" from the root project
npm ERR! 
npm ERR! Could not resolve dependency:
npm ERR! peer svelte@"^3.57.0" from @prosemirror-adapter/[email protected]
npm ERR! node_modules/@prosemirror-adapter/svelte
npm ERR!   @prosemirror-adapter/svelte@"*" from the root project
npm ERR! 
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.
npm ERR! 
npm ERR! 
npm ERR! For a full report see:
npm ERR! /Users/rchrdnsh/.npm/_logs/2023-07-15T23_53_13_299Z-eresolve-report.txt

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/rchrdnsh/.npm/_logs/2023-07-15T23_53_13_299Z-debug-0.log

when installing...

React warns about `flushSync` usage when dynamically adding or removing plugins that use `widgetViewFactory`

In my editor, I use a piece of React state to control whether a plugin should be added to or removed from ProseMirror's plugins list, to achieve the effect of enabling or disabling.

For example,

// The state to control enable/disable
const [enablePlugin, setEnablePlugin] = useState(true)

// An effect to reconfigure plugins
useEffect(() => {
  if (!enablePlugin) return

  const view = viewRef.current
  if (!view) return

  // Add plugin
  const { state } = view
  const newState = state.reconfigure({
    plugins: state.plugins.concat(decorationPlugin),
  })
  view.updateState(newState)

  return () => {
    // Remove plugin
    const currentState = view.state
    const newState = currentState.reconfigure({
      plugins: currentState.plugins.filter((plugin) => {
        return plugin !== decorationPlugin
      }),
    })
    view.updateState(newState)
  }
}, [enablePlugin, decorationPlugin])

The problem is that whenever a plugin that renders React components with widgetViewFactory is added or removed, I get an error message in the console saying:

Warning: flushSync was called from inside a lifecycle method. React cannot flush when React is already rendering. Consider moving this call to a scheduler task or micro task.

Although rendering still works, I wonder if there's a way to suppress the error.

Here is the complete and working code adapted from the react example: https://github.com/dragonman225/prosemirror-adapter/blob/main/examples/react/components/Editor.tsx

initialVNode is null

Im trying to use prosemirror adapter in vue. im using this code from the vue documentation to create a editor with a paragraph. The only thing i changed in my code is that i added code for the state and schema. When i run the code i always get this error:

TypeError: initialVNode is null
    componentUpdateFn runtime-core.esm-bundler.js:5158
    run reactivity.esm-bundler.js:181
    update runtime-core.esm-bundler.js:5270
    setupRenderEffect runtime-core.esm-bundler.js:5280
    mountComponent runtime-core.esm-bundler.js:5048
    processComponent runtime-core.esm-bundler.js:5002
    patch runtime-core.esm-bundler.js:4471
    componentUpdateFn runtime-core.esm-bundler.js:5146
    run reactivity.esm-bundler.js:181
    update runtime-core.esm-bundler.js:5270
    setupRenderEffect runtime-core.esm-bundler.js:5280
    mountComponent runtime-core.esm-bundler.js:5048
    processComponent runtime-core.esm-bundler.js:5002
    patch runtime-core.esm-bundler.js:4471
    componentUpdateFn runtime-core.esm-bundler.js:5146
    run reactivity.esm-bundler.js:181
    update runtime-core.esm-bundler.js:5270
    setupRenderEffect runtime-core.esm-bundler.js:5280
    mountComponent runtime-core.esm-bundler.js:5048
    processComponent runtime-core.esm-bundler.js:5002
    patch runtime-core.esm-bundler.js:4471
    componentUpdateFn runtime-core.esm-bundler.js:5146
    run reactivity.esm-bundler.js:181
    update runtime-core.esm-bundler.js:5270
    setupRenderEffect runtime-core.esm-bundler.js:5280
    mountComponent runtime-core.esm-bundler.js:5048
    processComponent runtime-core.esm-bundler.js:5002
    patch runtime-core.esm-bundler.js:4471
    componentUpdateFn runtime-core.esm-bundler.js:5146
    run reactivity.esm-bundler.js:181
    update runtime-core.esm-bundler.js:5270
    setupRenderEffect runtime-core.esm-bundler.js:5280
    mountComponent runtime-core.esm-bundler.js:5048
    processComponent runtime-core.esm-bundler.js:5002
    patch runtime-core.esm-bundler.js:4471
    render2 runtime-core.esm-bundler.js:5796
    mount runtime-core.esm-bundler.js:3063
    mount runtime-dom.esm-bundler.js:1527
    renderToCanvas chunk-ERN3BQXJ.mjs:5
    renderToCanvas runtime.js:8247
    mount chunk-ERN3BQXJ.mjs:5
    mount runtime.js:8260
    runPhase runtime.js:8185
    mount runtime.js:8259
    render runtime.js:8285
    renderToElement runtime.js:8207
    renderSelection runtime.js:9097
    selectSpecifiedStory runtime.js:8976
    initializeWithStoryIndex runtime.js:8950
    initializeWithProjectAnnotations runtime.js:8433
    initialize runtime.js:8406
    gi runtime.js:8934
    ts runtime.js:9365
    <anonymous> vite-app.js:22

This is my code:

BlockEditorProvider.vue

<script setup lang="ts">
import { ProsemirrorAdapterProvider } from '@prosemirror-adapter/vue'
import BlockEditor from '@/Components/BlockEditor.vue'
</script>


<template>
  <ProsemirrorAdapterProvider>
    <BlockEditor/>
  </ProsemirrorAdapterProvider>
</template>

BlockEditor.vue

<script setup lang="ts">
import type { VNodeRef } from 'vue'
import { useNodeViewFactory } from '@prosemirror-adapter/vue'
import Paragraph from './Blocks/Paragraph.vue'

import { EditorState } from 'prosemirror-state'
import { EditorView } from 'prosemirror-view';
import { Schema } from 'prosemirror-model'

const nodeViewFactory = useNodeViewFactory()

const schema = new Schema({
    nodes: {
        doc: {
            content: "block+"
        },
        paragraph: {
            group: "block",
        },
        text: {
            group: "inline"
        }
    },
    marks: {}
})

let state = EditorState.create({
    schema: schema,
})


const editorRef: VNodeRef = (element) => {
  const el = element as HTMLElement

  if (!el || el.firstChild)
    return

  const editorView = new EditorView(el, {
    state: state,
    nodeViews: {
      paragraph: nodeViewFactory({
        component: Paragraph,
        as: 'div',  // Ensure this matches the expected tag of the component
        contentAs: 'p',  // Ensure this matches the expected tag in the Paragraph component
      }),
    }
  })
}

</script>

<template>
    <div :ref="editorRef" class="editor" />
</template>

Paragraph.vue

<script setup lang="ts">
import { useNodeViewContext } from '@prosemirror-adapter/vue'

const { contentRef, selected } = useNodeViewContext()
</script>

<template>
  <div :ref="contentRef" role="presentation"  />
</template>

<style scoped>
.selected {
    outline: blue solid 1px;
}
</style>

What am i doing wrong here ?

[Feature request] Support for Alpine.js

Any chance you could add support for Alpine.js?

It's based on Vue and is widely used as part of Laravel Livewire and in packages like Filament.

I might give it a go, if you can't, but I'm much more a backend guy and find the Prosemirror API quite confusing ๐Ÿ˜‰

Cheers!

should `triggerRef` after `VuePluginView.updateContext` in`@prosemirror-adapter/vue`

const { view } = usePluginViewContext()
const somePropDeep = computed(()=>view.value.state.select)//or other things

Vue can't track the effect of somePropDeep when view was shallowRef, we should use triggerRef manually after assigned


I just found one place, I think there might be such other things like this in this library

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.