Giter VIP home page Giter VIP logo

teemukoivisto / prosemirror-dev-toolkit Goto Github PK

View Code? Open in Web Editor NEW
108.0 3.0 6.0 10.3 MB

Injectable developer tools for ProseMirror rich-text editors implemented in Svelte and TypeScript.

Home Page: https://teemukoivisto.github.io/prosemirror-dev-toolkit/

License: MIT License

JavaScript 3.21% Svelte 29.85% TypeScript 63.59% HTML 0.29% CSS 2.67% SCSS 0.31% Shell 0.07%
prosemirror devtools inspector svelte typescript

prosemirror-dev-toolkit's Introduction

prosemirror-dev-toolkit version package minified size package size

This is a rewrite of prosemirror-dev-tools which I'm a big fan of, yet have felt could use some improvements. Since it didn't seem that actively maintained anymore and also because I wanted to remove the hard dependency to React, use TypeScript and make the whole thing smaller, I took it to my own hands to rewrite it in Svelte.

Unlike React, Svelte compiles directly to JS without having to bundle a runtime which should make the library smaller to use in projects that don't use React. I also pruned some of the extra dependencies by using out-of-the-box Svelte features and have experimented making the library injectable as a stand-alone script.

Also as a Chrome extension 🚀

This is a big improvement as you no longer have to bundle prosemirror-dev-toolkit with your dependencies (and filter it out for production build). Also, it allows you to inspect any live editor. It's basically the inject.js script packaged into its own extension.

Demo

npm i prosemirror-dev-toolkit

And use it eg:

import { applyDevTools } from 'prosemirror-dev-toolkit'

import { EditorState } from 'prosemirror-state'
import { EditorView } from 'prosemirror-view'
import { schema } from 'prosemirror-schema-basic'
import { exampleSetup } from 'prosemirror-example-setup'

const view = new EditorView(document.querySelector('#editor') as HTMLElement, {
  state: EditorState.create({
    doc: schema.nodes.doc.createAndFill(),
    plugins: exampleSetup({ schema })
  })
})
applyDevTools(view)

Code sandbox

API

type ButtonPosition = 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'
interface DevToolsOpts {
  devToolsExpanded?: boolean
  buttonPosition?: ButtonPosition
  disableWebComponent?: boolean
}
declare function applyDevTools(view: EditorView, opts?: DevToolsOpts): void
declare function removeDevTools(): void

declare global {
  interface Window {
    applyDevTools: typeof applyDevTools
    editorView?: EditorView
    pmCmd?: (cmd: Command) => void
    _node?: { node: PMNode; pos: number }
    _doc?: {
      [key: string]: any
    }
    _trs?: Transaction[]
    _plugin?: [Plugin | undefined, unknown]
  }
}
export { applyDevTools, removeDevTools, ButtonPosition, DevToolsOpts }

I am adding properties to the global Window interface which may or may not that convenient. From my perspective I don't have to ts-ignore those assignments but if you use those properties in your repos you'll have to obey the types or receive rather weird errors (that may take a moment to figure out).

Features

As was in the old tools, prosemirror-dev-toolkit consists of 6 tabs which interact with the PM editor in various ways. Basically what happens is dev-toolkit injects itself into the DOM, visible as the rounded button in the bottom right corner, and then sets the dispatchTransaction prop of the EditorView to be able to track transactions.

In addition it can persist snapshots and hydrate them which I enhanced by adding export/import from JSON as well as ensuring the functionality doesn't break with Yjs. In the old dev-tools there was a node picker to inspect PM nodes that I have not had time to remake.

For quick live editor instance debugging I have added a script to inject the dev-toolkit directly from CDN: inject.js Not 100% bulletproof but works in most cases. Improvements welcome. Might use it to turn this into a Chrome extension.

State

State tab

Shows the current toJSON'd state.doc as well as the selection, augmented by a few additional properties, active marks and a simple summary of the document. These tree views were implemented in the original with react-json-tree library which although ported to Svelte (svelte-json-tree) felt a little complex and difficult to customize. Instead, I wrote a simple recursive component and then rolled it out as a separate Svelte library: https://github.com/TeemuKoivisto/svelte-tree-view

