{"_id":"@benrbray/prosemirror-autocomplete","name":"@benrbray/prosemirror-autocomplete","dist-tags":{"next":"1.0.0-rc.0","latest":"1.0.0-rc.0"},"versions":{"1.0.0-rc.0":{"name":"@benrbray/prosemirror-autocomplete","version":"1.0.0-rc.0","author":{"name":"Benjamin R. Bray"},"license":"MIT","description":"Autocomplete suggestions for ProseMirror!","repository":{"type":"git","url":"git+https://github.com/benrbray/prosemirror-math.git"},"type":"module","exports":{".":"./dist/prosemirror-autocomplete.js"},"types":"dist/prosemirror-autocomplete.d.ts","scripts":{"build:check":"tsc --project tsconfig.build.json","build:lib":"vite --config vite.config.lib.ts build","build:site":"npm run build:check && vite build","build":"npm run build:check && npm run build:lib","dev":"vite","preview":"vite preview","prepare":"npm run build:lib"},"peerDependencies":{"prosemirror-example-setup":"^1.2.3","prosemirror-inputrules":"^1.5.1","prosemirror-model":"^1.25.4","prosemirror-schema-basic":"^1.2.4","prosemirror-state":"^1.4.4","prosemirror-view":"^1.41.3"},"devDependencies":{"@types/node":"^24.6.0","solid-js":"^1.9.9","typescript":"~5.9.3","vite":"^7.1.7","vite-plugin-dts":"^4.5.4","vite-plugin-solid":"^2.11.8","vite-tsconfig-paths":"^5.1.4"},"gitHead":"2892dba179f9c9afa30b57d4014e3c952521cb1e","_id":"@benrbray/prosemirror-autocomplete@1.0.0-rc.0","bugs":{"url":"https://github.com/benrbray/prosemirror-math/issues"},"homepage":"https://github.com/benrbray/prosemirror-math#readme","_nodeVersion":"24.11.0","_npmVersion":"11.6.1","dist":{"integrity":"sha512-h9fYzPD73GFbE1wxGExnxmNY6A0lte8zBk7CJIoJk0Uh7uJVebu76Db9KSaFJxAT0JMWHPpoyUksXTHA8cWyIA==","shasum":"4a800e1e91143b84eee7f79ea3fb5b093c03f37b","tarball":"https://registry.npmjs.org/@benrbray/prosemirror-autocomplete/-/prosemirror-autocomplete-1.0.0-rc.0.tgz","fileCount":13,"unpackedSize":239134,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDUdSgu+xGMm8IzyknIO5hD7uDyctNf9EBuEhK1bJ0aYQIgKDlUN1fSoJ1DD34m4+RM3yf5pPDgJoBfn9hRzVvTr80="}]},"_npmUser":{"name":"benrbray","email":"benrbray@gmail.com"},"directories":{},"maintainers":[{"name":"benrbray","email":"benrbray@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/prosemirror-autocomplete_1.0.0-rc.0_1762403570590_0.4499527373977503"},"_hasShrinkwrap":false}},"time":{"created":"2025-11-06T04:32:50.485Z","1.0.0-rc.0":"2025-11-06T04:32:50.797Z","modified":"2025-11-06T04:32:51.108Z"},"maintainers":[{"name":"benrbray","email":"benrbray@gmail.com"}],"description":"Autocomplete suggestions for ProseMirror!","homepage":"https://github.com/benrbray/prosemirror-math#readme","repository":{"type":"git","url":"git+https://github.com/benrbray/prosemirror-math.git"},"author":{"name":"Benjamin R. Bray"},"bugs":{"url":"https://github.com/benrbray/prosemirror-math/issues"},"license":"MIT","readme":"# `prosemirror-autocomplete`\n\n> This is a fork of [`curvenote/prosemirror-autocomplete`](https://www.npmjs.com/package/prosemirror-autocomplete).\n\n[![prosemirror-autocomplete on npm](https://img.shields.io/npm/v/prosemirror-autocomplete.svg)](https://www.npmjs.com/package/prosemirror-autocomplete)\n[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/curvenote/prosemirror-autocomplete/blob/master/LICENSE)\n![CI](https://github.com/curvenote/prosemirror-autocomplete/workflows/CI/badge.svg)\n[![demo](https://img.shields.io/badge/live-demo-blue)](https://curvenote.github.io/prosemirror-autocomplete/)\n\nA plugin for [ProseMirror](https://prosemirror.net/) that adds triggers for `#hashtags`, `@mentions`, `/menus`, and other more complex autocompletions. The `prosemirror-autocomplete` library can be used to create suggestions similar to Notion, Google Docs or Confluence; it is created and used by [Curvenote](https://curvenote.com). The library does not provide a user interface beyond the [demo code](./demo/index.ts).\n\n[![Autocomplete](./public/autocomplete.gif)](https://github.com/benrbray/prosemirror-autocomplete/)\n\n## Install\n\n```bash\nnpm install @benrbray/prosemirror-autocomplete\n```\n\nOr see the [live demo here](https://benrbray.com/prosemirror-autocomplete/)!\n\n## Overview\n\n`prosemirror-autocomplete` allows you to have fine-grained control over an autocomplete suggestion, similar to an IDE but simple enough for `@` or `#` mentions.\n\n```ts\nimport autocomplete, { Options } from 'prosemirror-autocomplete';\n\n// Create autocomplete with triggers and specified handers:\nconst options: Options = {\n  triggers: [\n    { name: 'hashtag', trigger: '#' },\n    { name: 'mention', trigger: '@' },\n  ],\n  onOpen: ({ view, range, trigger, type }) => handleOpen(),\n  onArrow: ({ view, kind }) => handleArrow(kind),\n  onFilter: ({ view, filter }) => handleFilter(),\n  onEnter: ({ view }) => handleSelect(),\n  onClose: ({ view }) => handleClose(),\n};\n\n// Alternatively, use a single reducer to handle all actions:\nconst options: Options = {\n  triggers: [\n    { name: 'hashtag', trigger: '#' },\n    { name: 'mention', trigger: '@' },\n  ],\n  reducer: (action) => dispatch(action),\n};\n\n// Then add these plugins to the EditorView as normal in ProseMirror\nconst view = new EditorView(editor, {\n  state: EditorState.create({\n    doc: DOMParser.fromSchema(schema).parse(content),\n    plugins: [...autocomplete(options), ...otherPlugins],\n  }),\n});\n```\n\nThe function `autocomplete` takes handlers or a single `reducer` and a list of `triggers`, it returns a two plugins:\n\n1. a decoration plugin that wraps the trigger and filter text (e.g. `[@][mention]`); and\n2. an `InputRule` plugin that has a series of triggers that are defined in the options.\n\nAll handlers take an `AutocompleteAction` as the first and only argument (same as the `reducer`).\n\n- `onOpen({ view, range, trigger, filter, type })` — when the autocomplete should be opened\n  - The `type` is the `Trigger` that cause this action\n- `onEnter({ view, range, filter })` — called on `Enter` or `Tab`\n- `onArrow({ view, kind })`\n  - `kind` is one or `ArrowUp`, `ArrowDown`, `ArrowLeft`, `ArrowRight`\n  - left/right are only called if `allArrowKeys = true` for the trigger\n- `onFilter({ view, filter })` — called when the user types, use this to filter the suggestions shown\n- `onClose({ view })` — called on escape, click away, or paste\n\nTo use a `reducer` instead of distinct handlers, use the option `reducer: (action: AutocompleteAction) => boolean`, which will be used in place of the above handler functions.\n\n## Defining a Trigger\n\nBy default, each Trigger has a `name`, and a `trigger`, which is a `string` or `RegExp`. For example, a simple trigger can just use a single string:\n\n```ts\nimport type { Trigger } from 'prosemirror-autocomplete';\n\nconst mentionTrigger: Trigger = { name: 'mention', trigger: '@' };\n```\n\nThis trigger gets wrapped in a regular expresion:\n\n```ts\nconst equivalentTrigger = /(?:^|\\s|\\n|[^\\d\\w])(@)$/;\n```\n\nThis does what you want most of the time, ensuring that you don't trigger when writing an email, or if you are writing something else. This is a bit more strict than you might want for a social plugin, which picks up hashtags or mentions anywhere you write them.\n\nIf you want this to come up all the time, try:\n\n```ts\nconst peskyMentionTrigger: Trigger = { name: 'mention', trigger: /(@)$/ };\n```\n\nProvide the trigger in the matched group and anything before in a non-capture group (`(?:)`), this will help you split the action into a `action.search` and an `action.trigger`.\n\n### Trigger Options\n\n- `name: string`: the trigger is passed in the action, you can use this to descriminate handler calls\n- `trigger: string | RegExp`: used to trigger an autocomplete suggestion - described above\n- `cancelOnSpace?: boolean` (default `false`) When `true`, a space anywhere in the filter cancels autocompletion, and the value of `cancelOnFirstSpace` is ignored.  Default is `false`.\n- `cancelOnFirstSpace?: boolean` (default `true`) When `true`, only a space in the initial position of the filter will cancel autocompletion, while spaces elsewhere are allowed.  Default is `true`.\n- `allArrowKeys?: boolean`: Use left/right arrow keys, default is false\n- `decorationAttrs?: DecorationAttrs`, passed to the `<span>` element directly through prosemirror\n\n## Defining a Reducer\n\nThe library does not provide a user interface beyond the [demo code](./demo/index.ts), you will have to do that when you get an action from the autocomplete plugin. You can _either_ use the handlers `onOpen`, `onArrow`, `onFilter`, `onEnter`, and `onClose` _or_ you can define a single reducer that will take over these responsibilities. Note: you cannot use handlers and a reducer. You can also access the original keyboard event on the action, as `action.event`. If the action was not created by a keyboard event, that property will not be available.\n\n```ts\nimport { AutocompleteAction, KEEP_OPEN } from 'prosemirror-autocomplete';\n\nexport function reducer(action: AutocompleteAction): boolean | KEEP_OPEN {\n  switch (action.kind) {\n    case ActionKind.open:\n      handleSearch(action.search);\n      placeSuggestion(true);\n      return true;\n    case ActionKind.up:\n      selectSuggestion(-1);\n      return true;\n    case ActionKind.down:\n      selectSuggestion(+1);\n      return true;\n    case ActionKind.filter:\n      filterSuggestions(action.filter);\n      return true;\n    case ActionKind.enter:\n      // This is on Enter or Tab\n      const { from, to } = action.range;\n      const tr = action.view.state.tr\n        .deleteRange(from, to) // This is the full selection\n        .insertText('You can define this!'); // This can be a node view, or something else!\n      action.view.dispatch(tr);\n      return true;\n      // To keep the suggestion open after selecting:\n      return KEEP_OPEN;\n    case ActionKind.close:\n      // Hit Escape or Click outside of the suggestion\n      closeSuggestion();\n      return true;\n    default:\n      return false;\n  }\n}\n```\n\nAn `AutocompleteAction` is passed to both the reducer and each handler has the following structure:\n\n```ts\nexport type AutocompleteAction = {\n  kind: ActionKind; // open, ArrowUp, ArrowDown, filter, enter, close\n  view: EditorView; // the view that the plugin came from\n  trigger: string; // This is the string that triggered the suggestion\n  filter?: string; // This is the search string\n  range: FromTo; // { from: number; to: number }, use to delete the selection\n  type: Trigger | null; // This is the trigger object passed in\n};\n```\n\n## Positioning & Styling\n\nYou can use something like [popper.js](https://popper.js.org/) to ensure that the autocomplete suggestions stay in the right place on scroll or simply an abolutely positioned `<div>` in some cases is sufficient.\n\n```ts\nfunction placeSuggestion(open: boolean) {\n  suggestion.style.display = open ? 'block' : 'none';\n  const rect = document.getElementsByClassName('autocomplete')[0].getBoundingClientRect();\n  suggestion.style.top = `${rect.top + rect.height}px`;\n  suggestion.style.left = `${rect.left}px`;\n}\n```\n\nIf you don't want to use the class provided (which is `'autocomplete'`) or have multiple on the page, then you can provide your own for any trigger:\n\n```ts\nconst options: Options = {\n  handler: reducer,\n  triggers: [\n    {\n      name: 'command',\n      trigger: '/',\n      decorationAttrs: { id: 'myId', class: 'myClass' },\n    },\n  ],\n};\n```\n\nThis will allow you to specify styling of the wrapped decoration (which is a `<span>`). This can be different based on the trigger type. For example, in the above example you can use a css rule to style the inline span, this is what is done in [the demo](./demo/index.html):\n\n```css\n/* The default decoration class. Override with `decorationAttrs: { class: 'myClass' }` */\n.autocomplete {\n  border: 1px solid #333;\n  border-radius: 2px 2px 0 0;\n  border-bottom-color: white;\n  padding: 2px 5px;\n  color: blue;\n}\n```\n\n## Triggering Autocomplete without an `InputRule`\n\nThere are certain times when you want to open up an autocomplete suggestion without the user typing. For example, you might have a command menu under `/` that shows all commands for users to discover other triggers, where they can discover `/emoji` and then the UI should move them into an emoji selection or `:`.\n\nThere are two actions:\n\n```ts\nimport { openAutocomplete, closeAutocomplete } from 'prosemirror-autocomplete';\n```\n\n- `openAutocomplete(view: EditorView, trigger: string, filter?: string)`\n- `closeAutocomplete(view: EditorView)`\n\nIf the above scenario, the user would trigger an input rule for the first action by typing `/emoji` and then the `onEnter` or `reducer` would call `closeAutocomplete(view)` and then `openAutocomplete(view, ':', 'rocket')`, optional 🚀 obviously!\n\n## Related Projects\n\nThere are a few other packages that offer similar functionality:\n\n- [prosemirror-suggestions](https://github.com/quartzy/prosemirror-suggestions)\n- [prosemirror-mentions](https://github.com/joelewis/prosemirror-mentions)\n- [prosemirror-suggest](https://github.com/remirror/remirror/tree/next/packages/prosemirror-suggest)\n- [@tiptap/suggestion](https://www.npmjs.com/package/@tiptap/suggestion)\n\n`prosemirror-suggestions` is similar in that it does not provide a UI, if you want a simple UI out of the box you can look at `prosemirror-mentions`. All three of these libraries trigger based on RegExp and leave the decorations in the state. This is similar to how Twitter works, but is undesirable in writing longer documents where you want to dismiss the suggestions with an escape and not see them again in that area.\n\nThis library, `prosemirror-autocomplete`, works based on an input rule and then a decoration around the chosen area meaning you can target the suggestion specifically and dismiss it with ease.","readmeFilename":"README.md","_rev":"1-46a0f2b7c05eea220638bf3c9b3c7772"}