Skip to content

Milestones

List view

  • # Editor Status Users now can peek the editor status by calling `editor.status`. The possible values are: ```typescript export enum EditorStatus { Init = 'Init', OnCreat = 'OnCreated', Created = 'Created', OnDestroy = 'OnDestroy', Destroyed = 'Destroyed', } ``` Users can also inspect the change by passing a callback to `editor.onStatusChange`. ```typescript editor.onStatusChange((status) => { // Do something... }); ``` # Plugin Remove ## API ```typescript import { myPlugin } from 'my-plugin'; let editor; // after created editor = await Editor.make() .use(myPlugin) .create(); await editor.remove(myPlugin) // before created editor = await Editor.make() .use(myPlugin) .remove(myPlugin); ``` ## Post Progress for Plugin Plugin now can do cleanup it's state in a new `post` progress. ```typescript const plugin: MilkdownPlugin = (pre) => { // pre progress return (ctx) => { // run progress return (post) => { // post progress } } } ``` The post progress will execute when editor trying to remove the plugin. This feature makes it possible to enable and disable plugins in runtime: ```typescript const editor = await Editor.make() .use(gfm) .use(menu) .create(); await editor.remove(menu); editor.use(tooltip); // Recreate the editor. await editor.create(); ``` # Editor Destroy & Recreation Since we have a `remove` method for editor, it'll also bring us the `destroy` feature automatically by remove all plugins. ```typescript editor = await Editor.make().use(/*...*/).create(); await editor.destroy(); ``` We can also recreate an editor by calling `editor.create` after it's been created. This will call `editor.destroy` automatically. ```typescript editor = await Editor.make().use(/*...*/).create(); await editor.remove(somePlugin); await editor.create(); ``` # Move `inject` in factory from arguments to options Previously, if users want to inject slices when using `factory` to create plugins, they need to: ```typescript import { createPlugin } from '@milkdown/utils'; const slice1 = createSlice('my-slice'); const myPlugin = createPlugin(() => { // ... }, [slice1]); ``` We improve this API to make it align with other methods: ```typescript import { createPlugin } from '@milkdown/utils'; const slice1 = createSlice('my-slice'); const myPlugin = createPlugin(() => { return { // ... injectSlices: [slice1] } }); ``` # Table Input Rule When user input `|<col>x<row>| `, create a table with `<col>` cols and `<row>` rows. For example: `|3x2| ` or `|3X2| `.

    No due date
    4/4 issues closed
  • # Inline Sync Plugin In the previous versions of milkdown, the user input is handled by [prosemirror input rule](https://prosemirror.net/docs/ref/#inputrules). For example, if user type `*strong text*`, the user will get a piece of text decorated by strong style. However, there're two major limitation of input rule: 1. Inputrule can only match texts before user's cursor, if user doesn't type text in such a sequence, but type two `*` symbol and insert text between them, the input rule won't work. 2. Inputrule is to match the user input by a regexp, the markdown syntax rules are much more complicated than that. So, users may type some texts that match the inputrule but not really match the markdown syntax rule, for example, in commonmark, `asd**'ef**` shoudn't have any bold texts, but `asd **'ef**` and `asd**ef**` should have bold texts. The inline sync plugin is designed to solve this problem. When users type something, the plugin will transform the line (for better performance) to real markdown AST by serializer and render the AST to dom by parser, thus the input texts can be displayed correctly. The inline sync plugin is part of `@milkdown/preset-commonmark` so you don't need to use it yourself. It will be enabled by default. ### Config You can configure the behavior of inline sync plugin to adapt for some special cases: ```typescript editor.config((ctx) => { ctx.update(inlineSyncConfigCtx, (prevCfg) => ({ ...prevCfg, // your config here. })) }); ``` The possible properties are: ### shouldSyncNode A function to control whether the inline sync plugin should sync for the user change. You can inspect the current prosemirror node and the future prosemirror node (which will replace the current one) to decide whether to skip this sync action. ### movePlaceholder The inline sync plugin will insert a special charater that stands for the current user's `cursor`, this function is used to control whether we need to move the user's cursor to a right position. For example: ```markdown This is a *|*test*** line. ``` We use `|` to stand for the user cursor. If user type a `*` now and the cursor will be between two `*`, however, the `***test***` should be a word decorated by inline and bold style. So, the cursor should move to `***|test***`, if it stays at `**|*test***`, we'll get wrong content. ### placeholderConfig Use to decide which character the plugin will use to insert as placeholder. You should use a character that the user would never insert it themselves. Default value: ``` placeholderConfig: { hole: '∅', punctuation: '⁂', char: '∴', } ``` ### globalNodes In markdown, sometimes the parser and serializer needs some additional context for some nodes. For example, if user type `Test[^1]`, the parser will search for footnote definition for `[^1]`, if there is a definition, a footnote reference node will be generated, otherwise it will be a normal text node. So, we need to add `footnote_definition` in `globalNodes` list to tell the inline sync plugin it needs to collect this kind of nodes when sync. # Paste Code from VS Code > This feature needs `@milkdown/plugin-clipboard`. Now we add support for copy code in VS Code and paste it as a code block directly in milkdown. ![Code_0Qe9gtx8eZ](https://user-images.githubusercontent.com/10047788/188861914-00d6fa9f-8c95-405b-9327-bb85488698f1.gif) # Drag and Scroll > The feature needs `@milkdown/plugin-block` Drag the block will scroll the editor when cursor near the edge of the editor. ![msedge_C8JhaP1dz3](https://user-images.githubusercontent.com/10047788/188862174-aeeb1d58-4849-4ecb-bc3a-f08eaccde41d.gif) # rootDOMCtx `rootDOMCtx` is a slice that stores the root dom element of current editor. # remarkStringifyOptionsCtx `remarkStringifyOptionsCtx` is a slice that stores the config for [remark stringify](https://www.npmjs.com/package/remark-stringify#api) This is designed to make user can configure the remark stringify behaviors. For example: ```typescript editor.config((ctx) => { ctx.update(remarkStringify, (cfg) => ({ ...cfg, bullet: '+', }); }); ```

    No due date
    14/14 issues closed
  • # Block Plugin I'll introduce you a new plugin in our plugin family: `@milkdown/plugin-block`. This plugin provides two features: 1. Make blocks can be drag and drop by a handlebar. 2. When users click on handlebar, a list of possible actions will be displayed and can be executed by a single click. You can configure the list through `configBuilder` options. You can provide your own configBuilder by: ```typescript import { block, blockPlugin } from '@milkdown/plugin-block'; Editor .make() .use(block.configure(blockPlugin, { configBuilder: (ctx) => { return [/* your actions */]; } })) ``` You can find the [default config builder here](https://github.com/Saul-Mirone/milkdown/blob/main/packages/plugin-block/src/config.ts). # Hashtag for Heading Heading nodes now will display hashtag. And `delete` key now can downgrade a heading node. This feature is enabled by default. But you can disable it if you don't need it: ```typescript commonmark.configure(heading, { displayHashtag: false }); ``` # `useNodeCtx` in React and Vue is Up to Date Now In the previous version of Milkdown. The value returned by `useNodeCtx` will not be updated when prosemirror update views. So, users will need to write some code to keep the value up to date. Luckily, we make it up to date in v6.3. Which means you can just get the value of it and won't need to update it. ```typescript // before: const [filename, setFilename] = useState<string>(node.attrs['filename']); const updateFilename = (name: string) => { view.dispatch(updateNodeAttr('filename', name)); setFilename(name); } // after: const filename = node.attrs['filename']; const updateFilename = (name: string) => { view.dispatch(updateNodeAttr('filename', name)); } ``` # Tooltip now can be configured The tooltip plugin can now be configured by `items` options with a callback function. ```typescript export type TooltipOptions = { // ... items: ((ctx: Ctx) => Array<Item>; }; ``` You can view the default config here: https://github.com/Saul-Mirone/milkdown/blob/main/packages/plugin-tooltip/src/item.ts # Menu now can be also configured by a function In the previous version of @milkdown/plugin-menu. It'll accept an array configuration. Now you can also pass a function which returns an array configuration. ```typescript export type MenuOptions = { // ... config: Config | ((ctx: Ctx) => Config); }; ``` # Exceptions Now, all errors throwed by milkdown are defined in `@milkdown/exception` and the error will be an [MilkdownError](https://github.com/Saul-Mirone/milkdown/blob/main/packages/exception/src/error.ts). Which will have an error code on it. # Dependencies Upgrade * `tslib` -> 2.4.0 * `vite` -> 3.0.0 ...etc

    No due date
    8/8 issues closed
  • # New React and Vue API We provide more powerful renderer wrapper for both react and vue now. You can get following properties from the `useEditor` API. - `editor`: This editor should be passed into the component. - `loading`: Get the loading status of milkdown component, will return `ture` if it's still loading. - `getInstance`: You can get the milkdown `editor` instance through this function. - `getDom`: Get the root dom of milkdown component. ## React Example: ```typescript const { editor, getInstance, getDom, loading } = useEditor(/* creator */); useEffect(() => { if (!loading) { const editor = getInstance(); const rootDOM = getDom(); } }, [getInstance]) // when render <ReactEditor editor={editor} /> ``` ## Vue Example: ```typescript const { editor, getInstance, getDom, loading } = useEditor(/* creator */); effect(() => { if (!loading) { const editor = getInstance(); const rootDOM = getDom(); } }) // in your template <VueEditor editor={editor} /> ``` # Custom heading Id The `heading` node from `preset-commonmark` now can receive options to control the id generator. ```typescript import { commonmark, heading } from 'preset-commonmark'; commonmark.configure(heading, { getId: (node) => { const textContent = node.textContent; return yourTransformer(textContent); } }); ``` # Promise Upgrade Prosemirror now upgrade it self to typescript. So there maybe some breaking changes in type level. Because the type definition may have little difference from `@types/prosemirror-*`. The good news is that it's type check only and there will be no breaking changes in functionalities. # Async Composable API Now, for every composable plugin API, there will be an async version for it to add promise support. For example: ```typescript import { $prose, $proseAsync } from '@milkdown/utils'; import { Plugin } from '@milkdown/prose/state'; const myProsePlugin = $prose((ctx) => { return new Plugin({ //... }); }); const myAsyncProsePlugin = $proseAsync(async (ctx) => { await somePromise(); return new Plugin({ //... }); }); ```

    No due date
    10/10 issues closed
  • During thie iteration, I'll focus on documentation improvement.

    Due by May 28, 2022
    7/7 issues closed
  • Due by May 20, 2022
    6/6 issues closed