History

History tab

History tab shows the last dispatched transaction with a diff of the changed content, diff of the changed selection, toDOM'd document slice between the selection start and end and the actual transactions at the bottom. Rather than just showing the originating transaction, I am also displaying all the appended transactions as well.

Copying the old diffing logic was perhaps the most annoying feature to implement as it required me to wrap my head around the jsondiffpatch's delta format and figure out how to rewrite it in Svelte. Somewhat interesting topic, certainly, but quite tedious. I changed the original colors to a perhaps more distinct palette and added the transaction to the history entry as well. I've found it quite useful.

I added some new features to the trees such as 'log' and 'copy' buttons which log the node to window (eg into _node variable) or add it to your clipboard.

You can hydrate the doc from a transaction by double-clicking it. I have thought of adding import/export of transaction history to allow replaying them but haven't had time for it.

Plugins

Plugins tab

Shows the current plugins and their states. This is mostly the same as in the old tools but I added some convenience buttons to for example log the plugin to a _plugin object that you can manipulate from the console. Handy for inspecting the plugin editor props which are not visible from the plugin state.

As a side-note, I'm sure I have not covered all possible object types in svelte-tree-view beyond the common Objects/Arrays/Maps/Sets. Make an issue or PR if something's weird.

Schema

Schema tab

Shows the current schema's nodes and marks. Same as in the old tools but I added circular node detection to make the recursion a little lighter. Not strictly needed as all the nodes are uncollapsed and can't be auto-expanded but hey, it's there.

Structure

Structure tab

A bit more complicated tab it shows the document in a neat block view with a side panel to inspect the node contents. Auto-expands the content whenever it's within reasonable size (less than 50 children). You can double click nodes to scroll into them. Thought about allowing selecting and deleting nodes directly from the DocView.

Snapshots

Snapshots tab

Shows the stored snapshots (toJSON'd topNode eg "doc") in localStorage. The changes I've made are switching to date strings instead of unix timestamps for default names. Then, you can double-click the snapshot to edit its name. 'Show' button replaces the current doc with the snapshot doc, allowing you to revert it with 'Hide'. 'Restore' does a one-way replacement of the doc with the snapshot. And well I say one-way but since I don't erase the actual PM history you can Ctrl-z your way out of it.

'Export' toJSONs and downloads the snapshot. You have to now double-click 'Delete' to prevent occasional misclicks. There's also a new 'Import snapshot' button above the main panel to replace the doc with exported snapshot.

How to run locally

You need pnpm installed globally.

  1. pnpm
  2. pnpm start

Should open a React app at http://localhost:3300

Tests

To run the end-to-end tests you must launch the site (pnpm site) and then run pnpm --filter prosemirror-dev-tolkit test:e2e. NOTE: the snapshots might locally fail. They should work in CI however and hopefully they'll be fixed when I next time migrate Cypress and its libraries.

Contributing

Contributions and issues are more than welcome! This project uses changesets https://github.com/changesets/changesets to generate changelogs but in short, when you commit from terminal the changeset prompt is opened in which you should write your functionality using conventional commits https://www.conventionalcommits.org/en/v1.0.0/ to create a changeset (which is for something that changes the library for the downstream users).

prosemirror-dev-toolkit's People

Contributors

github-actions[bot] avatar ocavue avatar teemukoivisto 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  avatar  avatar

Watchers

 avatar  avatar  avatar

prosemirror-dev-toolkit's Issues

Extension causes bug with NodeView .update not being called

(reposted from discuss.prosemirror.net)

I have a problem in my app of a .update method not getting called. I’ve reduced the issue to a minimal example (single html file). It turns out to only happen when the dev-toolkit is active.

I have a doc with just one node, with a node-view that displays a button. I would expect that on clicking the button I would see a message in the console but I don’t.

During testing I noticed things seem to behave properly when the editor is not mounted in the DOM. I’ve included a demonstration of this in the example code (so in fact I the example code should display two messages in the console but I only see one)

https://gist.github.com/tslocke/466271841ea57f911fdddd917f4d478e

pollutes globals

console was telling me windows.get was defined, which was odd. turns out in inject.js.

image

Skips iframes: .ProseMirror not found

I'm loading prosemirror inside an iframe (to the same domain).
Like this:
image

The dev-toolkit chrome extension doesn't find it.
Thanks for this wonderful tool.

When using HMR with TipTap, throws error on module reload.

TypeError: Cannot read properties of null (reading 'matchesNode')
    at EditorView.updateStateInner (http://localhost:3000/static/js/bundle.js:183490:45)
    at EditorView.update (http://localhost:3000/static/js/bundle.js:183442:10)
    at EditorView.setProps (http://localhost:3000/static/js/bundle.js:183454:10)
    at subscribeToDispatchTransaction (http://localhost:3000/static/js/bundle.js:171086:8)
    at applyDevTools (http://localhost:3000/static/js/bundle.js:171140:3)
    at http://localhost:3000/static/js/bundle.js:3994:77
    at commitHookEffectListMount (http://localhost:3000/static/js/bundle.js:113152:30)
    at commitPassiveMountOnFiber (http://localhost:3000/static/js/bundle.js:114645:17)
    at commitPassiveMountEffects_complete (http://localhost:3000/static/js/bundle.js:114617:13)
    at commitPassiveMountEffects_begin (http://localhost:3000/static/js/bundle.js:114607:11)

Looking at what's in updateStateInner, this happens because editorView.docView is null, which happens when the editorView is destroyed. I suspect HMR (maybe TipTap?) is destroying the editor view, but prosemirror-dev-toolkit is still listening to the dispatch event and updating props. I suspect this is fixed by a if (!view.docView) { return } at the top of the subscribeToDispatchTransaction function.

'svelte-kit' is not recognized as an internal or external command

When npm install v1.0.2 or above, I hit this error:

npm ERR! 'svelte-kit' is not recognized as an internal or external command,
npm ERR! operable program or batch file.
npm ERR! 'true' is not recognized as an internal or external command,
npm ERR! operable program or batch file.

v1.0.1 is fine.

'svelte-kit' is not recognized as an internal or external command

When npm install v1.0.2 or above, I hit this error:

npm ERR! 'svelte-kit' is not recognized as an internal or external command,
npm ERR! operable program or batch file.
npm ERR! 'true' is not recognized as an internal or external command,
npm ERR! operable program or batch file.

v1.0.1 is fine.

Breaking google docs/sheets

This has just started for me - I get an error as soon as I load a google sheet or a google doc. Fixed when I disable this extension.

Issue with injected Svelte styles

Not 100% grounded and completely investigated, but I have noticed that on some pages, content elements are hidden whenever this Chrome Extension is activated.

A short investigation led to the following assumption:
The extension/DevTools uses Svelte and injects its styles at runtime. This includes a generic hidden class. Because it is injected in the head as a style block, the specificity is higher than the same class referenced before.

Please see the following screenshots:

Without extension:
Bildschirm­foto 2023-02-14 um 15 04 37

Activated extension:
Bildschirm­foto 2023-02-14 um 15 03 25

This is the injection:
Bildschirm­foto 2023-02-14 um 15 02 24

If this is correct, it will disrupt functionality on many websites, as this is also a default class of Tailwind. If possible, it should be scoped.

[ UI Enhance ] Choose a more suitable font for the view

First of all, thank you for the amazing plugin! It's pretty useful and awesome!

As a multi-device user, I used to have a Windows PC. Now, I have a MacBook for daily work and an Ubuntu Linux PC for my side projects. However, I have noticed that the font rendering of this DevTool isn't quite well while developing.

Maybe we can choose FiraCode, JetBrains Mono... etc ?
Monospaced fonts can effectively enhance the appearance of a webpage, especially we're displaying many editor states and data ...
You can take the style of Chrome DevTools as a reference.

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.