442 kBSource Map (JSON)View Raw
2 "version": 3,
3 "sources": ["../src/builtins/attributes-extension.ts", "../src/extension/extension.ts", "../src/extension/base-class.ts", "../src/helpers.ts", "../src/extension/extension-decorator.ts", "../src/builtins/builtin-decorators.ts", "../src/builtins/builtin-preset.ts", "../src/builtins/commands-extension.ts", "../src/commands.ts", "../src/builtins/decorations-extension.ts", "../src/builtins/doc-changed-extension.ts", "../src/builtins/helpers-extension.ts", "../src/builtins/input-rules-extension.ts", "../src/builtins/keymap-extension.ts", "../src/builtins/node-views-extension.ts", "../src/builtins/paste-rules-extension.ts", "../src/builtins/plugins-extension.ts", "../src/builtins/schema-extension.ts", "../src/builtins/suggest-extension.ts", "../src/builtins/tags-extension.ts", "../src/builtins/upload-extension/file-placeholder-plugin.ts", "../src/builtins/upload-extension/file-upload.ts", "../src/builtins/upload-extension/upload-context.ts", "../src/builtins/upload-extension/upload-extension.ts", "../src/builtins/meta-extension.ts", "../src/framework/framework.ts", "../src/manager/remirror-manager.ts", "../src/manager/remirror-manager-helpers.ts", "../src/index.ts"],
4 "sourcesContent": ["import type { ClassName } from '@linaria/core/types/cx';\nimport { cx, object } from '@remirror/core-helpers';\nimport type { ProsemirrorAttributes } from '@remirror/core-types';\n\nimport { AnyExtension, PlainExtension } from '../extension';\n\n/**\n * This extension allows others extension to add the `createAttributes` method\n * for adding attributes to the prosemirror dom element.\n *\n * @remarks\n *\n * Use this to include all the dynamically generated attributes provided by each\n * extension. High priority extensions have preference over the lower priority\n * extensions.\n *\n * @category Builtin Extension\n */\nexport class AttributesExtension extends PlainExtension {\n get name() {\n return 'attributes' as const;\n }\n\n private attributeList: ProsemirrorAttributes[] = [];\n private attributeObject: ProsemirrorAttributes = object();\n\n /**\n * Create the attributes object on initialization.\n *\n * @internal\n */\n onCreate(): void {\n this.transformAttributes();\n this.store.setExtensionStore('updateAttributes', this.updateAttributes);\n }\n\n private readonly updateAttributes = (triggerUpdate = true) => {\n this.transformAttributes();\n\n if (triggerUpdate) {\n this.store.commands.forceUpdate('attributes');\n }\n };\n\n private transformAttributes() {\n this.attributeObject = object();\n\n // Exit early when the manager excludes these settings.\n if (this.store.managerSettings.exclude?.attributes) {\n this.store.setStoreKey('attributes', this.attributeObject);\n return;\n }\n\n // Reset this attributes\n this.attributeList = [];\n\n for (const extension of this.store.extensions) {\n if (extension.options.exclude?.attributes) {\n continue;\n }\n\n const createdAttributes = extension.createAttributes?.();\n const attributes = {\n ...createdAttributes,\n class: cx(...(extension.classNames ?? []), createdAttributes?.class),\n };\n\n // Inserted at the start of the list so that when combining the full\n // attribute object the higher priority extension attributes are\n // preferred to the lower priority since they merge with the object\n // later.\n this.attributeList.unshift(attributes);\n }\n\n for (const attributes of this.attributeList) {\n this.attributeObject = {\n ...this.attributeObject,\n ...attributes,\n class: cx(this.attributeObject.class, attributes.class),\n };\n }\n\n this.store.setStoreKey('attributes', this.attributeObject);\n }\n}\n\ndeclare global {\n namespace Remirror {\n interface ManagerStore<Extension extends AnyExtension> {\n /**\n * The attributes to be added to the prosemirror editor.\n */\n attributes: ProsemirrorAttributes;\n }\n\n interface ExtensionStore {\n /**\n * Triggers a recalculation of the `view.dom` attributes for each\n * extension and notifies the parent UI once done.\n *\n * This will also dispatch an update to the state automatically. However\n * you can disable this by setting `triggerUpdate` to `false`.\n *\n * By not triggering an update the new value may not be capture by the view layer, e.g. `React`.\n *\n * @param triggerUpdate - defaults to true\n */\n updateAttributes: (triggerUpdate?: boolean) => void;\n }\n\n interface ExcludeOptions {\n /**\n * Whether to use the attributes provided by this extension\n *\n * @defaultValue undefined\n */\n attributes?: boolean;\n }\n\n interface BaseExtension {\n /**\n * A list of class names to add to the main editor element.\n */\n classNames?: ClassName[];\n\n /**\n * Allows the extension to modify the attributes for the Prosemirror editor\n * dom element.\n *\n * @remarks\n *\n * Sometimes an extension will need to make a change to the attributes of the\n * editor itself. For example a placeholder may need to do some work to make\n * the editor more accessible by setting the `aria-placeholder` value to match\n * the value of the placeholder.\n *\n * @alpha\n */\n createAttributes?(): ProsemirrorAttributes;\n }\n\n interface AllExtensions {\n attributes: AttributesExtension;\n }\n }\n}\n", "/* eslint-disable @typescript-eslint/member-ordering */\n\nimport {\n __INTERNAL_REMIRROR_IDENTIFIER_KEY__,\n ErrorConstant,\n ExtensionPriority,\n RemirrorIdentifier,\n} from '@remirror/core-constants';\nimport { assertGet, freeze, invariant, pascalCase, uniqueBy } from '@remirror/core-helpers';\nimport type {\n ApplySchemaAttributes,\n Dispose,\n EditorState,\n EditorView,\n EmptyShape,\n MarkExtensionSpec,\n MarkSpecOverride,\n MarkType,\n NodeExtensionSpec,\n NodeSpecOverride,\n NodeType,\n Replace,\n Shape,\n ValidOptions,\n} from '@remirror/core-types';\nimport { isIdentifierOfType, isRemirrorType } from '@remirror/core-utils';\n\nimport type {\n AppendLifecycleProps,\n ApplyStateLifecycleProps,\n BaseExtensionOptions,\n ExtensionCommandReturn,\n ExtensionHelperReturn,\n StateUpdateLifecycleProps,\n} from '../types';\nimport {\n AnyBaseClassOverrides,\n BaseClass,\n BaseClassConstructor,\n ConstructorProps,\n DefaultOptions,\n} from './base-class';\n\n/**\n * Auto infers the parameter for the constructor. If there is a required static\n * option then the TypeScript compiler will error if nothing is passed in.\n */\nexport type ExtensionConstructorProps<Options extends ValidOptions> = ConstructorProps<\n Options,\n BaseExtensionOptions\n>;\n\n/**\n * Extensions are fundamental to the way that Remirror works by grouping\n * together the functionality and handling the management of similar concerns.\n *\n * @remarks\n *\n * Extension can adjust editor functionality in any way. Here are some\n * examples.\n *\n * - How the editor displays certain content, i.e. **bold**, _italic_,\n * **underline**.\n * - Which commands should be made available e.g. `commands.toggleBold()` to\n * toggle the weight of the selected text.\n * - Check if a command is currently enabled (i.e a successful dry run) e.g.\n * `commands.toggleBold.isEnabled()`.\n * - Register Prosemirror `Plugin`s, `keymap`s, `InputRule`s `PasteRule`s,\n * `Suggestions`, and custom `nodeViews` which affect the behavior of the\n * editor.\n *\n * There are three types of `Extension`.\n *\n * - `NodeExtension` - For creating Prosemirror nodes in the editor. See\n * {@link NodeExtension}\n * - `MarkExtension` - For creating Prosemirror marks in the editor. See\n * {@link MarkExtension}\n * - `PlainExtension` - For behavior which doesn't map to a `ProsemirrorNode` or\n * `Mark` and as a result doesn't directly affect the Prosemirror `Schema` or\n * content. See {@link PlainExtension}.\n *\n * This `Extension` is an abstract class that should not be used directly but\n * rather extended to add the intended functionality.\n *\n * ```ts\n * import { PlainExtension, Static } from 'remirror';\n *\n * interface AwesomeExtensionOptions {\n * isAwesome?: Static<boolean>;\n * id?: string;\n * }\n *\n * class AwesomeExtension extends PlainExtension<AwesomeExtensionOptions> {\n * static defaultOptions: DefaultExtensionOptions<AwesomeExtensionOptions> = {\n * isAwesome: true,\n * id: '',\n * }\n *\n * get name() {\n * return 'awesome' as const;\n * }\n * }\n * ```\n */\nabstract class Extension<Options extends ValidOptions = EmptyShape> extends BaseClass<\n Options,\n BaseExtensionOptions\n> {\n /**\n * The default priority for this family of extensions.\n */\n static readonly defaultPriority: ExtensionPriority = ExtensionPriority.Default;\n\n /**\n * Allows for the `RemirrorManager` or `Preset`'s to override the priority of\n * an extension.\n */\n private priorityOverride?: ExtensionPriority;\n\n /**\n * The priority level for this instance of the extension. A higher value\n * corresponds to a higher priority extension\n */\n get priority(): ExtensionPriority {\n return this.priorityOverride ?? this.options.priority ?? this.constructor.defaultPriority;\n }\n\n /**\n * The name that the constructor should have, which doesn't get mangled in\n * production.\n */\n get constructorName(): string {\n return `${pascalCase(this.name)}Extension`;\n }\n\n /**\n * The store is a shared object that's internal to each extension. It includes\n * often used items like the `view` and `schema` that are added by the\n * extension manager and also the lifecycle extension methods.\n *\n * **NOTE** - The store is not available until the manager has been created\n * and received the extension. As a result trying to access the store during\n * `init` and `constructor` will result in a runtime error.\n *\n * Some properties of the store are available at different phases. You should\n * check the inline documentation to know when a certain property is useable\n * in your extension.\n */\n protected get store(): Remirror.ExtensionStore {\n invariant(this._store, {\n code: ErrorConstant.MANAGER_PHASE_ERROR,\n message: `An error occurred while attempting to access the 'extension.store' when the Manager has not yet set created the lifecycle methods.`,\n });\n\n return freeze(this._store, { requireKeys: true });\n }\n\n /**\n * The list of extensions added to the editor by this `Preset`.\n */\n get extensions(): Array<this['~E']> {\n return this._extensions;\n }\n /**\n * Private list of extension stored within this [[`Preset`]].\n */\n private _extensions: Array<this['~E']>;\n\n /**\n * An extension mapping of the extensions and their constructors.\n */\n private readonly extensionMap: Map<this['~E']['constructor'], this['~E']>;\n\n /**\n * This store is can be modified by the extension manager with and lifecycle\n * extension methods.\n *\n * Different properties are added at different times so it's important to\n * check the documentation for each property to know what phase is being\n * added.\n */\n private _store?: Remirror.ExtensionStore;\n\n constructor(...args: ExtensionConstructorProps<Options>) {\n super(defaultOptions, ...args);\n\n // Create the extension list.\n this._extensions = uniqueBy(\n this.createExtensions() as any,\n // Ensure that all the provided extensions are unique.\n (extension) => extension.constructor,\n );\n\n this.extensionMap = new Map<this['~E']['constructor'], this['~E']>();\n\n // Create the extension map for retrieving extensions from the `Preset`\n for (const extension of this._extensions) {\n this.extensionMap.set(extension.constructor, extension);\n }\n }\n\n /**\n * When there are duplicate extensions used within the editor the extension\n * manager will call this method and make sure all extension holders are using\n * the same instance of the `ExtensionConstructor`.\n *\n * @internal\n */\n replaceChildExtension(constructor: AnyExtensionConstructor, extension: this['~E']): void {\n if (!this.extensionMap.has(constructor)) {\n return;\n }\n\n this.extensionMap.set(constructor, extension);\n this._extensions = this.extensions.map((currentExtension) =>\n extension.constructor === constructor ? extension : currentExtension,\n );\n }\n\n /**\n * Not for usage. This is purely for types to make it easier to infer\n * available sub extension types.\n *\n * @internal\n */\n ['~E']: ReturnType<this['createExtensions']>[number] = {} as ReturnType<\n this['createExtensions']\n >[number];\n\n /**\n * Create the extensions which will be consumed by the preset. Override this\n * if you would like to make your extension a parent to other (holder)\n * extensions which don't make sense existing outside of the context of this\n * extension.\n *\n * @remarks\n *\n * Since this method is called in the constructor it should always be created\n * as an instance method and not a property. Properties aren't available for\n * the call to the parent class.\n *\n * ```ts\n * class HolderExtension extends PlainExtension {\n * get name() {\n * return 'holder'\n * }\n *\n * // GOOD \u2705\n * createExtensions() {\n * return [];\n * }\n *\n * // BAD \u274C\n * createExtensions = () => {\n * return [];\n * }\n * }\n * ```\n */\n createExtensions(): AnyExtension[] {\n return [];\n }\n\n /**\n * Get an extension from this holder extension by providing the desired\n * `Constructor`.\n *\n * @param Constructor - the extension constructor to find in the editor.\n *\n * @remarks\n *\n * This method will throw an error if the constructor doesn't exist within the\n * extension created by this extension.\n *\n * It can be used to forward options and attach handlers to the children\n * extensions. It is the spiritual replacement of the `Preset` extension.\n *\n * ```ts\n * import { PlainExtension, OnSetOptionsProps } from 'remirror';\n *\n * interface ParentOptions { weight?: string }\n *\n * class ParentExtension extends PlainExtension<ParentOptions> {\n * get name() {\n * return 'parent' as const;\n * }\n *\n * createExtensions() {\n * return [new BoldExtension()]\n * }\n *\n * onSetOptions(options: OnSetOptionsProps<ParentOptions>): void {\n * if (options.changes.weight.changed) {\n * // Update the value of the provided extension.\n * this.getExtension(BoldExtension).setOption({ weight: options.changes.weight.value });\n * }\n * }\n * }\n * ```\n */\n getExtension<Type extends this['~E']['constructor']>(Constructor: Type): InstanceType<Type> {\n const extension = this.extensionMap.get(Constructor);\n\n // Throws an error if attempting to get an extension which is not available\n // in this preset.\n invariant(extension, {\n code: ErrorConstant.INVALID_GET_EXTENSION,\n message: `'${Constructor.name}' does not exist within the preset: '${this.name}'`,\n });\n\n return extension as InstanceType<Type>;\n }\n\n /**\n * Check if the type of this extension's constructor matches the type of the\n * provided constructor.\n */\n isOfType<Type extends AnyExtensionConstructor>(Constructor: Type): this is InstanceType<Type> {\n return this.constructor === (Constructor as unknown);\n }\n\n /**\n * Pass a reference to the globally shared `ExtensionStore` for this\n * extension.\n *\n * @remarks\n *\n * The extension store allows extensions to access important variables without\n * complicating their creator methods.\n *\n * ```ts\n * import { PlainExtension } from 'remirror';\n *\n * class Awesome extends PlainExtension {\n * customMethod() {\n * if (this.store.view.hasFocus()) {\n * log('dance dance dance');\n * }\n * }\n * }\n * ```\n *\n * This should only be called by the `RemirrorManager`.\n *\n * @internal\n * @nonVirtual\n */\n setStore(store: Remirror.ExtensionStore): void {\n if (this._store) {\n return;\n }\n\n this._store = store;\n }\n\n /**\n * Clone an extension.\n */\n clone(...args: ExtensionConstructorProps<Options>): Extension<Options> {\n return new this.constructor(...args);\n }\n\n /**\n * Set the priority override for this extension. This is used in the\n * `RemirrorManager` in order to override the priority of an extension.\n *\n * If you set the first parameter to `undefined` it will remove the priority\n * override.\n *\n * @internal\n */\n setPriority(priority: undefined | ExtensionPriority): void {\n this.priorityOverride = priority;\n }\n\n /**\n * This handler is called when the `RemirrorManager` is first created.\n *\n * @remarks\n *\n * Since it is called as soon as the manager is some methods may not be\n * available in the extension store. When accessing methods on `this.store` be\n * shore to check when they become available in the lifecycle.\n *\n * You can return a `Dispose` function which will automatically be called when\n * the extension is destroyed.\n *\n * This handler is called before the `onView` handler.\n *\n * @category Lifecycle Methods\n */\n onCreate?(): Dispose | void;\n\n /**\n * This event happens when the view is first received from the view layer\n * (e.g. React).\n *\n * Return a dispose function which will be called when the extension is\n * destroyed.\n *\n * This handler is called after the `onCreate` handler.\n *\n * @category Lifecycle Methods\n */\n onView?(view: EditorView): Dispose | void;\n\n /**\n * This can be used by the `Extension` to append a transaction to the latest\n * update.\n *\n * This is shorthand for the `ProsemirrorPlugin.spec.appendTransaction`.\n *\n * @category Lifecycle Methods\n */\n onAppendTransaction?(props: AppendLifecycleProps): void;\n\n /**\n * This is called when the prosemirror editor state is first attached to the\n * editor. It can be useful for doing some preparation work.\n *\n * This is a shorthand for creating a plugin and adding the\n * [[`Plugin.spec.state.init`]].\n *\n * @category Lifecycle Methods\n */\n onInitState?(state: EditorState): void;\n\n /**\n * This is called when the state is being applied to the editor. This can be\n * used as a shorthand for the [[`Plugin.spec.state.apply`]] method.\n *\n * For example, when using [[`createDecorations`]] you can respond to editor\n * updates within this callback.\n *\n * @category Lifecycle Methods\n */\n onApplyState?(props: ApplyStateLifecycleProps): void;\n\n /**\n * This handler is called after a transaction successfully updates the editor\n * state. It is called asynchronously after the [[`onApplyState`]] hook has\n * been run run.\n *\n * @category Lifecycle Methods\n */\n onStateUpdate?(props: StateUpdateLifecycleProps): void;\n\n /**\n * Called when the extension is being destroyed.\n *\n * @category Lifecycle Methods\n */\n onDestroy?(): void;\n}\n\n/**\n * Declaration merging since the constructor property can't be defined on the\n * actual class.\n */\ninterface Extension<Options extends ValidOptions = EmptyShape> extends Remirror.BaseExtension {\n /**\n * The type of the constructor for the extension.\n */\n constructor: ExtensionConstructor<Options>;\n\n /**\n * An extension can declare the extensions it requires.\n *\n * @remarks\n *\n * When creating the extension manager the extension will be checked for\n * required extension as well as a quick check to see if the required\n * extension is already included. If not present a descriptive error will be\n * thrown.\n */\n requiredExtensions?: AnyExtensionConstructor[];\n}\n\n/**\n * Get the expected type signature for the `defaultOptions`. Requires that every\n * optional setting key (except for keys which are defined on the\n * `BaseExtensionOptions`) has a value assigned.\n */\nexport type DefaultExtensionOptions<Options extends ValidOptions> = DefaultOptions<\n Options,\n BaseExtensionOptions\n>;\n\n/**\n * Create a plain extension which doesn't directly map to Prosemirror nodes or\n * marks.\n *\n * Plain extensions are a great way to add custom behavior to your editor.\n */\nexport abstract class PlainExtension<\n Options extends ValidOptions = EmptyShape,\n> extends Extension<Options> {\n /** @internal */\n static get [__INTERNAL_REMIRROR_IDENTIFIER_KEY__](): RemirrorIdentifier.PlainExtensionConstructor {\n return RemirrorIdentifier.PlainExtensionConstructor;\n }\n\n /** @internal */\n get [__INTERNAL_REMIRROR_IDENTIFIER_KEY__](): RemirrorIdentifier.PlainExtension {\n return RemirrorIdentifier.PlainExtension;\n }\n}\n\n/**\n * A mark extension is based on the `Mark` concept from from within prosemirror\n * {@link https://prosemirror.net/docs/guide/#schema.marks}\n *\n * @remarks\n *\n * Marks are used to add extra styling or other information to inline content.\n * Mark types are objects much like node types, used to tag mark objects and\n * provide additional information about them.\n */\nexport abstract class MarkExtension<\n Options extends ValidOptions = EmptyShape,\n> extends Extension<Options> {\n /** @internal */\n static get [__INTERNAL_REMIRROR_IDENTIFIER_KEY__](): RemirrorIdentifier.MarkExtensionConstructor {\n return RemirrorIdentifier.MarkExtensionConstructor;\n }\n\n /**\n * Whether to disable extra attributes for this extension.\n */\n static readonly disableExtraAttributes: boolean = false;\n\n /** @internal */\n get [__INTERNAL_REMIRROR_IDENTIFIER_KEY__](): RemirrorIdentifier.MarkExtension {\n return RemirrorIdentifier.MarkExtension;\n }\n\n /**\n * Provides access to the mark type from the schema.\n *\n * @remarks\n *\n * The type is available as soon as the schema is created by the\n * `SchemaExtension` which has the priority `Highest`. It should be safe to\n * access in any of the lifecycle methods.\n */\n get type(): MarkType {\n return assertGet(this.store.schema.marks, this.name);\n }\n\n constructor(...args: ExtensionConstructorProps<Options>) {\n super(...args);\n }\n\n /**\n * Provide a method for creating the schema. This is required in order to\n * create a `MarkExtension`.\n *\n * @remarks\n *\n * The main difference between the return value of this method and Prosemirror\n * `MarkSpec` is that that the `toDOM` method doesn't allow dom manipulation.\n * You can only return an array or string.\n *\n * For more advanced requirements, it may be possible to create a `nodeView`\n * to manage the dom interactions.\n */\n abstract createMarkSpec(\n extra: ApplySchemaAttributes,\n override: MarkSpecOverride,\n ): MarkExtensionSpec;\n}\n\nexport interface MarkExtension<Options extends ValidOptions = EmptyShape>\n extends Extension<Options>,\n Remirror.MarkExtension {}\n\n/**\n * Defines the abstract class for extensions which can place nodes into the\n * prosemirror state.\n *\n * @remarks\n *\n * For more information see {@link https://prosemirror.net/docs/ref/#model.Node}\n */\nexport abstract class NodeExtension<\n Options extends ValidOptions = EmptyShape,\n> extends Extension<Options> {\n static get [__INTERNAL_REMIRROR_IDENTIFIER_KEY__](): RemirrorIdentifier.NodeExtensionConstructor {\n return RemirrorIdentifier.NodeExtensionConstructor;\n }\n\n /**\n * Whether to disable extra attributes for this extension.\n */\n static readonly disableExtraAttributes: boolean = false;\n\n /** @internal */\n get [__INTERNAL_REMIRROR_IDENTIFIER_KEY__](): RemirrorIdentifier.NodeExtension {\n return RemirrorIdentifier.NodeExtension as const;\n }\n\n /**\n * Provides access to the node type from the schema.\n */\n get type(): NodeType {\n return assertGet(this.store.schema.nodes, this.name);\n }\n\n constructor(...args: ExtensionConstructorProps<Options>) {\n super(...args);\n }\n\n /**\n * Provide a method for creating the schema. This is required in order to\n * create a `NodeExtension`.\n *\n * @remarks\n *\n * A node schema defines the behavior of the content within the editor. This\n * is very tied to the prosemirror implementation and the best place to learn\n * more about it is in the\n * {@link https://prosemirror.net/docs/guide/#schema docs}.\n *\n * @params hole - a method that is meant to indicate where extra attributes\n * should be placed (if they exist).\n *\n * The `hole` is a function that augments the passed object adding a special\n * `secret` key which is used to insert the extra attributes setter.\n *\n * ```ts\n * import { NodeExtension, SpecHole } from 'remirror';\n *\n * class AwesomeExtension extends NodeExtension {\n * get name() { return 'awesome' as const'; }\n *\n * createNodeSpec() {\n * return {\n * toDOM: (node) => {\n * return ['p', hole(), 0]\n * }\n * }\n * }\n * }\n * ```\n *\n * The above example will have the `hole()` method call replaced with the\n * extra attributes.\n */\n abstract createNodeSpec(\n extra: ApplySchemaAttributes,\n override: NodeSpecOverride,\n ): NodeExtensionSpec;\n}\n\nexport interface NodeExtension<Options extends ValidOptions = EmptyShape>\n extends Extension<Options>,\n Remirror.NodeExtension {}\n\n/**\n * The type which is applicable to any extension instance.\n *\n * **NOTE** `& object` forces VSCode to use the name `AnyExtension` rather than\n * print out `Replace<Extension<Shape>, Remirror.AnyExtensionOverrides>`\n */\nexport type AnyExtension = Replace<Extension<Shape>, Remirror.AnyExtensionOverrides> & object;\n\n/**\n * The type which is applicable to any extension instance.\n */\nexport type AnyExtensionConstructor = Replace<\n ExtensionConstructor<any>,\n // eslint-disable-next-line @typescript-eslint/prefer-function-type\n { new (...args: any[]): AnyExtension }\n>;\n\n/**\n * The type for any potential PlainExtension.\n */\nexport type AnyPlainExtension = Replace<PlainExtension<Shape>, Remirror.AnyExtensionOverrides> &\n object;\n\n/**\n * The type for any potential NodeExtension.\n */\nexport type AnyNodeExtension = Replace<NodeExtension<Shape>, Remirror.AnyExtensionOverrides> &\n object;\n\n/**\n * The type for any potential MarkExtension.\n */\nexport type AnyMarkExtension = Replace<MarkExtension<Shape>, Remirror.AnyExtensionOverrides> &\n object;\n\n/**\n * These are the default options merged into every extension. They can be\n * overridden.\n */\nconst defaultOptions: BaseExtensionOptions = {\n priority: undefined,\n extraAttributes: {},\n disableExtraAttributes: false,\n exclude: {},\n} as BaseExtensionOptions;\n\n/**\n * Mutate the default extension options.\n *\n * @remarks\n *\n * This is a dangerous method since it allows you to mutate the received object.\n * Don't use it unless you absolutely have to.\n *\n * A potential use case is for adding a new default option to all extensions. It\n * shows an example of how to accomplish this in a typesafe way.\n *\n * ```ts\n * import { mutateDefaultExtensionOptions } from 'remirror';\n *\n * mutateDefaultExtensionOptions((settings) => {\n * // Set the default value of all extensions to have a property `customSetting` with value `false`.\n * settings.customSetting = false;\n * })\n *\n * declare global {\n * namespace Remirror {\n * interface BaseExtensionOptions {\n * customSetting?: boolean;\n * }\n * }\n * }\n *```\n *\n * The mutation must happen before any extension have been instantiated.\n */\nexport function mutateDefaultExtensionOptions(\n mutatorMethod: (defaultOptions: BaseExtensionOptions) => void,\n): void {\n mutatorMethod(defaultOptions);\n}\n\n/**\n * Determines if the passed value is an extension.\n *\n * @param value - the value to test\n */\nexport function isExtension<Type extends AnyExtension = AnyExtension>(\n value: unknown,\n): value is Type {\n return (\n isRemirrorType(value) &&\n isIdentifierOfType(value, [\n RemirrorIdentifier.PlainExtension,\n RemirrorIdentifier.MarkExtension,\n RemirrorIdentifier.NodeExtension,\n ])\n );\n}\n\n/**\n * Determines if the passed value is an extension constructor.\n *\n * @param value - the value to test\n */\nexport function isExtensionConstructor<\n Type extends AnyExtensionConstructor = AnyExtensionConstructor,\n>(value: unknown): value is Type {\n return (\n isRemirrorType(value) &&\n isIdentifierOfType(value, [\n RemirrorIdentifier.PlainExtensionConstructor,\n RemirrorIdentifier.MarkExtensionConstructor,\n RemirrorIdentifier.NodeExtensionConstructor,\n ])\n );\n}\n\n/**\n * Checks whether the provided value is a plain extension.\n *\n * @param value - the extension to check\n */\nexport function isPlainExtension<Type extends AnyPlainExtension = AnyPlainExtension>(\n value: unknown,\n): value is Type {\n return isRemirrorType(value) && isIdentifierOfType(value, RemirrorIdentifier.PlainExtension);\n}\n\n/**\n * Determines if the passed in extension is a node extension. Useful as a type\n * guard where a particular type of extension is needed.\n *\n * @param value - the extension to check\n */\nexport function isNodeExtension<Type extends AnyNodeExtension = AnyNodeExtension>(\n value: unknown,\n): value is Type {\n return isRemirrorType(value) && isIdentifierOfType(value, RemirrorIdentifier.NodeExtension);\n}\n\n/**\n * Determines if the passed in extension is a mark extension. Useful as a type\n * guard where a particular type of extension is needed.\n *\n * @param value - the extension to check\n */\nexport function isMarkExtension<Type extends AnyMarkExtension = AnyMarkExtension>(\n value: unknown,\n): value is Type {\n return isRemirrorType(value) && isIdentifierOfType(value, RemirrorIdentifier.MarkExtension);\n}\n\nexport interface ExtensionConstructor<Options extends ValidOptions = EmptyShape>\n extends BaseClassConstructor<Options, BaseExtensionOptions>,\n Partial<Remirror.StaticExtensionOptions> {\n new (...args: ExtensionConstructorProps<Options>): Extension<Options>;\n\n /**\n * The default priority level for all instance of this extension.\n *\n * @defaultValue ExtensionPriority.Default\n */\n readonly defaultPriority: ExtensionPriority;\n}\n\nexport type AnyManagerStore = Remirror.ManagerStore<any>;\nexport type ManagerStoreKeys = keyof Remirror.ManagerStore<any>;\n\ndeclare global {\n /**\n * This namespace is global and you can use declaration merging to extend and\n * create new types used by the `remirror` project.\n *\n * @remarks\n *\n * The following would add `MyCustomType` to the `Remirror` namespace. Please\n * note that this can only be used for types and interfaces.\n *\n * ```ts\n * declare global {\n * namespace Remirror {\n * type MyCustomType = 'look-at-me';\n * }\n * }\n * ```\n */\n namespace Remirror {\n /**\n * This interface stores all the currently installed extensions. As a result\n * it can be used to set the default loaded extensions to include all\n * available within `node_modules`. By extending this extension in the\n * global `Remirror` namespace the key is ignored but the value is used to\n * form the union type in the `chain`, `commands`, `helpers` properties on\n * the `Remirror.ExtensionStore` interface.\n *\n * This is useful for extensions being able to reuse the work of other\n * extension.\n */\n interface AllExtensions {}\n\n /**\n * This is the global interface for adding extra methods and properties to\n * all [[`Extension`]]s using declaration merging.\n *\n * @remarks\n *\n * The following will add `newOption` to the expected options. This is the\n * way that extensions which add new functionality to the editor can request\n * configuration options.\n *\n * ```ts\n * declare global {\n * namespace Remirror {\n * interface ExtensionFactoryProps {\n * newOption?: string;\n * }\n * }\n * }\n * ```\n */\n interface BaseExtension {}\n\n interface NodeExtension {}\n interface MarkExtension {}\n\n /**\n * An override to for the `AnyExtension` type. If you're extension adds a\n * new property to the `Extension` that is deeply nested or very complex it\n * can break the `AnyExtension` implementation from being compatible with\n * all valid extensions.\n *\n * The keys you provide on this override replace the default `AnyExtension`\n * types include unsafe properties that need to be simplified.\n *\n * An example is the `constructor` property which makes it impossible to\n * find a common interface between extensions with different settings and\n * properties. By setting the `constructor` to a much simpler override all\n * `Extension`'s are now assignable to the `AnyExtension type again.`\n */\n interface AnyExtensionOverrides extends AnyBaseClassOverrides {\n constructor: AnyExtensionConstructor;\n ['~C']: ExtensionCommandReturn;\n ['~H']: ExtensionHelperReturn;\n ['~E']: AnyExtension;\n }\n }\n}\n\n/* eslint-enable @typescript-eslint/member-ordering */\n\n// Make the abstract extension available but only as a type.\nexport type { Extension };\n", "/* eslint-disable @typescript-eslint/member-ordering */\n\nimport {\n __INTERNAL_REMIRROR_IDENTIFIER_KEY__,\n ErrorConstant,\n ExtensionPriority,\n RemirrorIdentifier,\n} from '@remirror/core-constants';\nimport {\n deepMerge,\n invariant,\n isEmptyArray,\n isFunction,\n keys,\n noop,\n object,\n omit,\n sort,\n} from '@remirror/core-helpers';\nimport type {\n AnyFunction,\n Dispose,\n EmptyShape,\n GetAcceptUndefined,\n GetConstructorProps,\n GetCustomHandler,\n GetFixed,\n GetFixedDynamic,\n GetHandler,\n GetMappedHandler,\n GetPartialDynamic,\n GetStatic,\n IfNoRequiredProperties,\n LiteralUnion,\n MakeUndefined,\n Primitive,\n RemoveAnnotations,\n Replace,\n Shape,\n StringKey,\n UndefinedFlipPartialAndRequired,\n ValidOptions,\n} from '@remirror/core-types';\nimport { environment } from '@remirror/core-utils';\n\nimport { getChangedOptions } from '../helpers';\nimport type { OnSetOptionsProps } from '../types';\n\nconst IGNORE = '__IGNORE__';\nconst GENERAL_OPTIONS = '__ALL__' as const;\n\nexport abstract class BaseClass<\n Options extends ValidOptions = EmptyShape,\n DefaultStaticOptions extends Shape = EmptyShape,\n> {\n /**\n * The default options for this extension.\n *\n * TODO see if this can be cast to something other than any and allow\n * composition.\n */\n static readonly defaultOptions: any = {};\n\n /**\n * The static keys for this class.\n */\n static readonly staticKeys: string[] = [];\n\n /**\n * The event handler keys.\n */\n static readonly handlerKeys: string[] = [];\n\n /**\n * Customize the way the handler should behave.\n */\n static handlerKeyOptions: Partial<\n Record<string, HandlerKeyOptions> & { [GENERAL_OPTIONS]?: HandlerKeyOptions }\n > = {};\n\n /**\n * The custom keys.\n */\n static readonly customHandlerKeys: string[] = [];\n\n /**\n * This is not for external use. It is purely here for TypeScript inference of\n * the generic `Options` type parameter.\n *\n * @internal\n */\n ['~O']: Options & DefaultStaticOptions = {} as Options & DefaultStaticOptions;\n\n /**\n * This identifies this as a `Remirror` object. .\n * @internal\n */\n abstract readonly [__INTERNAL_REMIRROR_IDENTIFIER_KEY__]: RemirrorIdentifier;\n\n /**\n * The unique name of this extension.\n *\n * @remarks\n *\n * Every extension **must** have a name. The name should have a distinct type\n * to allow for better type inference for end users. By convention the name\n * should be `camelCased` and unique within your editor instance.\n *\n * ```ts\n * class SimpleExtension extends Extension {\n * get name() {\n * return 'simple' as const;\n * }\n * }\n * ```\n */\n abstract get name(): string;\n\n /**\n * The options for this extension.\n *\n * @remarks\n *\n * Options are composed of Static, Dynamic, Handlers and ObjectHandlers.\n *\n * - `Static` - set at instantiation by the constructor.\n * - `Dynamic` - optionally set at instantiation by the constructor and also\n * set during the runtime.\n * - `Handlers` - can only be set during the runtime.\n * - `ObjectHandlers` - Can only be set during the runtime of the extension.\n */\n get options(): RemoveAnnotations<GetFixed<Options> & DefaultStaticOptions> {\n return this._options;\n }\n\n /**\n * Get the dynamic keys for this extension.\n */\n get dynamicKeys(): string[] {\n return this._dynamicKeys;\n }\n\n /**\n * The options that this instance was created with, merged with all the\n * default options.\n */\n get initialOptions(): RemoveAnnotations<GetFixed<Options> & DefaultStaticOptions> {\n return this._initialOptions;\n }\n\n /**\n * The initial options at creation (used to reset).\n */\n private readonly _initialOptions: RemoveAnnotations<GetFixed<Options> & DefaultStaticOptions>;\n\n /**\n * All the dynamic keys supported by this extension.\n */\n private readonly _dynamicKeys: string[];\n\n /**\n * Private instance of the extension options.\n */\n private _options: RemoveAnnotations<GetFixed<Options> & DefaultStaticOptions>;\n\n /**\n * The mapped function handlers.\n */\n private _mappedHandlers: GetMappedHandler<Options>;\n\n constructor(\n defaultOptions: DefaultStaticOptions,\n ...[options]: ConstructorProps<Options, DefaultStaticOptions>\n ) {\n this._mappedHandlers = object();\n this.populateMappedHandlers();\n\n this._options = this._initialOptions = deepMerge(\n defaultOptions,\n this.constructor.defaultOptions,\n options ?? object(),\n this.createDefaultHandlerOptions(),\n );\n\n this._dynamicKeys = this.getDynamicKeys();\n\n // Triggers the `init` options update for this extension.\n this.init();\n }\n\n /**\n * This method is called by the extension constructor. It is not strictly a\n * lifecycle method since at this point the manager has not yet been\n * instantiated.\n *\n * @remarks\n *\n * It should be used instead of overriding the constructor which is strongly\n * advised against.\n *\n * There are some limitations when using this method.\n *\n * - Accessing `this.store` will throw an error since the manager hasn't been\n * created and it hasn't yet been attached to the extensions.\n * - `this.type` in `NodeExtension` and `MarkExtension` will also throw an\n * error since the schema hasn't been created yet.\n *\n * You should use this to setup any instance properties with the options\n * provided to the extension.\n */\n protected init(): void {}\n\n /**\n * Clone the current instance with the provided options. If nothing is\n * provided it uses the same initial options as the current instance.\n */\n abstract clone(\n ...parameters: ConstructorProps<Options, DefaultStaticOptions>\n ): BaseClass<Options, DefaultStaticOptions>;\n\n /**\n * Get the dynamic keys for this extension.\n */\n private getDynamicKeys(): string[] {\n const dynamicKeys: string[] = [];\n const { customHandlerKeys, handlerKeys, staticKeys } = this.constructor;\n\n for (const key of keys(this._options)) {\n if (\n staticKeys.includes(key) ||\n handlerKeys.includes(key) ||\n customHandlerKeys.includes(key)\n ) {\n continue;\n }\n\n dynamicKeys.push(key);\n }\n\n return dynamicKeys;\n }\n\n /**\n * Throw an error if non dynamic keys are updated.\n */\n private ensureAllKeysAreDynamic(update: GetPartialDynamic<Options>) {\n if (environment.isProduction) {\n return;\n }\n\n const invalid: string[] = [];\n\n for (const key of keys(update)) {\n if (this._dynamicKeys.includes(key)) {\n continue;\n }\n\n invalid.push(key);\n }\n\n invariant(isEmptyArray(invalid), {\n code: ErrorConstant.INVALID_SET_EXTENSION_OPTIONS,\n message: `Invalid properties passed into the 'setOptions()' method: ${JSON.stringify(\n invalid,\n )}.`,\n });\n }\n\n /**\n * Update the properties with the provided partial value when changed.\n */\n setOptions(update: GetPartialDynamic<Options>): void {\n const previousOptions = this.getDynamicOptions();\n\n this.ensureAllKeysAreDynamic(update);\n\n const { changes, options, pickChanged } = getChangedOptions({\n previousOptions,\n update,\n });\n\n this.updateDynamicOptions(options);\n\n // Trigger the update handler so the extension can respond to any relevant\n // property updates.\n this.onSetOptions?.({\n reason: 'set',\n changes,\n options,\n pickChanged,\n initialOptions: this._initialOptions,\n });\n }\n\n /**\n * Reset the extension properties to their default values.\n *\n * @nonVirtual\n */\n resetOptions(): void {\n const previousOptions = this.getDynamicOptions();\n const { changes, options, pickChanged } = getChangedOptions<Options>({\n previousOptions,\n update: this._initialOptions,\n });\n\n this.updateDynamicOptions(options);\n\n // Trigger the update handler so that child extension properties can also be\n // updated.\n this.onSetOptions?.({\n reason: 'reset',\n options,\n changes,\n pickChanged,\n initialOptions: this._initialOptions,\n });\n }\n\n /**\n * Override this to receive updates whenever the options have been updated on\n * this instance. This method is called after the updates have already been\n * applied to the instance. If you need more control over exactly how the\n * option should be applied you should set the option to be `Custom`.\n *\n * **Please Note**:\n *\n * This must be defined as a instance method and not a property since it is\n * called in the constructor.\n *\n * ```ts\n * class ThisPreset extends Preset {\n * // GOOD \u2705\n * onSetOptions(props: OnSetOptionsProps<Options>) {}\n *\n * // BAD \u274C\n * onSetOptions = (props: OnSetOptionsProps<Options>) => {}\n * }\n * ```\n *\n * @abstract\n */\n protected onSetOptions?(props: OnSetOptionsProps<Options>): void;\n\n /**\n * Update the private options.\n */\n private getDynamicOptions(): GetFixedDynamic<Options> {\n return omit(this._options, [\n ...this.constructor.customHandlerKeys,\n ...this.constructor.handlerKeys,\n ]) as any;\n }\n\n /**\n * Update the dynamic options.\n */\n private updateDynamicOptions(options: GetFixedDynamic<Options>) {\n this._options = { ...this._options, ...options };\n }\n\n /**\n * Set up the mapped handlers object with default values (an empty array);\n */\n private populateMappedHandlers() {\n for (const key of this.constructor.handlerKeys) {\n this._mappedHandlers[key as keyof GetMappedHandler<Options>] = [];\n }\n }\n\n /**\n * This is currently fudged together, I'm not sure it will work.\n */\n private createDefaultHandlerOptions() {\n const methods = object<any>();\n\n for (const key of this.constructor.handlerKeys) {\n methods[key] = (...args: any[]) => {\n const { handlerKeyOptions } = this.constructor;\n const reducer = handlerKeyOptions[key]?.reducer;\n let returnValue: unknown = reducer?.getDefault(...args);\n\n for (const [, handler] of this._mappedHandlers[key as keyof GetMappedHandler<Options>]) {\n const value = (handler as unknown as AnyFunction)(...args);\n returnValue = reducer ? reducer.accumulator(returnValue, value, ...args) : value;\n\n // Check if the method should cause an early return, based on the\n // return value.\n if (shouldReturnEarly(handlerKeyOptions, returnValue, key)) {\n return returnValue;\n }\n }\n\n return returnValue;\n };\n }\n\n return methods;\n }\n\n /**\n * Add a handler to the event handlers so that it is called along with all the\n * other handler methods.\n *\n * This is helpful for integrating react hooks which can be used in multiple\n * places. The original problem with fixed properties is that you can only\n * assign to a method once and it overwrites any other methods. This pattern\n * for adding handlers allows for multiple usages of the same handler in the\n * most relevant part of the code.\n *\n * More to come on this pattern.\n *\n * @nonVirtual\n */\n addHandler<Key extends keyof GetHandler<Options>>(\n key: Key,\n method: GetHandler<Options>[Key],\n priority = ExtensionPriority.Default,\n ): Dispose {\n this._mappedHandlers[key].push([priority, method]);\n this.sortHandlers(key);\n\n // Return a method for disposing of the handler.\n return () =>\n (this._mappedHandlers[key] = this._mappedHandlers[key].filter(\n ([, handler]) => handler !== method,\n ));\n }\n\n /**\n * Determines if handlers exist for the given key.\n *\n * Checking the existence of a handler property directly gives wrong results.\n * `this.options.onHandlerName` is always truthy because it is a reference to\n * the wrapper function that calls each handler.\n *\n * ```ts\n *\n * // GOOD \u2705\n * if (!this.hasHandlers('onHandlerName')) {\n * return;\n * }\n *\n * // BAD \u274C\n * if (!this.options.onHandlerName) {\n * return;\n * }\n * ```\n *\n * @param key The handler to test\n */\n hasHandlers<Key extends keyof GetHandler<Options>>(key: Key): boolean {\n return (this._mappedHandlers[key] ?? []).length > 0;\n }\n\n private sortHandlers<Key extends keyof GetHandler<Options>>(key: Key) {\n this._mappedHandlers[key] = sort(\n this._mappedHandlers[key],\n // Sort from highest binding to the lowest.\n ([a], [z]) => z - a,\n );\n }\n\n /**\n * A method that can be used to add a custom handler. It is up to the\n * extension creator to manage the handlers and dispose methods.\n */\n addCustomHandler<Key extends keyof GetCustomHandler<Options>>(\n key: Key,\n value: Required<GetCustomHandler<Options>>[Key],\n ): Dispose {\n return this.onAddCustomHandler?.({ [key]: value } as any) ?? noop;\n }\n\n /**\n * Override this method if you want to set custom handlers on your extension.\n *\n * This must return a dispose function.\n */\n protected onAddCustomHandler?: AddCustomHandler<Options>;\n}\n\ntype HandlerKeyOptionsMap = Partial<\n Record<string, HandlerKeyOptions> & { [GENERAL_OPTIONS]?: HandlerKeyOptions }\n>;\n\n/**\n * A function used to determine whether the value provided by the handler\n * warrants an early return.\n */\nfunction shouldReturnEarly(\n handlerKeyOptions: HandlerKeyOptionsMap,\n returnValue: unknown,\n handlerKey: string,\n): boolean {\n const { [GENERAL_OPTIONS]: generalOptions } = handlerKeyOptions;\n const handlerOptions = handlerKeyOptions[handlerKey];\n\n if (!generalOptions && !handlerOptions) {\n return false;\n }\n\n // First check if there are options set for the provided handlerKey\n if (\n handlerOptions &&\n // Only proceed if the value should not be ignored.\n handlerOptions.earlyReturnValue !== IGNORE &&\n (isFunction(handlerOptions.earlyReturnValue)\n ? handlerOptions.earlyReturnValue(returnValue) === true\n : returnValue === handlerOptions.earlyReturnValue)\n ) {\n return true;\n }\n\n if (\n generalOptions &&\n // Only proceed if they are not ignored.\n generalOptions.earlyReturnValue !== IGNORE &&\n // Check whether the `earlyReturnValue` is a predicate check.\n (isFunction(generalOptions.earlyReturnValue)\n ? // If it is a predicate and when called with the current\n // `returnValue` the value is `true` then we should return\n // early.\n generalOptions.earlyReturnValue(returnValue) === true\n : // Check the actual return value.\n returnValue === generalOptions.earlyReturnValue)\n ) {\n return true;\n }\n\n return false;\n}\n\n/**\n * @internal\n */\nexport type CustomHandlerMethod<Options extends ValidOptions> = <\n Key extends keyof GetCustomHandler<Options>,\n>(\n key: Key,\n value: Required<GetCustomHandler<Options>>[Key],\n) => Dispose;\n\nexport type AddCustomHandler<Options extends ValidOptions> = (\n props: Partial<GetCustomHandler<Options>>,\n) => Dispose | undefined;\n\nexport type AddHandler<Options extends ValidOptions> = <Key extends keyof GetHandler<Options>>(\n key: Key,\n method: GetHandler<Options>[Key],\n) => Dispose;\n\n/**\n * TODO see if this is needed or remove.\n */\nexport type AddHandlers<Options extends ValidOptions> = (\n props: Partial<GetHandler<Options>>,\n) => Dispose;\n\nexport interface HandlerKeyOptions<ReturnType = any, Args extends any[] = any[]> {\n /**\n * When this value is encountered the handler will exit early.\n *\n * Set the value to `'__IGNORE__'` to ignore the early return value.\n */\n earlyReturnValue?: LiteralUnion<typeof IGNORE, Primitive> | ((value: unknown) => boolean);\n\n /**\n * Allows combining the values from the handlers together to produce a single\n * reduced output value.\n */\n reducer?: {\n /**\n * Combine the value with the the previous value\n */\n accumulator: (accumulated: ReturnType, latestValue: ReturnType, ...args: Args) => ReturnType;\n\n /**\n * The a function that returns the default value for combined handler\n * values. This is required for setting up a default value.\n */\n getDefault: (...args: Args) => ReturnType;\n };\n}\n\nexport interface BaseClass<\n Options extends ValidOptions,\n DefaultStaticOptions extends Shape = EmptyShape,\n> {\n constructor: BaseClassConstructor<Options, DefaultStaticOptions>;\n}\n\nexport interface BaseClassConstructor<\n Options extends ValidOptions = EmptyShape,\n DefaultStaticOptions extends Shape = EmptyShape,\n> extends Function {\n new (...args: ConstructorProps<Options, DefaultStaticOptions>): any;\n\n /**\n * The identifier for the constructor which can determine whether it is a node\n * constructor, mark constructor or plain constructor.\n * @internal\n */\n readonly [__INTERNAL_REMIRROR_IDENTIFIER_KEY__]: RemirrorIdentifier;\n\n /**\n * Defines the `defaultOptions` for all extension instances.\n *\n * @remarks\n *\n * Once set it can't be updated during run time. Some of the settings are\n * optional and some are not. Any non-required settings must be specified in\n * the `defaultOptions`.\n *\n * **Please note**: There is a slight downside when setting up\n * `defaultOptions`. `undefined` is not supported for partial settings at this\n * point in time. As a workaround use `null` as the type and pass it as the\n * value in the default settings.\n *\n * @defaultValue {}\n *\n * @internal\n */\n readonly defaultOptions: DefaultOptions<Options, DefaultStaticOptions>;\n\n /**\n * An array of the keys that are static for this extension.\n *\n * This is actually currently unused, but might become useful in the future.\n * An auto-fix lint rule will be added should that be the case.\n */\n readonly staticKeys: string[];\n\n /**\n * An array of all the keys which correspond to the the event handler options.\n *\n * This **MUST** be present if you want to use event handlers in your\n * extension.\n *\n * Every key here is automatically removed from the `setOptions` method and is\n * added to the `addHandler` method for adding new handlers. The\n * `this.options[key]` is automatically replaced with a method that combines\n * all the handlers into one method that can be called effortlessly. All this\n * work is done for you.\n */\n readonly handlerKeys: string[];\n\n /**\n * Customize the way the handler should behave.\n */\n readonly handlerKeyOptions: Partial<\n Record<string, HandlerKeyOptions> & { __ALL__?: HandlerKeyOptions }\n >;\n\n /**\n * A list of the custom keys in the extension or preset options.\n */\n readonly customHandlerKeys: string[];\n}\n\nexport type AnyBaseClassConstructor = Replace<\n BaseClassConstructor<any, any>,\n // eslint-disable-next-line @typescript-eslint/prefer-function-type\n { new (...args: any[]): AnyFunction }\n>;\n\n/* eslint-enable @typescript-eslint/member-ordering */\n\n/**\n * Auto infers the parameter for the constructor. If there is a required static\n * option then the TypeScript compiler will error if nothing is passed in.\n */\nexport type ConstructorProps<\n Options extends ValidOptions,\n DefaultStaticOptions extends Shape,\n> = IfNoRequiredProperties<\n GetStatic<Options>,\n [options?: GetConstructorProps<Options> & DefaultStaticOptions],\n [options: GetConstructorProps<Options> & DefaultStaticOptions]\n>;\n\n/**\n * Get the expected type signature for the `defaultOptions`. Requires that every\n * optional setting key (except for keys which are defined on the\n * `BaseExtensionOptions`) has a value assigned.\n */\nexport type DefaultOptions<\n Options extends ValidOptions,\n DefaultStaticOptions extends Shape,\n> = MakeUndefined<\n UndefinedFlipPartialAndRequired<GetStatic<Options>> &\n Partial<DefaultStaticOptions> &\n GetFixedDynamic<Options>,\n StringKey<GetAcceptUndefined<Options>>\n>;\n\nexport interface AnyBaseClassOverrides {\n addCustomHandler: AnyFunction;\n addHandler: AnyFunction;\n clone: AnyFunction;\n}\n", "import { ErrorConstant } from '@remirror/core-constants';\nimport { freeze, invariant, keys, object } from '@remirror/core-helpers';\nimport type { GetFixedDynamic, GetPartialDynamic, ValidOptions } from '@remirror/core-types';\n\nimport type { GetChangeOptionsReturn, PickChanged } from './types';\n\nexport interface GetChangedOptionsProps<Options extends ValidOptions> {\n /**\n * The previous readonly properties object.\n */\n previousOptions: GetFixedDynamic<Options>;\n\n /**\n * The partial update object that was passed through.\n */\n update: GetPartialDynamic<Options>;\n\n /**\n * A method to check whether two values are equal.\n */\n equals?: (valueA: unknown, valueB: unknown) => boolean;\n}\n\nfunction defaultEquals(valueA: unknown, valueB: unknown) {\n return valueA === valueB;\n}\n\n/**\n * Get the property changes and the next value from an update.\n */\nexport function getChangedOptions<Options extends ValidOptions>(\n props: GetChangedOptionsProps<Options>,\n): GetChangeOptionsReturn<Options> {\n const { previousOptions, update, equals = defaultEquals } = props;\n const next = freeze({ ...previousOptions, ...update });\n const changes = object<any>();\n const optionKeys = keys(previousOptions);\n\n for (const key of optionKeys) {\n const previousValue = previousOptions[key];\n const value = next[key];\n\n if (equals(previousValue, value)) {\n changes[key] = { changed: false };\n continue;\n }\n\n changes[key] = { changed: true, previousValue, value };\n }\n\n const pickChanged: PickChanged<Options> = (keys) => {\n const picked = object<any>();\n\n for (const key of keys) {\n const item = changes[key];\n\n if (item?.changed) {\n picked[key] = item.value;\n }\n }\n\n return picked;\n };\n\n return { changes: freeze(changes), options: next, pickChanged };\n}\n\nexport interface IsNameUniqueProps {\n /**\n * The name to check against\n */\n name: string;\n\n /**\n * The set to check within\n */\n set: Set<string>;\n\n /**\n * The error code to use\n *\n * @defaultValue 'extension'\n */\n code: ErrorConstant.DUPLICATE_HELPER_NAMES | ErrorConstant.DUPLICATE_COMMAND_NAMES;\n}\n\nconst codeLabelMap = {\n [ErrorConstant.DUPLICATE_HELPER_NAMES]: 'helper method',\n [ErrorConstant.DUPLICATE_COMMAND_NAMES]: 'command method',\n};\n\n/**\n * Checks whether a given string is unique to the set. Add the name if it\n * doesn't already exist, or throw an error when `shouldThrow` is true.\n *\n * @param props - destructured params\n */\nexport function throwIfNameNotUnique(props: IsNameUniqueProps): void {\n const { name, set, code } = props;\n const label = codeLabelMap[code];\n\n invariant(!set.has(name), {\n code,\n message: `There is a naming conflict for the name: ${name} used in this '${label}'. Please rename or remove from the editor to avoid runtime errors.`,\n });\n\n set.add(name);\n}\n", "import type { ExtensionPriority } from '@remirror/core-constants';\nimport { Cast } from '@remirror/core-helpers';\nimport type {\n CustomHandlerKeyList,\n EmptyShape,\n GetCustomHandler,\n GetHandler,\n GetStatic,\n HandlerKeyList,\n IfEmpty,\n IfHasRequiredProperties,\n Shape,\n StaticKeyList,\n Writeable,\n} from '@remirror/core-types';\n\nimport type { AnyExtensionConstructor, DefaultExtensionOptions, ExtensionConstructor } from './';\nimport type { HandlerKeyOptions } from './base-class';\n\ninterface DefaultOptionsProps<Options extends Shape = EmptyShape> {\n /**\n * The default options.\n *\n * All non required options must have a default value provided.\n *\n * Please note that as mentioned in this issue\n * [#624](https://github.com/remirror/remirror/issues/624), partial options\n * can cause trouble when setting a default.\n *\n * If you need to accept `undefined `as an acceptable default option there are\n * two possible ways to resolve this.\n *\n * #### Use `AcceptUndefined`\n *\n * This is the preferred solution and should be used instead of the following\n * `null` union.\n *\n * ```ts\n * import { AcceptUndefined } from 'remirror';\n *\n * interface Options {\n * optional?: AcceptUndefined<string>;\n * }\n * ```\n *\n * Now when the options are consumed by this decorator there should be no\n * errors when setting the value to `undefined`.\n *\n * #### `null` union\n *\n * If you don't mind using nulls in your code then this might appeal to you.\n *\n * ```ts\n * interface Options {\n * optional?: string | null;\n * }\n * ```\n *\n * @defaultValue {}\n */\n defaultOptions: DefaultExtensionOptions<Options>;\n}\n\ninterface DefaultPriorityProps {\n /**\n * The default priority for this extension.\n *\n * @defaultValue {}\n */\n defaultPriority?: ExtensionPriority;\n}\n\ninterface StaticKeysProps<Options extends Shape = EmptyShape> {\n /**\n * The list of all keys which are static and can only be set at the start.\n */\n staticKeys: StaticKeyList<Options>;\n}\n\n/**\n * This notifies the extension which options are handlers. Handlers typically\n * represent event handlers that are called in response to something happening.\n *\n * An `onChange` option could be a handler. When designing the API I had to\n * consider that often times, you might want to listen to a handler in several\n * places.\n *\n * A limitation of the static and dynamic options is that there is only one\n * value per extension. So if there is a `minValue` option and that min value\n * option is set in the extension then it becomes the value for all consumers of\n * the extension. Handlers don't have the same expected behaviour. It is\n * generally expected that you should be able to subscribe to an event in\n * multiple places.\n *\n * In order to make this possible with `remirror` the handlers are automatically\n * created based on the handler keys you provide. Each handler is an array and\n * when the handler is called with `this.options.onChange`, each item in the\n * array is called based on the rules provided.\n */\ninterface HandlerKeysProps<Options extends Shape = EmptyShape> {\n /**\n * The list of the option names which are event handlers.\n */\n handlerKeys: HandlerKeyList<Options>;\n\n /**\n * Customize how the handler should work.\n *\n * This allows you to decide how the handlers will be composed together.\n * Currently it only support function handlers, but you can tell the extension\n * to exit early when a certain return value is received.\n *\n * ```ts\n * const handlerOptions = { onChange: { earlyReturnValue: true }};\n * ```\n *\n * The above setting means that onChange will exit early as soon as one of the\n * methods returns true.\n */\n handlerKeyOptions?: MappedHandlerKeyOptions<Options>;\n}\n\ntype MappedHandlerKeyOptions<Options extends Shape = EmptyShape> = {\n [Key in keyof GetHandler<Options>]?: HandlerKeyOptions<\n ReturnType<GetHandler<Options>[Key]>,\n Parameters<GetHandler<Options>[Key]>\n >;\n} & { __ALL__?: HandlerKeyOptions };\n\ninterface CustomHandlerKeysProps<Options extends Shape = EmptyShape> {\n customHandlerKeys: CustomHandlerKeyList<Options>;\n}\n\nexport type ExtensionDecoratorOptions<Options extends Shape = EmptyShape> = DefaultPriorityProps &\n IfHasRequiredProperties<\n DefaultExtensionOptions<Options>,\n DefaultOptionsProps<Options>,\n Partial<DefaultOptionsProps<Options>>\n > &\n IfEmpty<GetStatic<Options>, Partial<StaticKeysProps<Options>>, StaticKeysProps<Options>> &\n IfEmpty<GetHandler<Options>, Partial<HandlerKeysProps<Options>>, HandlerKeysProps<Options>> &\n IfEmpty<\n GetCustomHandler<Options>,\n Partial<CustomHandlerKeysProps<Options>>,\n CustomHandlerKeysProps<Options>\n > &\n Partial<Remirror.StaticExtensionOptions>;\n\n/**\n * A decorator for the remirror extension.\n *\n * This adds static properties to the extension constructor.\n */\nexport function extension<Options extends Shape = EmptyShape>(\n options: ExtensionDecoratorOptions<Options>,\n) {\n return <Type extends AnyExtensionConstructor>(ReadonlyConstructor: Type): Type => {\n const {\n defaultOptions,\n customHandlerKeys,\n handlerKeys,\n staticKeys,\n defaultPriority,\n handlerKeyOptions,\n ...rest\n } = options;\n\n const Constructor = Cast<Writeable<ExtensionConstructor<Options>> & Shape>(ReadonlyConstructor);\n\n if (defaultOptions) {\n Constructor.defaultOptions = defaultOptions;\n }\n\n if (defaultPriority) {\n Constructor.defaultPriority = defaultPriority;\n }\n\n if (handlerKeyOptions) {\n Constructor.handlerKeyOptions = handlerKeyOptions;\n }\n\n Constructor.staticKeys = (staticKeys as string[]) ?? [];\n Constructor.handlerKeys = (handlerKeys as string[]) ?? [];\n Constructor.customHandlerKeys = (customHandlerKeys as string[]) ?? [];\n\n for (const [key, value] of Object.entries(rest as Shape)) {\n if (Constructor[key]) {\n continue;\n }\n\n Constructor[key] = value;\n }\n\n return Cast<Type>(Constructor);\n };\n}\n\n/**\n * @deprecated use `extension` instead.\n */\nexport const extensionDecorator = extension;\n", "import type { ExtensionPriority } from '@remirror/core-constants';\nimport type {\n AnyFunction,\n CommandFunction,\n KeyBindingCommandFunction,\n Listable,\n LiteralUnion,\n NonChainableCommandFunction,\n ProsemirrorAttributes,\n Shape,\n} from '@remirror/core-types';\nimport type { I18n } from '@remirror/i18n';\nimport type { CoreIcon } from '@remirror/icons';\n\nimport type { AnyExtension, HelperAnnotation } from '../extension';\nimport type { GetOptions, TypedPropertyDescriptor } from '../types';\n\n/**\n * A decorator which can be applied to top level methods on an extension to\n * identify them as helpers. This can be used as a replacement for the\n * `createHelpers` method.\n *\n * To allow the TypeScript compiler to automatically infer types, please create\n * your methods with the following type signature.\n *\n * ```ts\n * import { CommandFunction } from '@remirror/core';\n *\n * type Signature = (...args: any[]) => CommandFunction;\n * ```\n *\n * The following is an example of how this can be used within your extension.\n *\n * ```ts\n * import { helper, Helper } from '@remirror/core';\n *\n * class MyExtension {\n * get name() {\n * return 'my';\n * }\n *\n * @helper()\n * alwaysTrue(): Helper<boolean> {\n * return true;\n * }\n * }\n * ```\n *\n * The above helper can now be used within your editor instance.\n *\n * ```tsx\n * import { useRemirrorContext } from '@remirror/react';\n *\n * const MyEditorButton = () => {\n * const { helpers } = useRemirrorContext();\n *\n * return helpers.alwaysTrue() ? <button>My Button</button> : null\n * }\n * ```\n *\n * @category Method Decorator\n */\nexport function helper(options: HelperDecoratorOptions = {}) {\n return <Extension extends AnyExtension, Type>(\n target: Extension,\n propertyKey: string,\n _descriptor: TypedPropertyDescriptor<\n // This type signature helps enforce the need for the `Helper` annotation\n // while allowing for `null | undefined`.\n AnyFunction<NonNullable<Type> extends HelperAnnotation ? Type : never>\n >,\n ): void => {\n // Attach the options to the `decoratedCommands` property for this extension.\n (target.decoratedHelpers ??= {})[propertyKey] = options;\n };\n}\n\n/**\n * A decorator which can be applied to top level methods on an extension to\n * identify them as commands. This can be used as a replacement for the\n * `createCommands` method.\n *\n * If you prefer not to use decorators, then you can continue using\n * `createCommands`. Internally the decorators are being used as they are better\n * for documentation purposes.\n *\n * For automated type inference methods that use this decorator must implement\n * the following type signature.\n *\n * ```ts\n * import { CommandFunction } from '@remirror/core';\n *\n * type Signature = (...args: any[]) => CommandFunction;\n * ```\n *\n * The following is an example of how this can be used within your extension.\n *\n * ```ts\n * import { command, CommandFunction } from '@remirror/core';\n *\n * class MyExtension {\n * get name() {\n * return 'my';\n * }\n *\n * @command()\n * myCommand(text: string): CommandFunction {\n * return ({ tr, dispatch }) => {\n * dispatch?.(tr.insertText('my command ' + text));\n * return true;\n * }\n * }\n * }\n * ```\n *\n * The above command can now be used within your editor instance.\n *\n * ```tsx\n * import { useRemirrorContext } from '@remirror/react';\n *\n * const MyEditorButton = () => {\n * const { commands } = useRemirrorContext();\n *\n * return <button onClick={() => commands.myCommand('hello')}>My Button</button>\n * }\n * ```\n *\n * @category Method Decorator\n */\nexport function command<Extension extends AnyExtension>(\n options?: ChainableCommandDecoratorOptions<Required<GetOptions<Extension>>>,\n): ExtensionDecorator<Extension, CommandFunction, void>;\nexport function command<Extension extends AnyExtension>(\n options: NonChainableCommandDecoratorOptions<Required<GetOptions<Extension>>>,\n): ExtensionDecorator<Extension, NonChainableCommandFunction, void>;\nexport function command(options: CommandDecoratorOptions = {}): any {\n return (target: any, propertyKey: string, _descriptor: any): void => {\n // Attach the options to the decoratedCommands property for this extension.\n (target.decoratedCommands ??= {})[propertyKey] = options;\n };\n}\n\n/**\n * A decorator which can be applied to an extension method to\n * identify as a key binding method. This can be used as a replacement for\n * the `createKeymap` method depending on your preference.\n *\n * If you prefer not to use decorators, then you can continue using\n * `createKeymap`.\n *\n * @category Method Decorator\n */\n\nexport function keyBinding<Extension extends AnyExtension>(\n options: KeybindingDecoratorOptions<Required<GetOptions<Extension>>>,\n) {\n return (\n target: Extension,\n propertyKey: string,\n _descriptor: TypedPropertyDescriptor<KeyBindingCommandFunction>,\n ): void => {\n // Attach the options to the decoratedCommands property for this extension.\n (target.decoratedKeybindings ??= {})[propertyKey] = options as any;\n };\n}\n\nexport interface HelperDecoratorOptions {}\n\nexport type KeyboardShortcutFunction<Options extends Shape = Shape> = (\n options: Options,\n store: Remirror.ExtensionStore,\n) => KeyboardShortcut;\nexport type KeyboardShortcutValue = Listable<\n LiteralUnion<\n | 'Enter'\n | 'ArrowDown'\n | 'ArrowUp'\n | 'ArrowLeft'\n | 'ArrowRight'\n | 'Escape'\n | 'Delete'\n | 'Backspace',\n string\n >\n>;\n\nexport type KeyboardShortcut = KeyboardShortcutValue | KeyboardShortcutFunction;\n\nexport interface KeybindingDecoratorOptions<Options extends Shape = Shape> {\n /**\n * The keypress sequence to intercept.\n *\n * - `Enter`\n * - `Shift-Enter`\n */\n shortcut: KeyboardShortcut;\n\n /**\n * This can be used to set a keybinding as inactive based on the provided\n * options.\n */\n isActive?: (options: Options, store: Remirror.ExtensionStore) => boolean;\n\n /**\n * The priority for this keybinding.\n */\n priority?:\n | ExtensionPriority\n | ((options: Options, store: Remirror.ExtensionStore) => ExtensionPriority);\n\n /**\n * The name of the command that the keybinding should be attached to.\n */\n command?: Remirror.AllUiCommandNames;\n}\n\ntype ExtensionDecorator<Extension extends AnyExtension, Fn, Return> = (\n target: Extension,\n propertyKey: string,\n _descriptor: TypedPropertyDescriptor<AnyFunction<Fn>>,\n) => Return;\n\nexport interface CommandUiIcon {\n /**\n * The icon name.\n */\n name: CoreIcon;\n\n /**\n * Text placed in a superscript position. For `ltr` this is in the top right\n * hand corner of the icon.\n */\n sup?: string;\n\n /**\n * Text placed in a subscript position. For `ltr` this is in the bottom right\n * hand corner.\n */\n sub?: string;\n}\n\nexport type CommandDecoratorShortcut =\n | string\n | { shortcut: string; attrs: ProsemirrorAttributes }\n | string[]\n | Array<{ shortcut: string; attrs: ProsemirrorAttributes }>;\n\nexport interface CommandUiDecoratorOptions {\n /**\n * The default command icon to use if this has a UI representation.\n */\n icon?: CommandDecoratorValue<CoreIcon | CommandUiIcon>;\n\n /**\n * A label for the command with support for i18n. This makes use of\n * `babel-plugin-macros` to generate the message.\n */\n label?: CommandDecoratorMessage;\n\n /**\n * An i18n compatible description which can be used to provide extra context\n * for the command.\n */\n description?: CommandDecoratorMessage;\n\n /**\n * A keyboard shortcut which can be used to run the specified command.\n *\n * Rather than defining this here, you should create a decorated `keyBinding`\n * and set the `command` name option. This way the shortcut will dynamically\n * be added at runtime.\n */\n shortcut?: CommandDecoratorShortcut;\n}\n\nexport interface CommandDecoratorMessageProps {\n /**\n * True when the command is enabled.\n */\n enabled: boolean;\n\n /**\n * True when the extension is active.\n */\n active: boolean;\n\n /**\n * Predefined attributes which can influence the returned value.\n */\n attrs: ProsemirrorAttributes | undefined;\n\n /**\n * A translation utility for translating a predefined string / or message\n * descriptor.\n */\n t: I18n['_'];\n}\n\n/**\n * @typeParam Value - the value which should be returned from the function or\n * used directly.\n */\nexport type CommandDecoratorValue<Value> = ((props: CommandDecoratorMessageProps) => Value) | Value;\n\nexport type CommandDecoratorMessage = CommandDecoratorValue<string>;\ninterface ChainableCommandDecoratorOptions<Options extends Shape>\n extends Remirror.CommandDecoratorOptions<Options> {\n /**\n * Set this to `true` to disable chaining of this command. This means it will\n * no longer be available when running `\n *\n * @defaultValue false\n */\n disableChaining?: false;\n}\ninterface NonChainableCommandDecoratorOptions<Options extends Shape>\n extends Remirror.CommandDecoratorOptions<Options> {\n /**\n * Set this to `true` to disable chaining of this command. This means it will\n * no longer be available when running `\n *\n * @defaultValue false\n */\n disableChaining: true;\n}\n\nexport type CommandDecoratorOptions<Options extends Shape = Shape> =\n | ChainableCommandDecoratorOptions<Options>\n | NonChainableCommandDecoratorOptions<Options>;\n\ndeclare global {\n namespace Remirror {\n /**\n * UX options for the command which can be extended.\n */\n interface CommandDecoratorOptions<Options extends Shape = Shape>\n extends CommandUiDecoratorOptions {\n /**\n * A function which can be used to override whether a command is already\n * active for the current selection.\n */\n active?: (options: Options, store: ExtensionStore) => boolean;\n }\n }\n}\n", "import { pick } from '@remirror/core-helpers';\nimport type { GetStaticAndDynamic, ValueOf } from '@remirror/core-types';\n\nimport { AttributesExtension } from './attributes-extension';\nimport { CommandsExtension } from './commands-extension';\nimport { DecorationsExtension, DecorationsOptions } from './decorations-extension';\nimport { DocChangedExtension } from './doc-changed-extension';\nimport { HelpersExtension } from './helpers-extension';\nimport { InputRulesExtension, InputRulesOptions } from './input-rules-extension';\nimport { KeymapExtension, KeymapOptions } from './keymap-extension';\nimport { NodeViewsExtension } from './node-views-extension';\nimport { PasteRulesExtension } from './paste-rules-extension';\nimport { PluginsExtension } from './plugins-extension';\nimport { SchemaExtension } from './schema-extension';\nimport { SuggestExtension, SuggestOptions } from './suggest-extension';\nimport { TagsExtension } from './tags-extension';\nimport { UploadExtension } from './upload-extension';\n\nexport interface BuiltinOptions\n extends SuggestOptions,\n KeymapOptions,\n DecorationsOptions,\n InputRulesOptions {}\n\n/**\n * Provides all the builtin extensions to the editor.\n *\n * @remarks\n *\n * This is used automatically and (at the time of writing) can't be removed from\n * the editor. If you feel that there's a compelling reason to override these\n * extensions feel free to create a [discussion\n * here](https://github.com/remirror/remirror/discussions/category_choices) and\n * it can be addressed.\n *\n * @category Builtin Extension\n *\n * The order of these extension are important.\n *\n * - [[`TagsExtension`]] is places first because it provides tagging which is\n * used by the schema extension.\n * - [[`SchemeExtension`]] goes next because it's super important to the editor\n * functionality and needs to run before everything else which might depend\n * on it.\n */\nexport function builtinPreset(options: GetStaticAndDynamic<BuiltinOptions> = {}): BuiltinPreset[] {\n const defaultOptions = {\n exitMarksOnArrowPress: KeymapExtension.defaultOptions.exitMarksOnArrowPress,\n excludeBaseKeymap: KeymapExtension.defaultOptions.excludeBaseKeymap,\n selectParentNodeOnEscape: KeymapExtension.defaultOptions.selectParentNodeOnEscape,\n undoInputRuleOnBackspace: KeymapExtension.defaultOptions.undoInputRuleOnBackspace,\n persistentSelectionClass: DecorationsExtension.defaultOptions.persistentSelectionClass,\n };\n\n options = { ...defaultOptions, ...options };\n\n const keymapOptions = pick(options, [\n 'excludeBaseKeymap',\n 'selectParentNodeOnEscape',\n 'undoInputRuleOnBackspace',\n ]);\n const decorationsOptions = pick(options, ['persistentSelectionClass']);\n\n return [\n // The order of these extension is important. First come first served.\n new TagsExtension(),\n new SchemaExtension(),\n new AttributesExtension(),\n new PluginsExtension(),\n new InputRulesExtension(),\n new PasteRulesExtension(),\n new NodeViewsExtension(),\n new SuggestExtension(),\n new CommandsExtension(),\n new HelpersExtension(),\n new KeymapExtension(keymapOptions),\n new DocChangedExtension(),\n new UploadExtension(),\n new DecorationsExtension(decorationsOptions),\n ];\n}\n\nexport type BuiltinPreset =\n | TagsExtension\n | SchemaExtension\n | AttributesExtension\n | PluginsExtension\n | InputRulesExtension\n | PasteRulesExtension\n | NodeViewsExtension\n | SuggestExtension\n | CommandsExtension\n | HelpersExtension\n | KeymapExtension\n | DocChangedExtension\n | UploadExtension\n | DecorationsExtension;\n\ndeclare global {\n namespace Remirror {\n interface ManagerSettings {\n /**\n * The options that can be passed into the built in options.\n */\n builtin?: GetStaticAndDynamic<BuiltinOptions>;\n }\n\n /**\n * The builtin preset.\n */\n type Builtin = BuiltinPreset;\n\n /**\n * The union of every extension available via the remirror codebase.\n */\n type Extensions = ValueOf<AllExtensions>;\n }\n}\n", "import { ErrorConstant, ExtensionPriority, NamedShortcut } from '@remirror/core-constants';\nimport {\n entries,\n invariant,\n isEmptyArray,\n isEmptyObject,\n isString,\n object,\n uniqueArray,\n} from '@remirror/core-helpers';\nimport type {\n AnyFunction,\n CommandFunction,\n CommandFunctionProps,\n DispatchFunction,\n EmptyShape,\n Fragment,\n FromToProps,\n LiteralUnion,\n MarkType,\n NodeType,\n PrimitiveSelection,\n ProsemirrorAttributes,\n ProsemirrorNode,\n RemirrorContentType,\n Shape,\n Static,\n Transaction,\n} from '@remirror/core-types';\nimport {\n environment,\n getMarkRange,\n getTextSelection,\n htmlToProsemirrorNode,\n isEmptyBlockNode,\n isProsemirrorFragment,\n isProsemirrorNode,\n isTextSelection,\n removeMark,\n RemoveMarkProps,\n replaceText,\n ReplaceTextProps,\n setBlockType,\n toggleBlockItem,\n ToggleBlockItemProps,\n toggleWrap,\n wrapIn,\n} from '@remirror/core-utils';\nimport { CoreMessages as Messages } from '@remirror/messages';\nimport { Mark } from '@remirror/pm/model';\nimport { TextSelection } from '@remirror/pm/state';\nimport type { EditorView } from '@remirror/pm/view';\n\nimport { applyMark, insertText, InsertTextOptions, toggleMark, ToggleMarkProps } from '../commands';\nimport {\n AnyExtension,\n ChainedCommandProps,\n ChainedFromExtensions,\n CommandNames,\n CommandsFromExtensions,\n extension,\n Helper,\n PlainExtension,\n UiCommandNames,\n} from '../extension';\nimport { throwIfNameNotUnique } from '../helpers';\nimport type {\n CommandShape,\n CreateExtensionPlugin,\n ExtensionCommandFunction,\n ExtensionCommandReturn,\n FocusType,\n StateUpdateLifecycleProps,\n} from '../types';\nimport { command, CommandDecoratorOptions, helper } from './builtin-decorators';\n\nexport interface CommandOptions {\n /**\n * The className that is added to all tracker positions\n *\n * '@defaultValue 'remirror-tracker-position'\n */\n trackerClassName?: Static<string>;\n\n /**\n * The default element that is used for all trackers.\n *\n * @defaultValue 'span'\n */\n trackerNodeName?: Static<string>;\n}\n\n/**\n * Generate chained and unchained commands for making changes to the editor.\n *\n * @remarks\n *\n * Typically actions are used to create interactive menus. For example a menu\n * can use a command to toggle bold formatting or to undo the last action.\n *\n * @category Builtin Extension\n */\n@extension<CommandOptions>({\n defaultPriority: ExtensionPriority.Highest,\n defaultOptions: { trackerClassName: 'remirror-tracker-position', trackerNodeName: 'span' },\n staticKeys: ['trackerClassName', 'trackerNodeName'],\n})\nexport class CommandsExtension extends PlainExtension<CommandOptions> {\n get name() {\n return 'commands' as const;\n }\n\n /**\n * The current transaction which allows for making commands chainable.\n *\n * It is shared by all the commands helpers and can even be used in the\n * [[`KeymapExtension`]].\n *\n * @internal\n */\n get transaction(): Transaction {\n // Make sure we have the most up to date state.\n const state = this.store.getState();\n\n if (!this._transaction) {\n // Since there is currently no transaction set, make sure to create a new\n // one. Behind the scenes `state.tr` creates a new transaction for us to\n // use.\n this._transaction = state.tr;\n }\n\n // Check that the current transaction is valid.\n const isValid = this._transaction.before.eq(state.doc);\n\n // Check whether the current transaction has any already applied to it.\n const hasSteps = !isEmptyArray(this._transaction.steps);\n\n if (!isValid) {\n // Since the transaction is not valid we create a new one to prevent any\n // `mismatched` transaction errors.\n const tr = state.tr;\n\n // Now checking if any steps had been added to the previous transaction\n // and adding them to the newly created transaction.\n if (hasSteps) {\n for (const step of this._transaction.steps) {\n tr.step(step);\n }\n }\n\n // Make sure to store the transaction value to the instance of this\n // extension.\n this._transaction = tr;\n }\n\n return this._transaction;\n }\n\n /**\n * This is the holder for the shared transaction which is shared by commands\n * in order to support chaining.\n *\n * @internal\n */\n private _transaction?: Transaction;\n\n /**\n * Track the decorated command data.\n */\n private readonly decorated = new Map<string, WithName<CommandDecoratorOptions>>();\n\n onCreate(): void {\n this.store.setStoreKey('getForcedUpdates', this.getForcedUpdates.bind(this));\n }\n\n /**\n * Attach commands once the view is attached.\n */\n onView(view: EditorView): void {\n const { extensions, helpers } = this.store;\n const commands: Record<string, CommandShape> = object();\n const names = new Set<string>();\n let allDecoratedCommands: Record<string, CommandDecoratorOptions> = object();\n\n const chain = (tr?: Transaction) => {\n // This function allows for custom chaining.\n const customChain: Record<string, any> & ChainedCommandProps = object();\n const getTr = () => tr ?? this.transaction;\n let commandChain: Array<(dispatch?: DispatchFunction) => boolean> = [];\n const getChain = () => commandChain;\n\n for (const [name, command] of Object.entries(commands)) {\n if (allDecoratedCommands[name]?.disableChaining) {\n continue;\n }\n\n customChain[name] = this.chainedFactory({\n chain: customChain,\n command: command.original,\n getTr,\n getChain,\n });\n }\n\n /**\n * This function is used in place of the `view.dispatch` method which is\n * passed through to all commands.\n *\n * It is responsible for checking that the transaction which was\n * dispatched is the same as the shared transaction which makes chainable\n * commands possible.\n */\n const dispatch: DispatchFunction = (transaction) => {\n // Throw an error if the transaction being dispatched is not the same as\n // the currently stored transaction.\n invariant(transaction === getTr(), {\n message:\n 'Chaining currently only supports `CommandFunction` methods which do not use the `state.tr` property. Instead you should use the provided `tr` property.',\n });\n };\n\n customChain.run = (options = {}) => {\n const commands = commandChain;\n commandChain = [];\n\n for (const cmd of commands) {\n // Exit early when the command returns false and the option is\n // provided.\n if (!cmd(dispatch) && options.exitEarly) {\n return;\n }\n }\n\n view.dispatch(getTr());\n };\n\n customChain.tr = () => {\n const commands = commandChain;\n commandChain = [];\n\n for (const cmd of commands) {\n cmd(dispatch);\n }\n\n return getTr();\n };\n\n customChain.enabled = () => {\n for (const cmd of commandChain) {\n if (!cmd()) {\n return false;\n }\n }\n\n return true;\n };\n\n return customChain;\n };\n\n for (const extension of extensions) {\n const extensionCommands: ExtensionCommandReturn = extension.createCommands?.() ?? {};\n const decoratedCommands = extension.decoratedCommands ?? {};\n const active: Record<string, () => boolean> = {};\n\n // Augment the decorated commands.\n allDecoratedCommands = { ...allDecoratedCommands, decoratedCommands };\n\n for (const [commandName, options] of Object.entries(decoratedCommands)) {\n const shortcut =\n isString(options.shortcut) && options.shortcut.startsWith('_|')\n ? { shortcut: helpers.getNamedShortcut(options.shortcut, extension.options) }\n : undefined;\n this.updateDecorated(commandName, { ...options, name: extension.name, ...shortcut });\n\n extensionCommands[commandName] = (extension as Shape)[commandName].bind(extension);\n\n if (options.active) {\n active[commandName] = () => options.active?.(extension.options, this.store) ?? false;\n }\n }\n\n if (isEmptyObject(extensionCommands)) {\n continue;\n }\n\n // Gather the returned commands object from the extension.\n this.addCommands({ active, names, commands, extensionCommands });\n }\n\n const chainProperty = chain();\n\n for (const [key, command] of Object.entries(chainProperty)) {\n (chain as Record<string, any>)[key] = command;\n }\n\n this.store.setStoreKey('commands', commands as any);\n this.store.setStoreKey('chain', chain as any);\n this.store.setExtensionStore('commands', commands as any);\n this.store.setExtensionStore('chain', chain as any);\n }\n\n /**\n * Update the cached transaction whenever the state is updated.\n */\n onStateUpdate({ state }: StateUpdateLifecycleProps): void {\n this._transaction = state.tr;\n }\n\n /**\n * Create a plugin that solely exists to track forced updates via the\n * generated plugin key.\n */\n createPlugin(): CreateExtensionPlugin {\n return {};\n }\n\n /**\n * Enable custom commands to be used within the editor by users.\n *\n * This is preferred to the initial idea of setting commands on the\n * manager or even as a prop. The problem is that there's no typechecking\n * and it should be just fine to add your custom commands here to see the\n * dispatched immediately.\n *\n * To use it, firstly define the command.\n *\n * ```ts\n * import { CommandFunction } from 'remirror';\n *\n * const myCustomCommand: CommandFunction = ({ tr, dispatch }) => {\n * dispatch?.(tr.insertText('My Custom Command'));\n *\n * return true;\n * }\n * ```\n *\n * And then use it within the component.\n *\n * ```ts\n * import React, { useCallback } from 'react';\n * import { useRemirror } from '@remirror/react';\n *\n * const MyEditorButton = () => {\n * const { commands } = useRemirror();\n * const onClick = useCallback(() => {\n * commands.customDispatch(myCustomCommand);\n * }, [commands])\n *\n * return <button onClick={onClick}>Custom Command</button>\n * }\n * ```\n *\n * An alternative is to use a custom command directly from a\n * `prosemirror-*` library. This can be accomplished in the following way.\n *\n *\n * ```ts\n * import { joinDown } from 'prosemirror-commands';\n * import { convertCommand } from 'remirror';\n *\n * const MyEditorButton = () => {\n * const { commands } = useRemirror();\n * const onClick = useCallback(() => {\n * commands.customDispatch(convertCommand(joinDown));\n * }, [commands]);\n *\n * return <button onClick={onClick}>Custom Command</button>;\n * };\n * ```\n */\n @command()\n customDispatch(command: CommandFunction): CommandFunction {\n return command;\n }\n\n /**\n * Insert text into the dom at the current location by default. If a\n * promise is provided instead of text the resolved value will be inserted\n * at the tracked position.\n */\n @command()\n insertText(\n text: string | (() => Promise<string>),\n options: InsertTextOptions = {},\n ): CommandFunction {\n if (isString(text)) {\n return insertText(text, options);\n }\n\n return this.store\n .createPlaceholderCommand({\n promise: text,\n placeholder: { type: 'inline' },\n onSuccess: (value, range, props) => {\n return this.insertText(value, { ...options, ...range })(props);\n },\n })\n .generateCommand();\n }\n\n /**\n * Select the text within the provided range.\n *\n * Here are some ways it can be used.\n *\n * ```ts\n * // Set to the end of the document.\n * commands.selectText('end');\n *\n * // Set the selection to the start of the document.\n * commands.selectText('start');\n *\n * // Select all the text in the document.\n * commands.selectText('all')\n *\n * // Select a range of text. It's up to you to make sure the selected\n * // range is valid.\n * commands.selectText({ from: 10, to: 15 });\n *\n * // Specify the anchor and range in the selection.\n * commands.selectText({ anchor: 10, head: 15 });\n *\n * // Set to a specific position.\n * commands.selectText(10);\n *\n * // Use a ProseMirror selection\n * commands.selectText(TextSelection.near(state.doc.resolve(10)))\n * ```\n *\n * Although this is called `selectText` you can provide your own selection\n * option which can be any type of selection.\n */\n @command()\n selectText(\n selection: PrimitiveSelection,\n options: { forceUpdate?: boolean } = {},\n ): CommandFunction {\n return ({ tr, dispatch }) => {\n const textSelection = getTextSelection(selection, tr.doc);\n\n // Check if the selection is unchanged (for example when refocusing on the\n // editor) and if it is, then the text doesn't need to be reselected.\n const selectionUnchanged =\n tr.selection.anchor === textSelection.anchor && tr.selection.head === textSelection.head;\n\n if (selectionUnchanged && !options.forceUpdate) {\n // Do nothing if the selection is unchanged.\n return false;\n }\n\n dispatch?.(tr.setSelection(textSelection));\n\n return true;\n };\n }\n\n /**\n * Select the link at the current location.\n */\n @command()\n selectMark(type: string | MarkType): CommandFunction {\n return (props) => {\n const { tr } = props;\n const range = getMarkRange(tr.selection.$from, type);\n\n if (!range) {\n return false;\n }\n\n return this.store.commands.selectText.original({ from: range.from, to: range.to })(props);\n };\n }\n\n /**\n * Delete the provided range or current selection.\n */\n @command()\n delete(range?: FromToProps): CommandFunction {\n return ({ tr, dispatch }) => {\n const { from, to } = range ?? tr.selection;\n dispatch?.(tr.delete(from, to));\n\n return true;\n };\n }\n\n /**\n * Fire an empty update to trigger an update to all decorations, and state\n * that may not yet have run.\n *\n * This can be used in extensions to trigger updates when certain options that\n * affect the editor state have changed.\n *\n * @param action - provide an action which is called just before the empty\n * update is dispatched (only when dispatch is available). This can be used in\n * chainable editor scenarios when you want to lazily invoke an action at the\n * point the update is about to be applied.\n */\n @command()\n emptyUpdate(action?: () => void): CommandFunction {\n return ({ tr, dispatch }) => {\n if (dispatch) {\n action?.();\n dispatch(tr);\n }\n\n return true;\n };\n }\n\n /**\n * Force an update of the specific updatable ProseMirror props.\n *\n * This command is always available as a builtin command.\n *\n * @category Builtin Command\n */\n @command()\n forceUpdate(...keys: UpdatableViewProps[]): CommandFunction {\n return ({ tr, dispatch }) => {\n dispatch?.(this.forceUpdateTransaction(tr, ...keys));\n\n return true;\n };\n }\n\n /**\n * Update the attributes for the node at the specified `pos` in the\n * editor.\n *\n * @category Builtin Command\n */\n @command()\n updateNodeAttributes<Type extends object>(\n pos: number,\n attrs: ProsemirrorAttributes<Type>,\n ): CommandFunction {\n return ({ tr, dispatch }) => {\n dispatch?.(tr.setNodeMarkup(pos, undefined, attrs));\n\n return true;\n };\n }\n\n /**\n * Set the content of the editor while preserving history.\n *\n * Under the hood this is replacing the content in the document with the new\n * state.doc of the provided content.\n *\n * If the content is a string you will need to ensure you have the proper\n * string handler set up in the editor.\n */\n @command()\n setContent(content: RemirrorContentType, selection?: PrimitiveSelection): CommandFunction {\n return (props) => {\n const { tr, dispatch } = props;\n const state = this.store.manager.createState({ content, selection });\n\n if (!state) {\n return false;\n }\n\n dispatch?.(tr.replaceRangeWith(0, tr.doc.nodeSize - 2, state.doc));\n return true;\n };\n }\n\n /**\n * Reset the content of the editor while preserving the history.\n *\n * This means that undo and redo will still be active since the doc is replaced with a new doc.\n */\n @command()\n resetContent(): CommandFunction {\n return (props) => {\n const { tr, dispatch } = props;\n const doc = this.store.manager.createEmptyDoc();\n\n if (doc) {\n return this.setContent(doc)(props);\n }\n\n dispatch?.(tr.delete(0, tr.doc.nodeSize));\n return true;\n };\n }\n\n /**\n * Fire an update to remove the current range selection. The cursor will\n * be placed at the anchor of the current range selection.\n *\n * A range selection is a non-empty text selection.\n *\n * @category Builtin Command\n */\n @command()\n emptySelection(): CommandFunction {\n return ({ tr, dispatch }) => {\n if (tr.selection.empty) {\n return false;\n }\n\n dispatch?.(tr.setSelection(TextSelection.near(tr.selection.$anchor)));\n return true;\n };\n }\n\n /**\n * Insert a new line into the editor.\n *\n * Depending on editor setup and where the cursor is placed this may have\n * differing impacts.\n *\n * @category Builtin Command\n */\n @command()\n insertNewLine(): CommandFunction {\n return ({ dispatch, tr }) => {\n if (!isTextSelection(tr.selection)) {\n return false;\n }\n\n dispatch?.(tr.insertText('\\n'));\n\n return true;\n };\n }\n\n /**\n * Insert a node into the editor with the provided content.\n *\n * @category Builtin Command\n */\n @command()\n insertNode(\n node: string | NodeType | ProsemirrorNode | Fragment,\n options: InsertNodeOptions = {},\n ): CommandFunction {\n return ({ dispatch, tr, state }) => {\n const { attrs, range, selection, replaceEmptyParentBlock = false } = options;\n const { from, to, $from } = getTextSelection(selection ?? range ?? tr.selection, tr.doc);\n\n if (isProsemirrorNode(node) || isProsemirrorFragment(node)) {\n const pos = $from.before($from.depth);\n dispatch?.(\n replaceEmptyParentBlock && from === to && isEmptyBlockNode($from.parent)\n ? tr.replaceWith(pos, pos + $from.parent.nodeSize, node)\n : tr.replaceWith(from, to, node),\n );\n\n return true;\n }\n\n const nodeType = isString(node) ? state.schema.nodes[node] : node;\n\n invariant(nodeType, {\n code: ErrorConstant.SCHEMA,\n message: `The requested node type ${node} does not exist in the schema.`,\n });\n\n const marks: Mark[] | undefined = options.marks?.map((mark) => {\n if (mark instanceof Mark) {\n return mark;\n }\n\n const markType = isString(mark) ? state.schema.marks[mark] : mark;\n invariant(markType, {\n code: ErrorConstant.SCHEMA,\n message: `The requested mark type ${mark} does not exist in the schema.`,\n });\n\n return markType.create();\n });\n\n const content = nodeType.createAndFill(\n attrs,\n isString(options.content) ? state.schema.text(options.content) : options.content,\n marks,\n );\n\n if (!content) {\n return false;\n }\n\n // This should not be treated as a replacement.\n const isReplacement = from !== to;\n dispatch?.(isReplacement ? tr.replaceRangeWith(from, to, content) : tr.insert(from, content));\n return true;\n };\n }\n\n /**\n * Set the focus for the editor.\n *\n * If using this with chaining this should only be placed at the end of\n * the chain. It can cause hard to debug issues when used in the middle of\n * a chain.\n *\n * ```tsx\n * import { useCallback } from 'react';\n * import { useRemirrorContext } from '@remirror/react';\n *\n * const MenuButton = () => {\n * const { chain } = useRemirrorContext();\n * const onClick = useCallback(() => {\n * chain\n * .toggleBold()\n * .focus('end')\n * .run();\n * }, [chain])\n *\n * return <button onClick={onClick}>Bold</button>\n * }\n * ```\n */\n @command()\n focus(position?: FocusType): CommandFunction {\n return (props) => {\n const { dispatch, tr } = props;\n const { view } = this.store;\n\n if (position === false) {\n return false;\n }\n\n if (view.hasFocus() && (position === undefined || position === true)) {\n return false;\n }\n\n // Keep the current selection when position is `true` or `undefined`.\n if (position === undefined || position === true) {\n const { from = 0, to = from } = tr.selection;\n position = { from, to };\n }\n\n if (dispatch) {\n // Focus only when dispatch is provided.\n this.delayedFocus();\n }\n\n return this.selectText(position)(props);\n };\n }\n\n /**\n * Blur focus from the editor and also update the selection at the same\n * time.\n */\n @command()\n blur(position?: PrimitiveSelection): CommandFunction {\n return (props) => {\n const { view } = this.store;\n\n if (!view.hasFocus()) {\n return false;\n }\n\n requestAnimationFrame(() => {\n view.dom.blur();\n });\n\n return position ? this.selectText(position)(props) : true;\n };\n }\n\n /**\n * Set the block type of the current selection or the provided range.\n *\n * @param nodeType - the node type to create\n * @param attrs - the attributes to add to the node type\n * @param selection - the position in the document to set the block node\n * @param preserveAttrs - when true preserve the attributes at the provided selection\n */\n @command()\n setBlockNodeType(\n nodeType: string | NodeType,\n attrs?: ProsemirrorAttributes,\n selection?: PrimitiveSelection,\n preserveAttrs = true,\n ): CommandFunction {\n return setBlockType(nodeType, attrs, selection, preserveAttrs);\n }\n\n /**\n * Toggle between wrapping an inactive node with the provided node type, and\n * lifting it up into it's parent.\n *\n * @param nodeType - the node type to toggle\n * @param attrs - the attrs to use for the node\n * @param selection - the selection point in the editor to perform the action\n */\n @command()\n toggleWrappingNode(\n nodeType: string | NodeType,\n attrs?: ProsemirrorAttributes,\n selection?: PrimitiveSelection,\n ): CommandFunction {\n return toggleWrap(nodeType, attrs, selection);\n }\n\n /**\n * Toggle a block between the provided type and toggleType.\n */\n @command()\n toggleBlockNodeItem(toggleProps: ToggleBlockItemProps): CommandFunction {\n return toggleBlockItem(toggleProps);\n }\n\n /**\n * Wrap the selection or the provided text in a node of the given type with the\n * given attributes.\n */\n @command()\n wrapInNode(\n nodeType: string | NodeType,\n attrs?: ProsemirrorAttributes,\n range?: FromToProps | undefined,\n ): CommandFunction {\n return wrapIn(nodeType, attrs, range);\n }\n\n /**\n * Removes a mark from the current selection or provided range.\n */\n @command()\n applyMark(\n markType: string | MarkType,\n attrs?: ProsemirrorAttributes,\n selection?: PrimitiveSelection,\n ): CommandFunction {\n return applyMark(markType, attrs, selection);\n }\n\n /**\n * Removes a mark from the current selection or provided range.\n */\n @command()\n toggleMark(props: ToggleMarkProps): CommandFunction {\n return toggleMark(props);\n }\n\n /**\n * Removes a mark from the current selection or provided range.\n */\n @command()\n removeMark(props: RemoveMarkProps): CommandFunction {\n return removeMark(props);\n }\n\n /**\n * Set the meta data to attach to the editor on the next update.\n */\n @command()\n setMeta(name: string, value: unknown): CommandFunction {\n return ({ tr }) => {\n tr.setMeta(name, value);\n return true;\n };\n }\n\n /**\n * Select all text in the editor.\n */\n @command({\n description: ({ t }) => t(Messages.SELECT_ALL_DESCRIPTION),\n label: ({ t }) => t(Messages.SELECT_ALL_LABEL),\n shortcut: NamedShortcut.SelectAll,\n })\n selectAll(): CommandFunction {\n return this.selectText('all');\n }\n\n /**\n * Copy the selected content for non empty selections.\n */\n @command({\n description: ({ t }) => t(Messages.COPY_DESCRIPTION),\n label: ({ t }) => t(Messages.COPY_LABEL),\n shortcut: NamedShortcut.Copy,\n icon: 'fileCopyLine',\n })\n copy(): CommandFunction {\n return (props) => {\n if (props.tr.selection.empty) {\n return false;\n }\n\n if (props.dispatch) {\n document.execCommand('copy');\n }\n\n return true;\n };\n }\n\n /**\n * Select all text in the editor.\n */\n @command({\n description: ({ t }) => t(Messages.PASTE_DESCRIPTION),\n label: ({ t }) => t(Messages.PASTE_LABEL),\n shortcut: NamedShortcut.Paste,\n icon: 'clipboardLine',\n })\n paste(): CommandFunction {\n // Todo check if the permissions are supported first.\n // navigator.permissions.query({name: 'clipboard'})\n\n return this.store\n .createPlaceholderCommand({\n // TODO https://caniuse.com/?search=clipboard.read - once browser support is sufficient.\n promise: () => navigator.clipboard.readText(),\n placeholder: { type: 'inline' },\n onSuccess: (value, selection, props) => {\n return this.insertNode(\n htmlToProsemirrorNode({ content: value, schema: props.state.schema }),\n { selection },\n )(props);\n },\n })\n .generateCommand();\n }\n\n /**\n * Cut the selected content.\n */\n @command({\n description: ({ t }) => t(Messages.CUT_DESCRIPTION),\n label: ({ t }) => t(Messages.CUT_LABEL),\n shortcut: NamedShortcut.Cut,\n icon: 'scissorsFill',\n })\n cut(): CommandFunction {\n return (props) => {\n if (props.tr.selection.empty) {\n return false;\n }\n\n if (props.dispatch) {\n document.execCommand('cut');\n }\n\n return true;\n };\n }\n\n /**\n * Replaces text with an optional appended string at the end. The replacement\n * can be text, or a custom node.\n *\n * @param props - see [[`ReplaceTextProps`]]\n */\n @command()\n replaceText(props: ReplaceTextProps): CommandFunction {\n return replaceText(props);\n }\n\n /**\n * Get the all the decorated commands available on the editor instance.\n */\n @helper()\n getAllCommandOptions(): Helper<Record<string, WithName<CommandDecoratorOptions>>> {\n const uiCommands: Record<string, WithName<CommandDecoratorOptions>> = {};\n\n for (const [name, options] of this.decorated) {\n if (isEmptyObject(options)) {\n continue;\n }\n\n uiCommands[name] = options;\n }\n\n return uiCommands;\n }\n\n /**\n * Get the options that were passed into the provided command.\n */\n @helper()\n getCommandOptions(name: string): Helper<WithName<CommandDecoratorOptions> | undefined> {\n return this.decorated.get(name);\n }\n\n /**\n * A short hand way of getting the `view`, `state`, `tr` and `dispatch`\n * methods.\n */\n @helper()\n getCommandProp(): Helper<Required<CommandFunctionProps>> {\n return {\n tr: this.transaction,\n dispatch: this.store.view.dispatch,\n state: this.store.view.state,\n view: this.store.view,\n };\n }\n\n /**\n * Update the command options via a shallow merge of the provided options. If\n * no options are provided the entry is deleted.\n *\n * @internal\n */\n updateDecorated(name: string, options?: Partial<WithName<CommandDecoratorOptions>>): void {\n if (!options) {\n this.decorated.delete(name);\n return;\n }\n\n const decoratorOptions = this.decorated.get(name) ?? { name: '' };\n this.decorated.set(name, { ...decoratorOptions, ...options });\n }\n\n /**\n * Needed on iOS since `requestAnimationFrame` doesn't breaks the focus\n * implementation.\n */\n private handleIosFocus(): void {\n if (!environment.isIos) {\n return;\n }\n\n this.store.view.dom.focus();\n }\n\n /**\n * Focus the editor after a slight delay.\n */\n private delayedFocus(): void {\n // Manage focus on iOS.\n this.handleIosFocus();\n\n requestAnimationFrame(() => {\n // Use the built in focus method to refocus the editor.\n this.store.view.focus();\n\n // This has to be called again in order for Safari to scroll into view\n // after the focus. Perhaps there's a better way though or maybe place\n // behind a flag.\n this.store.view.dispatch(this.transaction.scrollIntoView());\n });\n }\n\n /**\n * A helper for forcing through updates in the view layer. The view layer can\n * check for the meta data of the transaction with\n * `manager.store.getForcedUpdate(tr)`. If that has a value then it should use\n * the unique symbol to update the key.\n */\n private readonly forceUpdateTransaction = (\n tr: Transaction,\n ...keys: UpdatableViewProps[]\n ): Transaction => {\n const { forcedUpdates } = this.getCommandMeta(tr);\n\n this.setCommandMeta(tr, { forcedUpdates: uniqueArray([...forcedUpdates, ...keys]) });\n return tr;\n };\n\n /**\n * Check for a forced update in the transaction. This pulls the meta data\n * from the transaction and if it is true then it was a forced update.\n *\n * ```ts\n * import { CommandsExtension } from 'remirror/extensions';\n *\n * const commandsExtension = manager.getExtension(CommandsExtension);\n * log(commandsExtension.getForcedUpdates(tr))\n * ```\n *\n * This can be used for updating:\n *\n * - `nodeViews`\n * - `editable` status of the editor\n * - `attributes` - for the top level node\n *\n * @internal\n */\n private getForcedUpdates(tr: Transaction): ForcedUpdateMeta {\n return this.getCommandMeta(tr).forcedUpdates;\n }\n\n /**\n * Get the command metadata.\n */\n private getCommandMeta(tr: Transaction): Required<CommandExtensionMeta> {\n const meta = tr.getMeta(this.pluginKey) ?? {};\n return { ...DEFAULT_COMMAND_META, ...meta };\n }\n\n private setCommandMeta(tr: Transaction, update: CommandExtensionMeta) {\n const meta = this.getCommandMeta(tr);\n tr.setMeta(this.pluginKey, { ...meta, ...update });\n }\n\n /**\n * Add the commands from the provided `commands` property to the `chained`,\n * `original` and `unchained` objects.\n */\n private addCommands(props: AddCommandsProps) {\n const { extensionCommands, commands, names, active } = props;\n\n for (const [name, command] of entries(extensionCommands)) {\n // Command names must be unique.\n throwIfNameNotUnique({ name, set: names, code: ErrorConstant.DUPLICATE_COMMAND_NAMES });\n\n // Make sure the command name is not forbidden.\n invariant(!forbiddenNames.has(name), {\n code: ErrorConstant.DUPLICATE_COMMAND_NAMES,\n message: 'The command name you chose is forbidden.',\n });\n\n // Create the unchained command.\n commands[name] = this.createUnchainedCommand(command, active[name]);\n }\n }\n\n /**\n * Create an unchained command method.\n */\n private unchainedFactory(props: UnchainedFactoryProps) {\n return (...args: unknown[]) => {\n const { shouldDispatch = true, command } = props;\n const { view } = this.store;\n const { state } = view;\n\n let dispatch: DispatchFunction | undefined;\n\n if (shouldDispatch) {\n dispatch = view.dispatch;\n }\n\n return command(...args)({ state, dispatch, view, tr: state.tr });\n };\n }\n\n /**\n * Create the unchained command.\n */\n private createUnchainedCommand(\n command: ExtensionCommandFunction,\n active: (() => boolean) | undefined,\n ): CommandShape {\n const unchainedCommand: CommandShape = this.unchainedFactory({ command }) as any;\n unchainedCommand.enabled = this.unchainedFactory({ command, shouldDispatch: false });\n unchainedCommand.isEnabled = unchainedCommand.enabled;\n unchainedCommand.original = command;\n unchainedCommand.active = active;\n\n return unchainedCommand;\n }\n\n /**\n * Create a chained command method.\n */\n private chainedFactory(props: ChainedFactoryProps) {\n return (...args: unknown[]) => {\n const { chain: chained, command, getTr, getChain } = props;\n const lazyChain = getChain();\n const { view } = this.store;\n const { state } = view;\n\n lazyChain.push((dispatch?: DispatchFunction) => {\n return command(...args)({ state, dispatch, view, tr: getTr() });\n });\n\n return chained;\n };\n }\n}\n\nexport interface InsertNodeOptions {\n attrs?: ProsemirrorAttributes;\n marks?: Array<Mark | string | MarkType>;\n\n /**\n * The content to insert.\n */\n content?: Fragment | ProsemirrorNode | ProsemirrorNode[] | string;\n\n /**\n * @deprecated use selection property instead.\n */\n range?: FromToProps;\n\n /**\n * Set the selection where the command should occur.\n */\n selection?: PrimitiveSelection;\n\n /**\n * Set this to true to replace an empty parent block with this content (if the\n * content is a block node).\n */\n replaceEmptyParentBlock?: boolean;\n}\n\nconst DEFAULT_COMMAND_META: Required<CommandExtensionMeta> = {\n forcedUpdates: [],\n};\n\n/**\n * Provides the list of Prosemirror EditorView props that should be updated/\n */\nexport type ForcedUpdateMeta = UpdatableViewProps[];\nexport type UpdatableViewProps = 'attributes' | 'editable';\n\nexport interface CommandExtensionMeta {\n forcedUpdates?: UpdatableViewProps[];\n}\n\ninterface AddCommandsProps {\n /**\n * Some commands can declare that they are active.\n */\n active: Record<string, () => boolean>;\n\n /**\n * The currently amassed commands (unchained) to mutate for each extension.\n */\n commands: Record<string, CommandShape>;\n\n /**\n * The untransformed commands which need to be added to the extension.\n */\n extensionCommands: ExtensionCommandReturn;\n\n /**\n * The names of the commands amassed. This allows for a uniqueness test.\n */\n names: Set<string>;\n}\n\ninterface UnchainedFactoryProps {\n /**\n * All the commands.\n */\n command: ExtensionCommandFunction;\n\n /**\n * When false the dispatch is not provided (making this an `isEnabled` check).\n *\n * @defaultValue true\n */\n shouldDispatch?: boolean;\n}\n\n/**\n * A type with a name property.\n */\ntype WithName<Type> = Type & { name: string };\n\ninterface ChainedFactoryProps {\n /**\n * All the commands.\n */\n command: ExtensionCommandFunction;\n\n /**\n * All the chained commands\n */\n chain: Record<string, any>;\n\n /**\n * A custom transaction getter to apply to all the commands in the chain.\n */\n getTr: () => Transaction;\n\n /**\n * Get the list of lazy commands.\n */\n getChain: () => Array<(dispatch?: DispatchFunction) => boolean>;\n}\n\n/**\n * The names that are forbidden from being used as a command name.\n */\nconst forbiddenNames = new Set(['run', 'chain', 'original', 'raw', 'enabled', 'tr']);\n\ndeclare global {\n namespace Remirror {\n interface ManagerStore<Extension extends AnyExtension> {\n /**\n * Get the forced updates from the provided transaction.\n */\n getForcedUpdates: (tr: Transaction) => ForcedUpdateMeta;\n\n /**\n * Enables the use of custom commands created by extensions which extend\n * the functionality of your editor in an expressive way.\n *\n * @remarks\n *\n * Commands are synchronous and immediately dispatched. This means that\n * they can be used to create menu items when the functionality you need\n * is already available by the commands.\n *\n * ```ts\n * if (commands.toggleBold.isEnabled()) {\n * commands.toggleBold();\n * }\n * ```\n */\n commands: CommandsFromExtensions<Extension>;\n\n /**\n * Chainable commands for composing functionality together in quaint and\n * beautiful ways\n *\n * @remarks\n *\n * You can use this property to create expressive and complex commands\n * that build up the transaction until it can be run.\n *\n * The way chainable commands work is by adding multiple steps to a shared\n * transaction which is then dispatched when the `run` command is called.\n * This requires making sure that commands within your code use the `tr`\n * that is provided rather than the `state.tr` property. `state.tr`\n * creates a new transaction which is not shared by the other steps in a\n * chainable command.\n *\n * The aim is to make as many commands as possible chainable as explained\n * [here](https://github.com/remirror/remirror/issues/418#issuecomment-666922209).\n *\n * There are certain commands that can't be made chainable.\n *\n * - undo\n * - redo\n *\n * ```ts\n * chain\n * .toggleBold()\n * .insertText('Hi')\n * .setSelection('all')\n * .run();\n * ```\n *\n * The `run()` method ends the chain and dispatches the command.\n */\n chain: ChainedFromExtensions<Extension>;\n }\n\n interface BaseExtension {\n /**\n * `ExtensionCommands`\n *\n * This pseudo property makes it easier to infer Generic types of this\n * class.\n *\n * @internal\n */\n ['~C']: this['createCommands'] extends AnyFunction\n ? ReturnType<this['createCommands']>\n : EmptyShape;\n\n /**\n * @experimental\n *\n * Stores all the command names for this decoration that have been added\n * as decorators to the extension instance. This is used by the\n * `CommandsExtension` to pick the commands and store meta data attached\n * to each command.\n *\n * @internal\n */\n decoratedCommands?: Record<string, CommandDecoratorOptions>;\n\n /**\n * Create and register commands for that can be called within the editor.\n *\n * These are typically used to create menu's actions and as a direct\n * response to user actions.\n *\n * @remarks\n *\n * The `createCommands` method should return an object with each key being\n * unique within the editor. To ensure that this is the case it is\n * recommended that the keys of the command are namespaced with the name\n * of the extension.\n *\n * ```ts\n * import { ExtensionFactory } from '@remirror/core';\n *\n * const MyExtension = ExtensionFactory.plain({\n * name: 'myExtension',\n * version: '1.0.0',\n * createCommands() {\n * return {\n * haveFun() {\n * return ({ state, dispatch }) => {\n * if (dispatch) {\n * dispatch(tr.insertText('Have fun!'));\n * }\n *\n * return true; // True return signifies that this command is enabled.\n * }\n * },\n * }\n * }\n * })\n * ```\n *\n * The actions available in this case would be `undoHistory` and\n * `redoHistory`. It is unlikely that any other extension would override\n * these commands.\n *\n * Another benefit of commands is that they are picked up by typescript\n * and can provide code completion for consumers of the extension.\n */\n createCommands?(): ExtensionCommandReturn;\n }\n\n interface ExtensionStore {\n /**\n * A property containing all the available commands in the editor.\n *\n * This should only be accessed after the `onView` lifecycle method\n * otherwise it will throw an error. If you want to use it in the\n * `createCommands` function then make sure it is used within the returned\n * function scope and not in the outer scope.\n */\n commands: CommandsFromExtensions<Extensions | (AnyExtension & { _T: false })>;\n\n /**\n * A method that returns an object with all the chainable commands\n * available to be run.\n *\n * @remarks\n *\n * Each chainable command mutates the states transaction so after running\n * all your commands. you should dispatch the desired transaction.\n *\n * This should only be called when the view has been initialized (i.e.)\n * within the `createCommands` method calls.\n *\n * ```ts\n * import { ExtensionFactory } from '@remirror/core';\n *\n * const MyExtension = ExtensionFactory.plain({\n * name: 'myExtension',\n * version: '1.0.0',\n * createCommands: () => {\n * // This will throw since it can only be called within the returned\n * methods.\n * const chain = this.store.chain; // \u274C\n *\n * return {\n * // This is good \uD83D\uDE0B\n * haveFun() {\n * return ({ state, dispatch }) =>\n * this.store.chain.insertText('fun!').run(); \u2705\n * },\n * }\n * }\n * })\n * ```\n *\n * This should only be accessed after the `EditorView` has been fully\n * attached to the `RemirrorManager`.\n *\n * The chain can also be called as a function with a custom `tr`\n * parameter. This allows you to provide a custom transaction to use\n * within the chainable commands.\n *\n * Use the command at the beginning of the command chain to override the\n * shared transaction.\n *\n * There are times when you want to be sure of the transaction which is\n * being updated.\n *\n * To restore the previous transaction call the `restore` chained method.\n *\n * @param tr - the transaction to set\n */\n chain: ChainedFromExtensions<Extensions | (AnyExtension & { _T: false })>;\n }\n\n interface AllExtensions {\n commands: CommandsExtension;\n }\n\n /**\n * The command names for all core extensions.\n */\n type AllCommandNames = LiteralUnion<CommandNames<Remirror.Extensions>, string>;\n\n /**\n * The command names for all core extensions.\n */\n type AllUiCommandNames = LiteralUnion<UiCommandNames<Remirror.Extensions>, string>;\n }\n}\n", "import { ErrorConstant } from '@remirror/core-constants';\nimport {\n assertGet,\n entries,\n invariant,\n isFunction,\n isPromise,\n isString,\n} from '@remirror/core-helpers';\nimport type {\n AttributesProps,\n CommandFunction,\n CommandFunctionProps,\n FromToProps,\n MarkType,\n MarkTypeProps,\n PrimitiveSelection,\n ProsemirrorAttributes,\n ProsemirrorNode,\n} from '@remirror/core-types';\nimport { convertCommand, getCursor, getTextSelection, isMarkActive } from '@remirror/core-utils';\nimport { toggleMark as originalToggleMark } from '@remirror/pm/commands';\nimport type { SelectionRange } from '@remirror/pm/state';\n\n/**\n * The parameter that is passed into `DelayedCommand`s.\n */\ninterface DelayedCommandProps<Value> {\n /**\n * Runs as soon as the command is triggered. For most delayed commands within\n * the `remirror` codebase this is used to add a position tracker to the\n * document.\n */\n immediate?: CommandFunction;\n\n /**\n * The promise that provides the value that the `onDone` callback uses to\n * complete the delayed command.\n */\n promise: DelayedValue<Value>;\n\n /**\n * Called when the provided promise resolves.\n */\n onDone: CommandFunction<{ value: Value }>;\n\n /**\n * Called when the promise fails. This could be used to cleanup the active\n * position trackers when the delayed command fails.\n */\n onFail?: CommandFunction;\n}\n\nexport type DelayedValue<Type> = Promise<Type> | (() => Promise<Type>);\n\n/**\n * Returns `true` when the provided value is a delayed value.\n */\nexport function isDelayedValue<Type>(value: unknown): value is DelayedValue<Type> {\n return isFunction(value) || isPromise(value);\n}\n\n/**\n * Add tentative support for delayed commands in the editor.\n *\n * Delayed commands are commands that run an immediate action, like adding a\n * tracker to a position in the document. Once the promise that is provided is\n * returned the `onDone` parameter is run with the document in the current\n * state. The tracker that was added can now be used to insert content, delete\n * content or replace content.\n *\n * @experimental This is still being worked on and the API is subject to changes\n * in structure going forward.\n *\n * @deprecated use [[`DelayedCommand`]] instead.\n *\n */\nexport function delayedCommand<Value>({\n immediate,\n promise,\n onDone,\n onFail,\n}: DelayedCommandProps<Value>): CommandFunction {\n return (props) => {\n const { view } = props;\n\n if (immediate?.(props) === false) {\n return false;\n }\n\n if (!view) {\n return true;\n }\n\n const deferred = isFunction(promise) ? promise() : promise;\n\n deferred\n .then((value) => {\n // Run the command\n onDone({ state: view.state, tr: view.state.tr, dispatch: view.dispatch, view, value });\n })\n .catch(() => {\n // Run the failure command if it exists.\n onFail?.({ state: view.state, tr: view.state.tr, dispatch: view.dispatch, view });\n });\n\n return true;\n };\n}\n\nexport type DelayedPromiseCreator<Value> = (props: CommandFunctionProps) => Promise<Value>;\n\nexport class DelayedCommand<Value> {\n private readonly failureHandlers: Array<CommandFunction<{ error: any }>> = [];\n private readonly successHandlers: Array<CommandFunction<{ value: Value }>> = [];\n private readonly validateHandlers: CommandFunction[] = [];\n\n constructor(private readonly promiseCreator: DelayedPromiseCreator<Value>) {}\n\n /**\n * The commands that will immediately be run and used to evaluate whether to\n * proceed.\n */\n validate(handler: CommandFunction, method: 'push' | 'unshift' = 'push'): this {\n this.validateHandlers[method](handler);\n return this;\n }\n\n /**\n * Add a success callback to the handler.\n */\n success(handler: CommandFunction<{ value: Value }>, method: 'push' | 'unshift' = 'push'): this {\n this.successHandlers[method](handler);\n return this;\n }\n\n /**\n * Add a failure callback to the handler.\n */\n failure(handler: CommandFunction<{ error: any }>, method: 'push' | 'unshift' = 'push'): this {\n this.failureHandlers[method](handler);\n return this;\n }\n\n private runHandlers<Param extends CommandFunctionProps>(\n handlers: Array<(params: Param) => boolean>,\n param: Param,\n ): void {\n for (const handler of handlers) {\n if (!handler({ ...param, dispatch: () => {} })) {\n break;\n }\n }\n\n param.dispatch?.(param.tr);\n }\n\n /**\n * Generate the `remirror` command.\n */\n readonly generateCommand = (): CommandFunction => {\n return (props) => {\n let isValid = true;\n const { view, tr, dispatch } = props;\n\n if (!view) {\n return false;\n }\n\n for (const handler of this.validateHandlers) {\n if (!handler({ ...props, dispatch: () => {} })) {\n isValid = false;\n break;\n }\n }\n\n if (!dispatch || !isValid) {\n return isValid;\n }\n\n // Start the promise.\n const deferred = this.promiseCreator(props);\n\n deferred\n .then((value) => {\n this.runHandlers(this.successHandlers, {\n value,\n state: view.state,\n tr: view.state.tr,\n dispatch: view.dispatch,\n view,\n });\n })\n .catch((error) => {\n this.runHandlers(this.failureHandlers, {\n error,\n state: view.state,\n tr: view.state.tr,\n dispatch: view.dispatch,\n view,\n });\n });\n\n dispatch(tr);\n return true;\n };\n };\n}\n\nexport interface ToggleMarkProps extends MarkTypeProps, Partial<AttributesProps> {\n /**\n * @deprecated use `selection` property instead.\n */\n range?: FromToProps;\n\n /**\n * The selection point for toggling the chosen mark.\n */\n selection?: PrimitiveSelection;\n}\n\n/**\n * A custom `toggleMark` function that works for the `remirror` codebase.\n *\n * Create a command function that toggles the given mark with the given\n * attributes. Will return `false` when the current selection doesn't support\n * that mark. This will remove the mark if any marks of that type exist in the\n * selection, or add it otherwise. If the selection is empty, this applies to\n * the [stored marks](#state.EditorState.storedMarks) instead of a range of the\n * document.\n *\n * The differences from the `prosemirror-commands` version.\n * - Acts on the transaction rather than the state to allow for commands to be\n * chained together.\n * - Uses the ONE parameter function signature for compatibility with remirror.\n * - Supports passing a custom range.\n */\nexport function toggleMark(props: ToggleMarkProps): CommandFunction {\n const { type, attrs, range, selection } = props;\n\n return (props) => {\n const { dispatch, tr, state } = props;\n const markType = isString(type) ? state.schema.marks[type] : type;\n\n invariant(markType, {\n code: ErrorConstant.SCHEMA,\n message: `Mark type: ${type} does not exist on the current schema.`,\n });\n\n if (range || selection) {\n const { from, to } = getTextSelection(selection ?? range ?? tr.selection, tr.doc);\n isMarkActive({ trState: tr, type, ...range })\n ? dispatch?.(tr.removeMark(from, to, markType))\n : dispatch?.(tr.addMark(from, to, markType.create(attrs)));\n\n return true;\n }\n\n return convertCommand(originalToggleMark(markType, attrs))(props);\n };\n}\n\n/**\n * Verifies that the mark type can be applied to the current document.\n */\nfunction markApplies(type: MarkType, doc: ProsemirrorNode, ranges: readonly SelectionRange[]) {\n for (const { $from, $to } of ranges) {\n let markIsAllowed = $from.depth === 0 ? doc.type.allowsMarkType(type) : false;\n\n doc.nodesBetween($from.pos, $to.pos, (node) => {\n if (markIsAllowed) {\n // This prevents diving deeper into child nodes.\n return false;\n }\n\n markIsAllowed = node.inlineContent && node.type.allowsMarkType(type);\n return;\n });\n\n if (markIsAllowed) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Apply the provided mark type and attributes.\n *\n * @param markType - the mark to apply.\n * @param attrs - the attributes to set on the applied mark.\n * @param selectionPoint - optionally specify where the mark should be applied.\n * Defaults to the current selection.\n */\nexport function applyMark(\n type: string | MarkType,\n attrs?: ProsemirrorAttributes,\n selectionPoint?: PrimitiveSelection,\n): CommandFunction {\n return ({ tr, dispatch, state }) => {\n const selection = getTextSelection(selectionPoint ?? tr.selection, tr.doc);\n const $cursor = getCursor(selection);\n\n const markType = isString(type) ? state.schema.marks[type] : type;\n\n invariant(markType, {\n code: ErrorConstant.SCHEMA,\n message: `Mark type: ${type} does not exist on the current schema.`,\n });\n\n if ((selection.empty && !$cursor) || !markApplies(markType, tr.doc, selection.ranges)) {\n return false;\n }\n\n if (!dispatch) {\n return true;\n }\n\n if ($cursor) {\n tr.removeStoredMark(markType);\n\n if (attrs) {\n tr.addStoredMark(markType.create(attrs));\n }\n\n dispatch(tr);\n return true;\n }\n\n let containsMark = false;\n\n for (const { $from, $to } of selection.ranges) {\n if (containsMark) {\n break;\n }\n\n containsMark = tr.doc.rangeHasMark($from.pos, $to.pos, markType);\n }\n\n for (const { $from, $to } of selection.ranges) {\n if (containsMark) {\n tr.removeMark($from.pos, $to.pos, markType);\n }\n\n if (attrs) {\n tr.addMark($from.pos, $to.pos, markType.create(attrs));\n }\n }\n\n dispatch(tr);\n\n return true;\n };\n}\n\nexport interface InsertTextOptions extends Partial<FromToProps> {\n /**\n * Marks can be added to the inserted text.\n */\n marks?: Record<string, ProsemirrorAttributes>;\n}\n\n/**\n * Insert text into the dom at the current location by default. If a promise is\n * provided then the text will be inserted at the tracked position when the\n * promise is resolved.\n */\nexport function insertText(text: string, options: InsertTextOptions = {}): CommandFunction {\n return ({ tr, dispatch, state }) => {\n const schema = state.schema;\n const selection = tr.selection;\n const { from = selection.from, to = from ?? selection.to, marks = {} } = options;\n\n if (!dispatch) {\n return true;\n }\n\n // Insert the text\n tr.insertText(text, from, to);\n\n // Map the end position after inserting the text to understand what needs to\n // be wrapped with a mark.\n const end = assertGet(tr.steps, tr.steps.length - 1)\n .getMap()\n .map(to);\n\n // Loop through the provided marks to add the mark to the selection. This\n // uses the order of the map you created. If any marks are exclusive, they\n // will override the previous.\n for (const [markName, attributes] of entries(marks)) {\n tr.addMark(from, end, assertGet(schema.marks, markName).create(attributes));\n }\n\n dispatch(tr);\n\n return true;\n };\n}\n", "import { ExtensionPriority } from '@remirror/core-constants';\nimport { isNumber, isString, uniqueArray, uniqueId } from '@remirror/core-helpers';\nimport type {\n AcceptUndefined,\n CommandFunction,\n CommandFunctionProps,\n EditorState,\n EditorView,\n FromToProps,\n Handler,\n MakeRequired,\n Static,\n Transaction,\n} from '@remirror/core-types';\nimport { findNodeAtPosition, isNodeSelection } from '@remirror/core-utils';\nimport { Decoration, DecorationSet } from '@remirror/pm/view';\n\nimport { DelayedCommand, DelayedPromiseCreator } from '../commands';\nimport { extension, Helper, PlainExtension } from '../extension';\nimport type { CreateExtensionPlugin } from '../types';\nimport { command, helper } from './builtin-decorators';\n\nexport interface DecorationsOptions {\n /**\n * This setting is for adding a decoration to the selected text and can be\n * used to preserve the marker for the selection when the editor loses focus.\n *\n * You can set it as `'selection'` to match the default styles provided by\n * `@remirror/styles`.\n *\n * @defaultValue undefined\n */\n persistentSelectionClass?: AcceptUndefined<string | boolean>;\n\n /**\n * Add custom decorations to the editor via `extension.addHandler`. This can\n * be used via the `useDecorations` hook available from `remirror/react`.\n */\n decorations: Handler<(state: EditorState) => DecorationSet>;\n\n /**\n * The className that is added to all placeholder positions\n *\n * '@defaultValue 'placeholder'\n */\n placeholderClassName?: Static<string>;\n\n /**\n * The default element that is used for all placeholders.\n *\n * @defaultValue 'span'\n */\n placeholderNodeName?: Static<string>;\n}\n\n/**\n * Simplify the process of adding decorations to the editor. All the decorations\n * added to the document this way are automatically tracked which allows for\n * custom components to be nested inside decorations.\n *\n * @category Builtin Extension\n */\n@extension<DecorationsOptions>({\n defaultOptions: {\n persistentSelectionClass: undefined,\n placeholderClassName: 'placeholder',\n placeholderNodeName: 'span',\n },\n staticKeys: ['placeholderClassName', 'placeholderNodeName'],\n handlerKeys: ['decorations'],\n handlerKeyOptions: {\n decorations: {\n reducer: {\n accumulator: (accumulated, latestValue, state) => {\n return accumulated.add(state.doc, latestValue.find());\n },\n getDefault: () => DecorationSet.empty,\n },\n },\n },\n defaultPriority: ExtensionPriority.Low,\n})\nexport class DecorationsExtension extends PlainExtension<DecorationsOptions> {\n get name() {\n return 'decorations' as const;\n }\n\n /**\n * The placeholder decorations.\n */\n private placeholders = DecorationSet.empty;\n\n /**\n * A map of the html elements to their decorations.\n */\n private readonly placeholderWidgets = new Map<unknown, Decoration>();\n\n onCreate(): void {\n this.store.setExtensionStore('createPlaceholderCommand', this.createPlaceholderCommand);\n }\n\n /**\n * Create the extension plugin for inserting decorations into the editor.\n */\n createPlugin(): CreateExtensionPlugin {\n return {\n state: {\n init: () => {},\n apply: (tr) => {\n // Get tracker updates from the meta data\n const { added, clearTrackers, removed, updated } = this.getMeta(tr);\n\n if (clearTrackers) {\n this.placeholders = DecorationSet.empty;\n\n for (const [, widget] of this.placeholderWidgets) {\n widget.spec.onDestroy?.(this.store.view, widget.spec.element);\n }\n\n this.placeholderWidgets.clear();\n return;\n }\n\n this.placeholders = this.placeholders.map(tr.mapping, tr.doc, {\n onRemove: (spec) => {\n // Remove any removed widgets.\n const widget = this.placeholderWidgets.get(spec.id);\n\n if (widget) {\n widget.spec.onDestroy?.(this.store.view, widget.spec.element);\n }\n },\n });\n\n for (const [, widget] of this.placeholderWidgets) {\n widget.spec.onUpdate?.(\n this.store.view,\n widget.from,\n widget.spec.element,\n widget.spec.data,\n );\n }\n\n // Update the decorations with any added position trackers.\n for (const placeholder of added) {\n if (placeholder.type === 'inline') {\n this.addInlinePlaceholder(placeholder as WithBase<InlinePlaceholder>, tr);\n continue;\n }\n\n if (placeholder.type === 'node') {\n this.addNodePlaceholder(placeholder as WithBase<NodePlaceholder>, tr);\n continue;\n }\n\n if (placeholder.type === 'widget') {\n this.addWidgetPlaceholder(placeholder as WithBase<WidgetPlaceholder>, tr);\n continue;\n }\n }\n\n for (const { id, data } of updated) {\n const widget = this.placeholderWidgets.get(id);\n\n // Only support updating widget decorations.\n if (!widget) {\n continue;\n }\n\n const updatedWidget = Decoration.widget(widget.from, widget.spec.element, {\n ...widget.spec,\n data,\n });\n\n this.placeholders = this.placeholders.remove([widget]).add(tr.doc, [updatedWidget]);\n this.placeholderWidgets.set(id, updatedWidget);\n }\n\n for (const id of removed) {\n const found = this.placeholders.find(\n undefined,\n undefined,\n (spec) => spec.id === id && spec.__type === __type,\n );\n\n const widget = this.placeholderWidgets.get(id);\n\n if (widget) {\n widget.spec.onDestroy?.(this.store.view, widget.spec.element);\n }\n\n this.placeholders = this.placeholders.remove(found);\n this.placeholderWidgets.delete(id);\n }\n },\n },\n props: {\n decorations: (state) => {\n let decorationSet = this.options.decorations(state);\n decorationSet = decorationSet.add(state.doc, this.placeholders.find());\n\n for (const extension of this.store.extensions) {\n // Skip this extension when the method doesn't exist.\n if (!extension.createDecorations) {\n continue;\n }\n\n const decorations = extension.createDecorations(state).find();\n decorationSet = decorationSet.add(state.doc, decorations);\n }\n\n return decorationSet;\n },\n handleDOMEvents: {\n // Dispatch a transaction for focus/blur events so that the editor state\n // can be refreshed.\n //\n // https://discuss.prosemirror.net/t/handling-focus-in-plugins/1981/2\n blur: (view) => {\n if (this.options.persistentSelectionClass) {\n view.dispatch(view.state.tr.setMeta(persistentSelectionFocusKey, false));\n }\n\n return false;\n },\n focus: (view) => {\n if (this.options.persistentSelectionClass) {\n view.dispatch(view.state.tr.setMeta(persistentSelectionFocusKey, true));\n }\n\n return false;\n },\n },\n },\n };\n }\n\n @command()\n updateDecorations(): CommandFunction {\n return ({ tr, dispatch }) => (dispatch?.(tr), true);\n }\n\n /**\n * Command to dispatch a transaction adding the placeholder decoration to\n * be tracked.\n *\n * @param id - the value that is used to identify this tracker. This can\n * be any value. A promise, a function call, a string.\n * @param options - the options to call the tracked position with. You can\n * specify the range `{ from: number; to: number }` as well as the class\n * name.\n */\n @command()\n addPlaceholder(\n id: unknown,\n placeholder: DecorationPlaceholder,\n deleteSelection?: boolean,\n ): CommandFunction {\n return ({ dispatch, tr }) => {\n return this.addPlaceholderTransaction(id, placeholder, tr, !dispatch)\n ? (dispatch?.(deleteSelection ? tr.deleteSelection() : tr), true)\n : false;\n };\n }\n\n /**\n * A command to updated the placeholder decoration.\n *\n * To update multiple placeholders you can use chained commands.\n *\n * ```ts\n * let idsWithData: Array<{id: unknown, data: number}>;\n *\n * for (const { id, data } of idsWithData) {\n * chain.updatePlaceholder(id, data);\n * }\n *\n * chain.run();\n * ```\n */\n @command()\n updatePlaceholder<Data = any>(id: unknown, data: Data): CommandFunction {\n return ({ dispatch, tr }) => {\n return this.updatePlaceholderTransaction({ id, data, tr, checkOnly: !dispatch })\n ? (dispatch?.(tr), true)\n : false;\n };\n }\n\n /**\n * A command to remove the specified placeholder decoration.\n */\n @command()\n removePlaceholder(id: unknown): CommandFunction {\n return ({ dispatch, tr }) => {\n return this.removePlaceholderTransaction({ id, tr, checkOnly: !dispatch })\n ? (dispatch?.(tr), true)\n : false;\n };\n }\n\n /**\n * A command to remove all active placeholder decorations.\n */\n @command()\n clearPlaceholders(): CommandFunction {\n return ({ tr, dispatch }) => {\n return this.clearPlaceholdersTransaction({ tr, checkOnly: !dispatch })\n ? (dispatch?.(tr), true)\n : false;\n };\n }\n\n /**\n * Find the position for the tracker with the ID specified.\n *\n * @param id - the unique position id which can be any type\n */\n @helper()\n findPlaceholder(id: unknown): Helper<FromToProps | undefined> {\n return this.findAllPlaceholders().get(id);\n }\n\n /**\n * Find the positions of all the trackers in document.\n */\n @helper()\n findAllPlaceholders(): Helper<Map<unknown, FromToProps>> {\n const trackers: Map<unknown, FromToProps> = new Map();\n const found = this.placeholders.find(undefined, undefined, (spec) => spec.__type === __type);\n\n for (const decoration of found) {\n trackers.set(decoration.spec.id, { from: decoration.from, to: decoration.to });\n }\n\n return trackers;\n }\n\n /**\n * Add some decorations based on the provided settings.\n */\n createDecorations(state: EditorState): DecorationSet {\n const { persistentSelectionClass } = this.options;\n\n // Only show the selection decoration when the view doesn't have focus.\n // Notice that we need to listen to the focus/blur DOM events to make\n // it work since the focus state is not stored in `EditorState`.\n if (\n !persistentSelectionClass ||\n this.store.view?.hasFocus() ||\n this.store.helpers.isInteracting?.()\n ) {\n return DecorationSet.empty;\n }\n\n // Add the selection decoration to the decorations array.\n return generatePersistentSelectionDecorations(state, DecorationSet.empty, {\n class: isString(persistentSelectionClass) ? persistentSelectionClass : 'selection',\n });\n }\n\n /**\n * This stores all tracked positions in the editor and maps them via the\n * transaction updates.\n */\n onApplyState(): void {}\n\n /**\n * Add a widget placeholder and track it as a widget placeholder.\n */\n private addWidgetPlaceholder(placeholder: WithBase<WidgetPlaceholder>, tr: Transaction): void {\n const { pos, createElement, onDestroy, onUpdate, className, nodeName, id, type } = placeholder;\n\n const element = createElement?.(this.store.view, pos) ?? document.createElement(nodeName);\n element.classList.add(className);\n const decoration = Decoration.widget(pos, element, {\n id,\n __type,\n type,\n element,\n onDestroy,\n onUpdate,\n });\n\n this.placeholderWidgets.set(id, decoration);\n this.placeholders = this.placeholders.add(tr.doc, [decoration]);\n }\n\n /**\n * Add an inline placeholder.\n */\n private addInlinePlaceholder(placeholder: WithBase<InlinePlaceholder>, tr: Transaction): void {\n const {\n from = tr.selection.from,\n to = tr.selection.to,\n className,\n nodeName,\n id,\n type,\n } = placeholder;\n let decoration: Decoration;\n\n if (from === to) {\n // Add this as a widget if the range is empty.\n const element = document.createElement(nodeName);\n element.classList.add(className);\n decoration = Decoration.widget(from, element, {\n id,\n type,\n __type,\n widget: element,\n });\n } else {\n // Make this span across nodes if the range is not empty.\n decoration = Decoration.inline(\n from,\n to,\n { nodeName, class: className },\n {\n id,\n __type,\n },\n );\n }\n\n this.placeholders = this.placeholders.add(tr.doc, [decoration]);\n }\n\n /**\n * Add a placeholder for nodes.\n */\n private addNodePlaceholder(placeholder: WithBase<NodePlaceholder>, tr: Transaction): void {\n const { pos, className, nodeName, id } = placeholder;\n const $pos = isNumber(pos) ? tr.doc.resolve(pos) : tr.selection.$from;\n const found = isNumber(pos)\n ? $pos.nodeAfter\n ? { pos, end: $pos.nodeAfter.nodeSize }\n : undefined\n : findNodeAtPosition($pos);\n\n if (!found) {\n return;\n }\n\n const decoration = Decoration.node(\n found.pos,\n found.end,\n { nodeName, class: className },\n { id, __type },\n );\n this.placeholders = this.placeholders.add(tr.doc, [decoration]);\n }\n\n /**\n * Add the node and class name to the placeholder object.\n */\n private withRequiredBase<Type extends BasePlaceholder>(\n id: unknown,\n placeholder: Type,\n ): WithBase<Type> {\n const { placeholderNodeName, placeholderClassName } = this.options;\n const { nodeName = placeholderNodeName, className, ...rest } = placeholder;\n const classes = (className ? [placeholderClassName, className] : [placeholderClassName]).join(\n ' ',\n );\n\n return { nodeName, className: classes, ...rest, id };\n }\n\n /**\n * Get the command metadata.\n */\n private getMeta(tr: Transaction): Required<DecorationPlaceholderMeta> {\n const meta = tr.getMeta(this.pluginKey) ?? {};\n return { ...DEFAULT_PLACEHOLDER_META, ...meta };\n }\n\n /**\n * Set the metadata for the command.\n */\n private setMeta(tr: Transaction, update: DecorationPlaceholderMeta) {\n const meta = this.getMeta(tr);\n tr.setMeta(this.pluginKey, { ...meta, ...update });\n }\n\n /**\n * Add a placeholder decoration with the specified params to the transaction\n * and return the transaction.\n *\n * It is up to you to dispatch the transaction or you can just use the\n * commands.\n */\n private addPlaceholderTransaction(\n id: unknown,\n placeholder: DecorationPlaceholder,\n tr: Transaction,\n checkOnly = false,\n ): boolean {\n const existingPosition = this.findPlaceholder(id);\n\n if (existingPosition) {\n return false;\n }\n\n if (checkOnly) {\n return true;\n }\n\n const { added } = this.getMeta(tr);\n\n this.setMeta(tr, {\n added: [...added, this.withRequiredBase(id, placeholder)],\n });\n\n return true;\n }\n\n /**\n * Update the data stored by a placeholder.\n *\n * This replaces the whole data value.\n */\n private updatePlaceholderTransaction<Data = any>(props: {\n id: unknown;\n data: Data;\n tr: Transaction;\n checkOnly?: boolean;\n }): boolean {\n const { id, tr, checkOnly = false, data } = props;\n const existingPosition = this.findPlaceholder(id);\n\n if (!existingPosition) {\n return false;\n }\n\n if (checkOnly) {\n return true;\n }\n\n const { updated } = this.getMeta(tr);\n this.setMeta(tr, { updated: uniqueArray([...updated, { id, data }]) });\n\n return true;\n }\n\n /**\n * Discards a previously defined tracker once not needed.\n *\n * This should be used to cleanup once the position is no longer needed.\n */\n private removePlaceholderTransaction(props: {\n id: unknown;\n tr: Transaction;\n checkOnly?: boolean;\n }): boolean {\n const { id, tr, checkOnly = false } = props;\n const existingPosition = this.findPlaceholder(id);\n\n if (!existingPosition) {\n return false;\n }\n\n if (checkOnly) {\n return true;\n }\n\n const { removed } = this.getMeta(tr);\n this.setMeta(tr, { removed: uniqueArray([...removed, id]) });\n\n return true;\n }\n\n /**\n * This helper returns a transaction that clears all position trackers when\n * any exist.\n *\n * Otherwise it returns undefined.\n */\n private clearPlaceholdersTransaction(props: { tr: Transaction; checkOnly?: boolean }): boolean {\n const { tr, checkOnly = false } = props;\n const positionTrackerState = this.getPluginState();\n\n if (positionTrackerState === DecorationSet.empty) {\n return false;\n }\n\n if (checkOnly) {\n return true;\n }\n\n this.setMeta(tr, { clearTrackers: true });\n return true;\n }\n\n /**\n * Handles delayed commands which rely on the\n */\n private readonly createPlaceholderCommand = <Value>(\n props: DelayedPlaceholderCommandProps<Value>,\n ): DelayedCommand<Value> => {\n const id = uniqueId();\n const { promise, placeholder, onFailure, onSuccess } = props;\n\n return new DelayedCommand(promise)\n .validate((props) => {\n return this.addPlaceholder(id, placeholder)(props);\n })\n .success((props) => {\n const { state, tr, dispatch, view, value } = props;\n const range = this.store.helpers.findPlaceholder(id);\n\n if (!range) {\n const error = new Error('The placeholder has been removed');\n return onFailure?.({ error, state, tr, dispatch, view }) ?? false;\n }\n\n this.removePlaceholder(id)({ state, tr, view, dispatch: () => {} });\n return onSuccess(value, range, { state, tr, dispatch, view });\n })\n\n .failure((props) => {\n this.removePlaceholder(id)({ ...props, dispatch: () => {} });\n return onFailure?.(props) ?? false;\n });\n };\n}\n\nconst DEFAULT_PLACEHOLDER_META: Required<DecorationPlaceholderMeta> = {\n added: [],\n updated: [],\n clearTrackers: false,\n removed: [],\n};\n\nconst __type = 'placeholderDecoration';\n\nconst persistentSelectionFocusKey = 'persistentSelectionFocus';\n\nexport interface DecorationPlaceholderMeta {\n /**\n * The trackers to add.\n */\n added?: Array<WithBase<DecorationPlaceholder>>;\n\n /**\n * The trackers to update with new data. Data is an object and is used to\n * include properties like `progress` for progress indicators. Only `widget`\n * decorations can be updated in this way.\n */\n updated?: Array<{ id: unknown; data: any }>;\n\n /**\n * The trackers to remove.\n */\n removed?: unknown[];\n\n /**\n * When set to true will delete all the active trackers.\n */\n clearTrackers?: boolean;\n}\n\ninterface BasePlaceholder {\n /**\n * A custom class name to use for the placeholder decoration. All the trackers\n * will automatically be given the class name `remirror-tracker-position`\n *\n * @defaultValue ''\n */\n className?: string;\n\n /**\n * A custom html element or string for a created element tag name.\n *\n * @defaultValue 'tracker'\n */\n nodeName?: string;\n}\n\ninterface DataProps<Data = any> {\n /**\n * The data to store for this placeholder.\n */\n data?: Data;\n}\n\ninterface InlinePlaceholder<Data = any>\n extends BasePlaceholder,\n Partial<FromToProps>,\n DataProps<Data> {\n type: 'inline';\n}\n\ninterface NodePlaceholder<Data = any> extends BasePlaceholder, DataProps<Data> {\n /**\n * Set this as a node tracker.\n */\n type: 'node';\n\n /**\n * If provided the The `pos` must be directly before the node in order to be\n * valid. If not provided it will select the parent node of the current\n * selection.\n */\n pos: number | null;\n}\n\nexport interface WidgetPlaceholder<Data = any> extends BasePlaceholder, DataProps<Data> {\n /**\n * Declare this as a widget tracker.\n *\n * Widget trackers support adding custom components to the created dom\n * element.\n */\n type: 'widget';\n\n /**\n * Widget trackers only support fixed positions.\n */\n pos: number;\n\n /**\n * Called the first time this widget decoration is added to the dom.\n */\n createElement?(view: EditorView, pos: number): HTMLElement;\n\n /**\n * Called whenever the position tracker updates with the new position.\n */\n onUpdate?(view: EditorView, pos: number, element: HTMLElement, data: any): void;\n\n /**\n * Called when the widget decoration is removed from the dom.\n */\n onDestroy?(view: EditorView, element: HTMLElement): void;\n}\n\ntype WithBase<Type extends BasePlaceholder> = MakeRequired<Type, keyof BasePlaceholder> & {\n id: unknown;\n};\n\nexport type DecorationPlaceholder = WidgetPlaceholder | NodePlaceholder | InlinePlaceholder;\n\n/**\n * Generate the persistent selection decoration for when the editor loses focus.\n */\nfunction generatePersistentSelectionDecorations(\n state: EditorState,\n decorationSet: DecorationSet,\n attrs: { class: string },\n): DecorationSet {\n const { selection, doc } = state;\n\n if (selection.empty) {\n return decorationSet;\n }\n\n const { from, to } = selection;\n const decoration = isNodeSelection(selection)\n ? Decoration.node(from, to, attrs)\n : Decoration.inline(from, to, attrs);\n\n return decorationSet.add(doc, [decoration]);\n}\n\nexport interface DelayedPlaceholderCommandProps<Value> {\n /**\n * A function that returns a promise.\n */\n promise: DelayedPromiseCreator<Value>;\n\n /**\n * The placeholder configuration.\n */\n placeholder: DecorationPlaceholder;\n\n /**\n * Called when the promise succeeds and the placeholder still exists. If no\n * placeholder can be found (for example, the user has deleted the entire\n * document) then the failure handler is called instead.\n */\n onSuccess: (value: Value, range: FromToProps, commandProps: CommandFunctionProps) => boolean;\n\n /**\n * Called when a failure is encountered.\n */\n onFailure?: CommandFunction<{ error: any }>;\n}\n\ndeclare global {\n namespace Remirror {\n interface ExtensionStore {\n /**\n * Create delayed command which automatically adds a placeholder to the\n * document while the delayed command is being run and also automatically\n * removes it once it has completed.\n */\n createPlaceholderCommand<Value = any>(\n props: DelayedPlaceholderCommandProps<Value>,\n ): DelayedCommand<Value>;\n }\n\n interface BaseExtension {\n /**\n * Create a decoration set which adds decorations to your editor. The\n * first parameter is the `EditorState`.\n *\n * This can be used in combination with the `onApplyState` handler which\n * can map the decoration.\n *\n * @param state - the editor state which was passed in.\n */\n createDecorations?(state: EditorState): DecorationSet;\n }\n\n interface AllExtensions {\n decorations: DecorationsExtension;\n }\n }\n}\n", "import { ExtensionPriority } from '@remirror/core-constants';\nimport type { Handler } from '@remirror/core-types';\n\nimport { extension, PlainExtension } from '../extension';\nimport type { StateUpdateLifecycleProps } from '../types';\n\nexport interface DocChangedOptions {\n docChanged?: Handler<(props: StateUpdateLifecycleProps) => void>;\n}\n\n@extension<DocChangedOptions>({\n handlerKeys: ['docChanged'],\n handlerKeyOptions: {\n docChanged: { earlyReturnValue: false }, // Execute all handlers, even if one returns false\n },\n defaultPriority: ExtensionPriority.Lowest,\n})\nexport class DocChangedExtension extends PlainExtension<DocChangedOptions> {\n get name() {\n return 'docChanged' as const;\n }\n\n onStateUpdate(props: StateUpdateLifecycleProps): void {\n const { firstUpdate, transactions, tr } = props;\n\n if (firstUpdate) {\n return;\n }\n\n if ((transactions ?? [tr]).some((tr) => tr?.docChanged)) {\n this.options.docChanged(props);\n }\n }\n}\n", "import { ErrorConstant, NULL_CHARACTER } from '@remirror/core-constants';\nimport { entries, isEmptyObject, object } from '@remirror/core-helpers';\nimport type {\n AnyFunction,\n CommandFunction,\n EditorState,\n EditorStateProps,\n EmptyShape,\n Fragment,\n LiteralUnion,\n ProsemirrorAttributes,\n ProsemirrorNode,\n RemirrorJSON,\n Shape,\n StateJSON,\n} from '@remirror/core-types';\nimport {\n containsAttributes,\n FragmentStringHandlerOptions,\n getActiveNode,\n getMarkRange,\n htmlToProsemirrorNode,\n isMarkActive,\n isNodeActive,\n isSelectionEmpty,\n NodeStringHandlerOptions,\n prosemirrorNodeToHtml,\n StringHandlerOptions,\n} from '@remirror/core-utils';\n\nimport {\n ActiveFromExtensions,\n AnyExtension,\n AttrsFromExtensions,\n extension,\n Helper,\n HelperNames,\n HelpersFromExtensions,\n isMarkExtension,\n isNodeExtension,\n PlainExtension,\n} from '../extension';\nimport { throwIfNameNotUnique } from '../helpers';\nimport type { ExtensionHelperReturn } from '../types';\nimport { command, helper, HelperDecoratorOptions } from './builtin-decorators';\nimport { InsertNodeOptions } from './commands-extension';\n\n/**\n * Helpers are custom methods that can provide extra functionality to the\n * editor.\n *\n * @remarks\n *\n * They can be used for pulling information from the editor or performing custom\n * async commands.\n *\n * Also provides the default helpers used within the extension.\n *\n * @category Builtin Extension\n */\n@extension({})\nexport class HelpersExtension extends PlainExtension {\n get name() {\n return 'helpers' as const;\n }\n\n /**\n * Add the `html` and `text` string handlers to the editor.\n */\n onCreate(): void {\n this.store.setStringHandler('text', this.textToProsemirrorNode.bind(this));\n this.store.setStringHandler('html', htmlToProsemirrorNode);\n\n const helpers: Record<string, AnyFunction> = object();\n const active: Record<string, AnyFunction> = object();\n const attrs: Record<string, AnyFunction> = object();\n const names = new Set<string>();\n\n for (const extension of this.store.extensions) {\n if (isNodeExtension(extension)) {\n active[extension.name] = (attrs?: ProsemirrorAttributes) => {\n return isNodeActive({ state: this.store.getState(), type: extension.type, attrs });\n };\n\n attrs[extension.name] = (attrs?: ProsemirrorAttributes) => {\n return getActiveNode({ state: this.store.getState(), type: extension.type, attrs })?.node\n .attrs;\n };\n }\n\n if (isMarkExtension(extension)) {\n active[extension.name] = (attrs?: ProsemirrorAttributes) => {\n return isMarkActive({ trState: this.store.getState(), type: extension.type, attrs });\n };\n\n attrs[extension.name] = (attrs?: ProsemirrorAttributes) => {\n const markRange = getMarkRange(this.store.getState().selection.$from, extension.type);\n\n if (!markRange || !attrs) {\n return markRange?.mark.attrs;\n }\n\n if (containsAttributes(markRange.mark, attrs)) {\n return markRange.mark.attrs;\n }\n\n return;\n };\n }\n\n const extensionHelpers = extension.createHelpers?.() ?? {};\n\n for (const helperName of Object.keys(extension.decoratedHelpers ?? {})) {\n extensionHelpers[helperName] = (extension as Shape)[helperName].bind(extension);\n }\n\n if (isEmptyObject(extensionHelpers)) {\n continue;\n }\n\n for (const [name, helper] of entries(extensionHelpers)) {\n throwIfNameNotUnique({ name, set: names, code: ErrorConstant.DUPLICATE_HELPER_NAMES });\n helpers[name] = helper;\n }\n }\n\n this.store.setStoreKey('attrs', attrs);\n this.store.setStoreKey('active', active);\n this.store.setStoreKey('helpers', helpers as any);\n this.store.setExtensionStore('attrs', attrs);\n this.store.setExtensionStore('active', active);\n this.store.setExtensionStore('helpers', helpers as any);\n }\n\n /**\n * Check whether the selection is empty.\n */\n @helper()\n isSelectionEmpty(state: EditorState = this.store.getState()): Helper<boolean> {\n return isSelectionEmpty(state);\n }\n\n /*\n * Check if the document view is currently editable.\n */\n @helper()\n isViewEditable(state: EditorState = this.store.getState()): Helper<boolean> {\n return this.store.view.props.editable?.(state) ?? false;\n }\n\n /**\n * Get the full JSON output for the ProseMirror editor state object.\n */\n @helper()\n getStateJSON(state: EditorState = this.store.getState()): Helper<StateJSON> {\n return state.toJSON() as StateJSON;\n }\n\n /**\n * Get the JSON output for the main ProseMirror `doc` node.\n *\n * This can be used to persist data between sessions and can be passed as\n * content to the `initialContent` prop.\n */\n @helper()\n getJSON(state: EditorState = this.store.getState()): Helper<RemirrorJSON> {\n return state.doc.toJSON() as RemirrorJSON;\n }\n\n /**\n * @deprecated use `getJSON` instead.\n */\n @helper()\n getRemirrorJSON(state: EditorState = this.store.getState()): Helper<RemirrorJSON> {\n return this.getJSON(state);\n }\n\n /**\n * Insert a html string as a ProseMirror Node.\n *\n * @category Builtin Command\n */\n @command()\n insertHtml(html: string, options?: InsertNodeOptions): CommandFunction {\n return (props) => {\n const { state } = props;\n const fragment = htmlToProsemirrorNode({\n content: html,\n schema: state.schema,\n fragment: true,\n });\n\n return this.store.commands.insertNode.original(fragment, options)(props);\n };\n }\n\n /**\n * A method to get all the content in the editor as text. Depending on the\n * content in your editor, it is not guaranteed to preserve it 100%, so it's\n * best to test that it meets your needs before consuming.\n */\n @helper()\n getText({\n lineBreakDivider = '\\n\\n',\n state = this.store.getState(),\n }: GetTextHelperOptions = {}): Helper<string> {\n return state.doc.textBetween(0, state.doc.content.size, lineBreakDivider, NULL_CHARACTER);\n }\n\n @helper()\n getTextBetween(\n from: number,\n to: number,\n doc: ProsemirrorNode = this.store.getState().doc,\n ): Helper<string> {\n return doc.textBetween(from, to, '\\n\\n', NULL_CHARACTER);\n }\n\n /**\n * Get the html from the current state, or provide a custom state.\n */\n @helper()\n getHTML(state: EditorState = this.store.getState()): Helper<string> {\n return prosemirrorNodeToHtml(state.doc, this.store.document);\n }\n\n /**\n * Wrap the content in a pre tag to preserve whitespace and see what the\n * editor does with it.\n */\n private textToProsemirrorNode(options: FragmentStringHandlerOptions): Fragment;\n private textToProsemirrorNode(options: NodeStringHandlerOptions): ProsemirrorNode;\n private textToProsemirrorNode(options: StringHandlerOptions): ProsemirrorNode | Fragment {\n const content = `<pre>${options.content}</pre>`;\n\n return this.store.stringHandlers.html({ ...(options as NodeStringHandlerOptions), content });\n }\n}\n\ninterface GetTextHelperOptions extends Partial<EditorStateProps> {\n /**\n * The divider used to separate text blocks.\n *\n * @defaultValue '\\n\\n'\n */\n lineBreakDivider?: string;\n}\n\ndeclare global {\n namespace Remirror {\n interface ManagerStore<Extension extends AnyExtension> {\n /**\n * The helpers provided by the extensions used.\n */\n helpers: HelpersFromExtensions<Extension>;\n\n /**\n * Check which nodes and marks are active under the current user\n * selection.\n *\n * ```ts\n * const { active } = manager.store;\n *\n * return active.bold() ? 'bold' : 'regular';\n * ```\n */\n active: ActiveFromExtensions<Extension>;\n\n /**\n * Get the attributes for the named node or mark from the current user\n * selection.\n *\n * ```ts\n * const { attrs } = manager.store;\n *\n * attrs.heading(); // => { id: 'i1238ha', level: 1 }\n * ```\n */\n attrs: AttrsFromExtensions<Extension>;\n }\n\n interface BaseExtension {\n /**\n * `ExtensionHelpers`\n *\n * This pseudo property makes it easier to infer Generic types of this\n * class.\n *\n * @internal\n */\n ['~H']: this['createHelpers'] extends AnyFunction\n ? ReturnType<this['createHelpers']>\n : EmptyShape;\n\n /**\n * @experimental\n *\n * Stores all the helpers that have been added via decorators to the\n * extension instance. This is used by the `HelpersExtension` to pick the\n * helpers.\n *\n * @internal\n */\n decoratedHelpers?: Record<string, HelperDecoratorOptions>;\n\n /**\n * A helper method is a function that takes in arguments and returns a\n * value depicting the state of the editor specific to this extension.\n *\n * @remarks\n *\n * Unlike commands they can return anything and may not effect the\n * behavior of the editor.\n *\n * Below is an example which should provide some idea on how to add\n * helpers to the app.\n *\n * ```tsx\n * // extension.ts\n * import { ExtensionFactory } from '@remirror/core';\n *\n * const MyBeautifulExtension = ExtensionFactory.plain({\n * name: 'beautiful',\n * createHelpers: () => ({\n * checkBeautyLevel: () => 100\n * }),\n * })\n * ```\n *\n * ```\n * // app.tsx\n * import { useRemirrorContext } from '@remirror/react';\n *\n * const MyEditor = () => {\n * const { helpers } = useRemirrorContext({ autoUpdate: true });\n *\n * return helpers.beautiful.checkBeautyLevel() > 50\n * ? (<span>\uD83D\uDE0D</span>)\n * : (<span>\uD83D\uDE22</span>);\n * };\n * ```\n */\n createHelpers?(): ExtensionHelperReturn;\n }\n\n interface StringHandlers {\n /**\n * Register the plain `text` string handler which renders a text string\n * inside a `<pre />`.\n */\n text: HelpersExtension;\n\n /**\n * Register the html string handler, which converts a html string to a\n * prosemirror node.\n */\n html: HelpersExtension;\n }\n\n interface ExtensionStore {\n /**\n * Helper method to provide information about the content of the editor.\n * Each extension can register its own helpers.\n *\n * This should only be accessed after the `onView` lifecycle method\n * otherwise it will throw an error.\n */\n helpers: HelpersFromExtensions<Extensions>;\n\n /**\n * Check which nodes and marks are active under the current user\n * selection.\n *\n * ```ts\n * const { active } = manager.store;\n *\n * return active.bold() ? 'bold' : 'regular';\n * ```\n */\n active: ActiveFromExtensions<Extensions>;\n\n /**\n * Get the attributes for the named node or mark from the current user\n * selection.\n *\n * ```ts\n * const { attrs } = manager.store;\n *\n * attrs.heading(); // => { id: 'i1238ha', level: 1 }\n * ```\n */\n attrs: AttrsFromExtensions<Extensions>;\n }\n\n interface ListenerProperties<Extension extends AnyExtension> {\n helpers: HelpersFromExtensions<Extension>;\n }\n\n interface AllExtensions {\n helpers: HelpersExtension;\n }\n }\n\n /**\n * The helpers name for all extension defined in the current project.\n */\n type AllHelperNames = LiteralUnion<HelperNames<Remirror.Extensions>, string>;\n}\n", "import { ExtensionPriority, ExtensionTag } from '@remirror/core-constants';\nimport type { Handler, ProsemirrorPlugin } from '@remirror/core-types';\nimport type { ShouldSkipFunction, SkippableInputRule } from '@remirror/core-utils';\nimport { InputRule, inputRules } from '@remirror/pm/inputrules';\n\nimport { extension, PlainExtension } from '../extension';\n\nexport interface InputRulesOptions {\n /**\n * Handlers which can be registered to check whether an input rule should be\n * active at this time.\n *\n * The handlers are given a parameter with the current `state`, the `fullMatch`\n * and the `captureGroup` and can determine whether the input rule should\n * still be run.\n *\n * Return `true` to prevent any active input rules from being triggered.\n */\n shouldSkipInputRule?: Handler<ShouldSkipFunction>;\n}\n\n/**\n * This extension allows others extension to add the `createInputRules` method\n * for automatically transforming text when a certain regex pattern is typed.\n *\n * @remarks\n *\n * This is an example of adding custom functionality to an extension via the\n * `ExtensionParameterMethods`.\n *\n * @category Builtin Extension\n */\n@extension<InputRulesOptions>({\n defaultPriority: ExtensionPriority.Default,\n handlerKeys: ['shouldSkipInputRule'],\n\n // Return when the value `true` is encountered.\n handlerKeyOptions: { shouldSkipInputRule: { earlyReturnValue: true } },\n})\nexport class InputRulesExtension extends PlainExtension<InputRulesOptions> {\n get name() {\n return 'inputRules' as const;\n }\n\n /**\n * Add the extension store method for rebuilding all input rules.\n */\n onCreate(): void {\n this.store.setExtensionStore('rebuildInputRules', this.rebuildInputRules.bind(this));\n }\n\n /**\n * Add the `inputRules` plugin to the editor.\n */\n createExternalPlugins(): ProsemirrorPlugin[] {\n return [this.generateInputRulesPlugin()];\n }\n\n private generateInputRulesPlugin() {\n const rules: SkippableInputRule[] = [];\n const invalidMarks = this.store.markTags[ExtensionTag.ExcludeInputRules];\n\n for (const extension of this.store.extensions) {\n if (\n // managerSettings excluded this from running\n this.store.managerSettings.exclude?.inputRules ||\n // Method doesn't exist\n !extension.createInputRules ||\n // Extension settings exclude it\n extension.options.exclude?.inputRules\n ) {\n continue;\n }\n\n // For each input rule returned by the extension, add a `shouldSkip`\n // property.\n for (const rule of extension.createInputRules() as SkippableInputRule[]) {\n rule.shouldSkip = this.options.shouldSkipInputRule;\n rule.invalidMarks = invalidMarks;\n rules.push(rule);\n }\n }\n\n return inputRules({ rules });\n }\n\n /**\n * The method for rebuilding all the input rules.\n *\n * 1. Rebuild inputRules.\n * 2. Replace the old input rules plugin.\n * 3. Update the plugins used in the state (triggers an editor update).\n */\n private rebuildInputRules() {\n this.store.updateExtensionPlugins(this);\n }\n}\n\ndeclare global {\n namespace Remirror {\n interface ExcludeOptions {\n /**\n * Whether to use the inputRules for this particular extension.\n *\n * @defaultValue undefined\n */\n inputRules?: boolean;\n }\n\n interface ExtensionStore {\n /**\n * When called this will run through every `createInputRules` method on every\n * extension to recreate input rules.\n *\n * @remarks\n *\n * Under the hood it updates the plugin which is used to insert the\n * input rules into the editor. This causes the state to be updated and\n * will cause a rerender in your ui framework.\n */\n rebuildInputRules: () => void;\n }\n\n interface BaseExtension {\n /**\n * Register input rules which are activated if the regex matches as a user is\n * typing.\n *\n * @param parameter - schema parameter with type included\n */\n createInputRules?(): InputRule[];\n }\n\n interface AllExtensions {\n inputRules: InputRulesExtension;\n }\n }\n}\n", "import { ExtensionPriority, ExtensionTag, NamedShortcut } from '@remirror/core-constants';\nimport {\n entries,\n includes,\n isArray,\n isEmptyArray,\n isFunction,\n isString,\n isUndefined,\n object,\n sort,\n values,\n} from '@remirror/core-helpers';\nimport type {\n CommandFunction,\n CustomHandler,\n EditorView,\n KeyBindingProps,\n KeyBindings,\n ProsemirrorPlugin,\n Shape,\n} from '@remirror/core-types';\nimport {\n chainKeyBindingCommands,\n convertCommand,\n environment,\n findParentNodeOfType,\n isDefaultBlockNode,\n isEmptyBlockNode,\n isEndOfTextBlock,\n isStartOfDoc,\n isStartOfTextBlock,\n mergeProsemirrorKeyBindings,\n} from '@remirror/core-utils';\nimport {\n baseKeymap,\n chainCommands as pmChainCommands,\n selectParentNode,\n} from '@remirror/pm/commands';\nimport { undoInputRule } from '@remirror/pm/inputrules';\nimport { keydownHandler } from '@remirror/pm/keymap';\nimport { Plugin } from '@remirror/pm/state';\n\nimport { AnyExtension, extension, Helper, PlainExtension } from '../extension';\nimport type { AddCustomHandler } from '../extension/base-class';\nimport type { OnSetOptionsProps } from '../types';\nimport {\n helper,\n keyBinding,\n KeybindingDecoratorOptions,\n KeyboardShortcut,\n} from './builtin-decorators';\nimport { CommandsExtension } from './commands-extension';\n\nexport interface KeymapOptions {\n /**\n * The shortcuts to use for named keybindings in the editor.\n *\n * @defaultValue 'default'\n */\n shortcuts?: KeyboardShortcuts;\n\n /**\n * Determines whether a backspace after an input rule has been applied should\n * reverse the effect of the input rule.\n *\n * @defaultValue true\n */\n undoInputRuleOnBackspace?: boolean;\n\n /**\n * Determines whether the escape key selects the current node.\n *\n * @defaultValue false\n */\n selectParentNodeOnEscape?: boolean;\n\n /**\n * When true will exclude the default prosemirror keymap.\n *\n * @remarks\n *\n * You might want to set this to true if you want to fully customise the\n * keyboard mappings for your editor. Otherwise it is advisable to leave it\n * unchanged.\n *\n * @defaultValue false\n */\n excludeBaseKeymap?: boolean;\n\n /**\n * Whether to support exiting marks when the left and right array keys are\n * pressed.\n *\n * Can be set to\n *\n * - `true` - enables exits from both the entrance and the end of the mark\n */\n exitMarksOnArrowPress?: boolean;\n\n /**\n * The implementation for the extra keybindings added to the settings.\n *\n * @remarks\n *\n * This allows for you to add extra key mappings which will be checked before\n * the default keymaps, if they return false then the default keymaps are\n * still checked.\n *\n * No key mappings are removed in this process.\n *\n * ```ts\n * const extension = BaseKeymapExtension.create({ keymap: {\n * Enter({ state, dispatch }) {\n * //... Logic\n * return true;\n * },\n * }});\n * ```\n */\n keymap?: CustomHandler<PrioritizedKeyBindings>;\n}\n\n/**\n * This extension allows others extension to use the `createKeymaps` method.\n *\n * @remarks\n *\n * Keymaps are the way of controlling how the editor responds to a keypress and\n * different key combinations.\n *\n * Without this extension most of the shortcuts and behaviors we have come to\n * expect from text editors would not be provided.\n *\n * @category Builtin Extension\n */\n@extension<KeymapOptions>({\n defaultPriority: ExtensionPriority.Low,\n defaultOptions: {\n shortcuts: 'default',\n undoInputRuleOnBackspace: true,\n selectParentNodeOnEscape: false,\n excludeBaseKeymap: false,\n exitMarksOnArrowPress: true,\n },\n customHandlerKeys: ['keymap'],\n})\nexport class KeymapExtension extends PlainExtension<KeymapOptions> {\n get name() {\n return 'keymap' as const;\n }\n\n /**\n * The custom keybindings added by the handlers. In react these can be added\n * via `hooks`.\n */\n private extraKeyBindings: PrioritizedKeyBindings[] = [];\n\n /**\n * Track the backward exits from a mark to allow double tapping the left arrow\n * to move to the previous block node.\n */\n private readonly backwardMarkExitTracker = new Map<number, boolean>();\n\n /**\n * The underlying keydown handler.\n */\n private keydownHandler: ((view: EditorView, event: KeyboardEvent) => boolean) | null = null;\n\n /**\n * Get the shortcut map.\n */\n private get shortcutMap(): ShortcutMap {\n const { shortcuts } = this.options;\n return isString(shortcuts) ? keyboardShortcuts[shortcuts] : shortcuts;\n }\n\n /**\n * This adds the `createKeymap` method functionality to all extensions.\n */\n override onCreate(): void {\n this.store.setExtensionStore('rebuildKeymap', this.rebuildKeymap);\n }\n\n /** Add the created keymap to the available plugins. */\n override createExternalPlugins(): ProsemirrorPlugin[] {\n if (\n // The user doesn't want any keymaps in the editor so don't add the keymap\n // handler.\n this.store.managerSettings.exclude?.keymap\n ) {\n return [];\n }\n\n this.setupKeydownHandler();\n\n return [\n new Plugin({\n props: {\n handleKeyDown: (view, event) => {\n return this.keydownHandler?.(view, event);\n },\n },\n }),\n ];\n }\n\n private setupKeydownHandler() {\n const bindings = this.generateKeymapBindings();\n this.keydownHandler = keydownHandler(bindings);\n }\n\n /**\n * Updates the stored keymap bindings on this extension.\n */\n private generateKeymapBindings() {\n const extensionKeymaps: PrioritizedKeyBindings[] = [];\n const shortcutMap = this.shortcutMap;\n const commandsExtension = this.store.getExtension(CommandsExtension);\n const extractNamesFactory = (extension: AnyExtension) => (shortcut: string) =>\n extractShortcutNames({\n shortcut,\n map: shortcutMap,\n store: this.store,\n options: extension.options,\n });\n\n for (const extension of this.store.extensions) {\n const decoratedKeybindings = extension.decoratedKeybindings ?? {};\n\n if (\n // The extension was configured to ignore the keymap.\n extension.options.exclude?.keymap\n ) {\n continue;\n }\n\n if (\n // The extension doesn't have the `createKeymap` method.\n extension.createKeymap\n ) {\n extensionKeymaps.push(\n updateNamedKeys(extension.createKeymap(extractNamesFactory(extension)), shortcutMap),\n );\n }\n\n for (const [name, options] of entries(decoratedKeybindings)) {\n if (options.isActive && !options.isActive(extension.options, this.store)) {\n continue;\n }\n\n // Bind the keybinding function to the extension.\n const keyBinding = (extension as Shape)[name].bind(extension);\n\n // Extract the keypress pattern.\n const shortcutNames = extractShortcutNames({\n shortcut: options.shortcut,\n map: shortcutMap,\n options: extension.options,\n store: this.store,\n });\n\n // Decide the priority to assign to the keymap.\n const priority = isFunction(options.priority)\n ? options.priority(extension.options, this.store)\n : options.priority ?? ExtensionPriority.Low;\n\n const bindingObject: KeyBindings = object();\n\n for (const shortcut of shortcutNames) {\n bindingObject[shortcut] = keyBinding;\n }\n\n extensionKeymaps.push([priority, bindingObject]);\n\n // Attach the normalized shortcut to the decorated command so that is\n // can be referenced in the UI.\n if (options.command) {\n commandsExtension.updateDecorated(options.command, { shortcut: shortcutNames });\n }\n }\n }\n\n // Sort the keymaps with a priority given to keymaps added via\n // `extension.addHandler` (e.g. in hooks).\n const sortedKeymaps = this.sortKeymaps([...this.extraKeyBindings, ...extensionKeymaps]);\n const mappedCommands = mergeProsemirrorKeyBindings(sortedKeymaps);\n\n return mappedCommands;\n }\n\n /**\n * Handle exiting the mark forwards.\n */\n @keyBinding<KeymapExtension>({\n shortcut: 'ArrowRight',\n isActive: (options) => options.exitMarksOnArrowPress,\n })\n arrowRightShortcut(props: KeyBindingProps): boolean {\n const excludedMarks = this.store.markTags[ExtensionTag.PreventExits];\n const excludedNodes = this.store.nodeTags[ExtensionTag.PreventExits];\n\n return this.exitMarkForwards(excludedMarks, excludedNodes)(props);\n }\n\n /**\n * Handle the arrow left key to exit the mark.\n */\n @keyBinding<KeymapExtension>({\n shortcut: 'ArrowLeft',\n isActive: (options) => options.exitMarksOnArrowPress,\n })\n arrowLeftShortcut(props: KeyBindingProps): boolean {\n const excludedMarks = this.store.markTags[ExtensionTag.PreventExits];\n const excludedNodes = this.store.nodeTags[ExtensionTag.PreventExits];\n return chainKeyBindingCommands(\n this.exitNodeBackwards(excludedNodes),\n this.exitMarkBackwards(excludedMarks, excludedNodes),\n )(props);\n }\n\n /**\n * Handle exiting the mark forwards.\n */\n @keyBinding<KeymapExtension>({\n shortcut: 'Backspace',\n isActive: (options) => options.exitMarksOnArrowPress,\n })\n backspace(props: KeyBindingProps): boolean {\n const excludedMarks = this.store.markTags[ExtensionTag.PreventExits];\n const excludedNodes = this.store.nodeTags[ExtensionTag.PreventExits];\n return chainKeyBindingCommands(\n this.exitNodeBackwards(excludedNodes, true),\n this.exitMarkBackwards(excludedMarks, excludedNodes, true),\n )(props);\n }\n\n /**\n * Create the base keymap and give it a low priority so that all other keymaps\n * override it.\n */\n override createKeymap(): PrioritizedKeyBindings {\n const { selectParentNodeOnEscape, undoInputRuleOnBackspace, excludeBaseKeymap } = this.options;\n const baseKeyBindings: KeyBindings = object();\n\n // Only add the base keymap if it is **NOT** excluded.\n if (!excludeBaseKeymap) {\n for (const [key, value] of entries(baseKeymap)) {\n baseKeyBindings[key] = convertCommand(value);\n }\n }\n\n // Automatically remove the input rule when the option is set to true.\n if (undoInputRuleOnBackspace && baseKeymap.Backspace) {\n baseKeyBindings.Backspace = convertCommand(\n pmChainCommands(undoInputRule, baseKeymap.Backspace),\n );\n }\n\n // Allow escape to select the parent node when set to true.\n if (selectParentNodeOnEscape) {\n baseKeyBindings.Escape = convertCommand(selectParentNode);\n }\n\n return [ExtensionPriority.Low, baseKeyBindings];\n }\n\n /**\n * Get the real shortcut name from the named shortcut.\n */\n @helper()\n getNamedShortcut(shortcut: string, options: Shape = {}): Helper<string[]> {\n if (!shortcut.startsWith('_|')) {\n return [shortcut];\n }\n\n return extractShortcutNames({\n shortcut,\n map: this.shortcutMap,\n store: this.store,\n options: options,\n });\n }\n\n /**\n * @internalremarks\n *\n * Think about the case where bindings are disposed of and then added in a\n * different position in the `extraKeyBindings` array. This is especially\n * pertinent when using hooks.\n */\n protected override onAddCustomHandler: AddCustomHandler<KeymapOptions> = ({ keymap }) => {\n if (!keymap) {\n return;\n }\n\n this.extraKeyBindings = [...this.extraKeyBindings, keymap];\n this.store.rebuildKeymap?.();\n\n return () => {\n this.extraKeyBindings = this.extraKeyBindings.filter((binding) => binding !== keymap);\n this.store.rebuildKeymap?.();\n };\n };\n\n /**\n * Handle changes in the dynamic properties.\n */\n protected override onSetOptions(props: OnSetOptionsProps<KeymapOptions>): void {\n const { changes } = props;\n\n if (\n changes.excludeBaseKeymap.changed ||\n changes.selectParentNodeOnEscape.changed ||\n changes.undoInputRuleOnBackspace.changed\n ) {\n this.store.rebuildKeymap?.();\n }\n }\n\n private sortKeymaps(bindings: PrioritizedKeyBindings[]): KeyBindings[] {\n // Sort the bindings.\n return sort(\n bindings.map((binding) =>\n // Make all bindings prioritized a default priority of\n // `ExtensionPriority.Default`\n isArray(binding) ? binding : ([ExtensionPriority.Default, binding] as const),\n ),\n // Sort from highest binding to the lowest.\n (a, z) => z[0] - a[0],\n // Extract the bindings from the prioritized tuple.\n ).map((binding) => binding[1]);\n }\n\n /**\n * The method for rebuilding all the extension keymaps.\n *\n * 1. Rebuild keymaps.\n * 2. Replace `this.keydownHandler` with the new keydown handler.\n */\n private readonly rebuildKeymap = () => {\n this.setupKeydownHandler();\n };\n\n /**\n * Exits the mark forwards when at the end of a block node.\n */\n private exitMarkForwards(excludedMarks: string[], excludedNodes: string[]): CommandFunction {\n return (props) => {\n const { tr, dispatch } = props;\n\n if (!isEndOfTextBlock(tr.selection)) {\n return false;\n }\n\n const isInsideExcludedNode = findParentNodeOfType({\n selection: tr.selection,\n types: excludedNodes,\n });\n\n if (isInsideExcludedNode) {\n return false;\n }\n\n const $pos = tr.selection.$from;\n const marksToRemove = $pos.marks().filter((mark) => !excludedMarks.includes(mark.type.name));\n\n if (isEmptyArray(marksToRemove)) {\n return false;\n }\n\n if (!dispatch) {\n return true;\n }\n\n for (const mark of marksToRemove) {\n tr.removeStoredMark(mark);\n }\n\n dispatch(tr.insertText(' ', tr.selection.from));\n\n return true;\n };\n }\n\n private exitNodeBackwards(excludedNodes: string[], startOfDoc = false): CommandFunction {\n return (props) => {\n const { tr } = props;\n const checker = startOfDoc ? isStartOfDoc : isStartOfTextBlock;\n\n if (!checker(tr.selection)) {\n return false;\n }\n\n const node = tr.selection.$anchor.node();\n\n if (\n !isEmptyBlockNode(node) ||\n isDefaultBlockNode(node) ||\n excludedNodes.includes(node.type.name)\n ) {\n return false;\n }\n\n return this.store.commands.toggleBlockNodeItem.original({ type: node.type })(props);\n };\n }\n\n /**\n * Exit a mark when at the beginning of a block node.\n */\n private exitMarkBackwards(\n excludedMarks: string[],\n excludedNodes: string[],\n startOfDoc = false,\n ): CommandFunction {\n return (props) => {\n const { tr, dispatch } = props;\n const checker = startOfDoc ? isStartOfDoc : isStartOfTextBlock;\n\n if (!checker(tr.selection) || this.backwardMarkExitTracker.has(tr.selection.anchor)) {\n // Clear the map to prevent it storing stale data.\n this.backwardMarkExitTracker.clear();\n return false;\n }\n\n const isInsideExcludedNode = findParentNodeOfType({\n selection: tr.selection,\n types: excludedNodes,\n });\n\n if (isInsideExcludedNode) {\n return false;\n }\n\n // Find all the marks to remove\n const marksToRemove = [...(tr.storedMarks ?? []), ...tr.selection.$from.marks()].filter(\n (mark) => !excludedMarks.includes(mark.type.name),\n );\n\n if (isEmptyArray(marksToRemove)) {\n return false;\n }\n\n if (!dispatch) {\n return true;\n }\n\n // Remove all the active marks at the current cursor.\n for (const mark of marksToRemove) {\n tr.removeStoredMark(mark);\n }\n\n this.backwardMarkExitTracker.set(tr.selection.anchor, true);\n\n dispatch(tr);\n return true;\n };\n }\n}\n\nfunction isNamedShortcut(value: string): value is NamedShortcut {\n return includes(values(NamedShortcut), value);\n}\n\ninterface ExtractShortcutNamesProps {\n shortcut: KeyboardShortcut;\n map: ShortcutMap;\n options: Shape;\n store: Remirror.ExtensionStore;\n}\n\nfunction extractShortcutNames({\n shortcut,\n map,\n options,\n store,\n}: ExtractShortcutNamesProps): string[] {\n if (isString(shortcut)) {\n return [normalizeShortcutName(shortcut, map)];\n }\n\n if (isArray(shortcut)) {\n return shortcut.map((value) => normalizeShortcutName(value, map));\n }\n\n shortcut = shortcut(options, store);\n return extractShortcutNames({ shortcut, map, options, store });\n}\n\nfunction normalizeShortcutName(value: string, shortcutMap: ShortcutMap): string {\n return isNamedShortcut(value) ? shortcutMap[value] : value;\n}\n\nfunction updateNamedKeys(\n prioritizedBindings: PrioritizedKeyBindings,\n shortcutMap: ShortcutMap,\n): PrioritizedKeyBindings {\n const updatedBindings: KeyBindings = {};\n let previousBindings: KeyBindings;\n let priority: ExtensionPriority | undefined;\n\n if (isArray(prioritizedBindings)) {\n [priority, previousBindings] = prioritizedBindings;\n } else {\n previousBindings = prioritizedBindings;\n }\n\n for (const [shortcutName, commandFunction] of entries(previousBindings)) {\n updatedBindings[normalizeShortcutName(shortcutName, shortcutMap)] = commandFunction;\n }\n\n return isUndefined(priority) ? updatedBindings : [priority, updatedBindings];\n}\n\n/**\n * A shortcut map which is used by the `KeymapExtension`.\n */\nexport type ShortcutMap = Record<NamedShortcut, string>;\n\n/**\n * The default named shortcuts used within `remirror`.\n */\nexport const DEFAULT_SHORTCUTS: ShortcutMap = {\n [NamedShortcut.Copy]: 'Mod-c',\n [NamedShortcut.Cut]: 'Mod-x',\n [NamedShortcut.Paste]: 'Mod-v',\n [NamedShortcut.PastePlain]: 'Mod-Shift-v',\n [NamedShortcut.SelectAll]: 'Mod-a',\n [NamedShortcut.Undo]: 'Mod-z',\n [NamedShortcut.Redo]: environment.isMac ? 'Shift-Mod-z' : 'Mod-y',\n [NamedShortcut.Bold]: 'Mod-b',\n [NamedShortcut.Italic]: 'Mod-i',\n [NamedShortcut.Underline]: 'Mod-u',\n [NamedShortcut.Strike]: 'Mod-d',\n [NamedShortcut.Code]: 'Mod-`',\n [NamedShortcut.Paragraph]: 'Mod-Shift-0',\n [NamedShortcut.H1]: 'Mod-Shift-1',\n [NamedShortcut.H2]: 'Mod-Shift-2',\n [NamedShortcut.H3]: 'Mod-Shift-3',\n [NamedShortcut.H4]: 'Mod-Shift-4',\n [NamedShortcut.H5]: 'Mod-Shift-5',\n [NamedShortcut.H6]: 'Mod-Shift-6',\n [NamedShortcut.TaskList]: 'Mod-Shift-7',\n [NamedShortcut.BulletList]: 'Mod-Shift-8',\n [NamedShortcut.OrderedList]: 'Mod-Shift-9',\n [NamedShortcut.Quote]: 'Mod->',\n [NamedShortcut.Divider]: 'Mod-Shift-|',\n [NamedShortcut.Codeblock]: 'Mod-Shift-~',\n [NamedShortcut.ClearFormatting]: 'Mod-Shift-C',\n [NamedShortcut.Superscript]: 'Mod-.',\n [NamedShortcut.Subscript]: 'Mod-,',\n [NamedShortcut.LeftAlignment]: 'Mod-Shift-L',\n [NamedShortcut.CenterAlignment]: 'Mod-Shift-E',\n [NamedShortcut.RightAlignment]: 'Mod-Shift-R',\n [NamedShortcut.JustifyAlignment]: 'Mod-Shift-J',\n [NamedShortcut.InsertLink]: 'Mod-k',\n [NamedShortcut.Find]: 'Mod-f',\n [NamedShortcut.FindBackwards]: 'Mod-Shift-f',\n [NamedShortcut.FindReplace]: 'Mod-Shift-H',\n [NamedShortcut.AddFootnote]: 'Mod-Alt-f',\n [NamedShortcut.AddComment]: 'Mod-Alt-m',\n [NamedShortcut.ContextMenu]: 'Mod-Shift-\\\\',\n [NamedShortcut.IncreaseFontSize]: 'Mod-Shift-.',\n [NamedShortcut.DecreaseFontSize]: 'Mod-Shift-,',\n [NamedShortcut.IncreaseIndent]: 'Tab',\n [NamedShortcut.DecreaseIndent]: 'Shift-Tab',\n [NamedShortcut.Shortcuts]: 'Mod-/',\n [NamedShortcut.Format]: environment.isMac ? 'Alt-Shift-f' : 'Shift-Ctrl-f',\n};\n\n/**\n * Shortcuts used within google docs.\n */\nexport const GOOGLE_DOC_SHORTCUTS: ShortcutMap = {\n ...DEFAULT_SHORTCUTS,\n [NamedShortcut.Strike]: 'Mod-Shift-S',\n [NamedShortcut.Code]: 'Mod-Shift-M',\n [NamedShortcut.Paragraph]: 'Mod-Alt-0',\n [NamedShortcut.H1]: 'Mod-Alt-1',\n [NamedShortcut.H2]: 'Mod-Alt-2',\n [NamedShortcut.H3]: 'Mod-Alt-3',\n [NamedShortcut.H4]: 'Mod-Alt-4',\n [NamedShortcut.H5]: 'Mod-Alt-5',\n [NamedShortcut.H6]: 'Mod-Alt-6',\n [NamedShortcut.OrderedList]: 'Mod-Alt-7',\n [NamedShortcut.BulletList]: 'Mod-Alt-8',\n [NamedShortcut.Quote]: 'Mod-Alt-9',\n [NamedShortcut.ClearFormatting]: 'Mod-\\\\',\n [NamedShortcut.IncreaseIndent]: 'Mod-[',\n [NamedShortcut.DecreaseIndent]: 'Mod-]',\n};\n\nexport const keyboardShortcuts = {\n default: DEFAULT_SHORTCUTS,\n googleDoc: GOOGLE_DOC_SHORTCUTS,\n};\n\nexport type KeyboardShortcuts = keyof typeof keyboardShortcuts | ShortcutMap;\n\n/**\n * KeyBindings as a tuple with priority and the keymap.\n */\nexport type KeyBindingsTuple = [priority: ExtensionPriority, bindings: KeyBindings];\n\n/**\n * `KeyBindings` as an object or prioritized tuple.\n */\nexport type PrioritizedKeyBindings = KeyBindings | KeyBindingsTuple;\n\ndeclare global {\n namespace Remirror {\n interface ExcludeOptions {\n /**\n * Whether to exclude keybindings support. This is not a recommended\n * action and can break functionality.\n *\n * @defaultValue undefined\n */\n keymap?: boolean;\n }\n\n interface ExtensionStore {\n /**\n * When called this will run through every `createKeymap` method on every\n * extension to recreate the keyboard bindings.\n *\n * @remarks\n *\n * **NOTE** - This will not update keybinding for extensions that\n * implement their own keybinding functionality (e.g. any plugin using\n * Suggestions)\n */\n rebuildKeymap: () => void;\n }\n\n interface BaseExtension {\n /**\n * Stores all the keybinding names and options for this decoration that\n * have been added as decorators to the extension instance. This is used\n * by the `KeymapExtension` to pick the commands and store metadata\n * attached to each command.\n *\n * @internal\n */\n decoratedKeybindings?: Record<string, KeybindingDecoratorOptions>;\n\n /**\n * Add keymap bindings for this extension.\n *\n * @param parameter - schema parameter with type included\n */\n createKeymap?(extractShortcutNames: (shortcut: string) => string[]): PrioritizedKeyBindings;\n }\n\n interface AllExtensions {\n keymap: KeymapExtension;\n }\n }\n}\n", "import { isFunction, object } from '@remirror/core-helpers';\nimport type { NodeViewMethod } from '@remirror/core-types';\n\nimport { PlainExtension } from '../extension';\nimport type { CreateExtensionPlugin } from '../types';\n\n/**\n * This extension allows others extension to add the `createNodeView` method\n * for creating nodeViews which alter how the dom is rendered for the node.\n *\n * @remarks\n *\n * This is an example of adding custom functionality to an extension via the\n * `ExtensionParameterMethods`.\n *\n * @category Builtin Extension\n */\nexport class NodeViewsExtension extends PlainExtension {\n get name() {\n return 'nodeViews' as const;\n }\n\n createPlugin(): CreateExtensionPlugin {\n const nodeViewList: Array<Record<string, NodeViewMethod>> = [];\n const nodeViews: Record<string, NodeViewMethod> = object();\n\n for (const extension of this.store.extensions) {\n if (!extension.createNodeViews) {\n // Method doesn't exist\n continue;\n }\n\n const nodeView = extension.createNodeViews();\n\n // `.unshift` ensures higher priority extensions can overwrite the lower\n // priority nodeViews.\n nodeViewList.unshift(isFunction(nodeView) ? { [extension.name]: nodeView } : nodeView);\n }\n\n // Insert the `nodeViews` provided via the manager.\n nodeViewList.unshift(this.store.managerSettings.nodeViews ?? {});\n\n for (const nodeView of nodeViewList) {\n Object.assign(nodeViews, nodeView);\n }\n\n return {\n props: { nodeViews },\n };\n }\n}\n\ndeclare global {\n namespace Remirror {\n interface ManagerSettings {\n /**\n * Add custom node views to the manager which will take priority over the\n * nodeViews provided by the extensions and plugins.\n */\n nodeViews?: Record<string, NodeViewMethod>;\n }\n\n interface BaseExtension {\n /**\n * Registers one or multiple nodeViews for the extension.\n *\n * This is a shorthand way of registering a nodeView without the need to\n * create a prosemirror plugin. It allows for the registration of one nodeView\n * which has the same name as the extension.\n *\n * To register more than one you would need to use a custom plugin returned\n * from the `plugin` method.\n *\n * @param parameter - schema parameter with type included\n */\n createNodeViews?(): NodeViewMethod | Record<string, NodeViewMethod>;\n }\n\n interface AllExtensions {\n nodeViews: NodeViewsExtension;\n }\n }\n}\n", "import { isArray } from '@remirror/core-helpers';\nimport type { ProsemirrorPlugin } from '@remirror/core-types';\nimport { PasteRule, pasteRules } from '@remirror/pm/paste-rules';\n\nimport { PlainExtension } from '../extension';\n\nexport interface PasteRulesOptions {}\n\n/**\n * This extension allows others extension to add the `createPasteRules` method\n * for automatically transforming pasted text which matches a certain regex\n * pattern in the dom.\n *\n * @category Builtin Extension\n */\nexport class PasteRulesExtension extends PlainExtension {\n get name() {\n return 'pasteRules' as const;\n }\n\n createExternalPlugins(): ProsemirrorPlugin[] {\n return [this.generatePasteRulesPlugin()];\n }\n\n private generatePasteRulesPlugin() {\n const extensionPasteRules: PasteRule[] = [];\n\n for (const extension of this.store.extensions) {\n if (\n // managerSettings excluded this from running\n this.store.managerSettings.exclude?.pasteRules ||\n // Method doesn't exist\n !extension.createPasteRules ||\n // Extension settings exclude it\n extension.options.exclude?.pasteRules\n ) {\n continue;\n }\n\n const value = extension.createPasteRules();\n const rules = isArray(value) ? value : [value];\n\n extensionPasteRules.push(...rules);\n }\n\n return pasteRules(extensionPasteRules);\n }\n}\n\ndeclare global {\n namespace Remirror {\n interface ExcludeOptions {\n /**\n * Whether to exclude the extension's pasteRules\n *\n * @defaultValue undefined\n */\n pasteRules?: boolean;\n }\n\n interface BaseExtension {\n /**\n * Register paste rules for this extension.\n *\n * Paste rules are activated when text, images, or html is pasted into the\n * editor.\n */\n createPasteRules?(): PasteRule[] | PasteRule;\n }\n\n interface AllExtensions {\n pasteRules: PasteRulesExtension;\n }\n }\n}\n", "import { ErrorConstant, ExtensionPriority, ManagerPhase } from '@remirror/core-constants';\nimport { assertGet, invariant, isEmptyArray, object } from '@remirror/core-helpers';\nimport type { Handler, ProsemirrorPlugin } from '@remirror/core-types';\nimport { EditorState, Plugin, PluginKey } from '@remirror/pm/state';\n\nimport {\n AnyExtension,\n AnyExtensionConstructor,\n extension,\n GetNameUnion,\n isExtension,\n isExtensionConstructor,\n PlainExtension,\n} from '../extension';\nimport type {\n AppendLifecycleProps,\n ApplyStateLifecycleProps,\n CreateExtensionPlugin,\n} from '../types';\n\nexport interface PluginsOptions {\n /**\n * The event handler which can be used by hooks to listen to state updates\n * when they are being applied to the editor.\n */\n applyState?: Handler<(props: ApplyStateLifecycleProps) => void>;\n\n /**\n * The event handler which can be used by hooks to listen to intercept updates\n * to the transaction.\n */\n appendTransaction?: Handler<(props: AppendLifecycleProps) => void>;\n}\n\n/**\n * This extension allows others extension to add the `createPlugin` method using\n * Prosemirror Plugins.\n *\n * @remarks\n *\n * This is an example of adding custom functionality to an extension via the\n * `ExtensionParameterMethods`.\n *\n * @category Builtin Extension\n */\n@extension<PluginsOptions>({\n defaultPriority: ExtensionPriority.Highest,\n handlerKeys: ['applyState', 'appendTransaction'],\n})\nexport class PluginsExtension extends PlainExtension<PluginsOptions> {\n get name() {\n return 'plugins' as const;\n }\n\n /**\n * All plugins created by other extension as well.\n */\n private plugins: ProsemirrorPlugin[] = [];\n\n /**\n * The plugins added via the manager (for reference only).\n */\n private managerPlugins: ProsemirrorPlugin[] = [];\n\n /**\n * Called when the state is is being applied after an update.\n */\n private readonly applyStateHandlers: Array<(props: ApplyStateLifecycleProps) => void> = [];\n\n /**\n * Called when the state is first initialized.\n */\n private readonly initStateHandlers: Array<(state: EditorState) => void> = [];\n\n /**\n * Handlers for the `onAppendTransaction` lifecycle method.\n */\n private readonly appendTransactionHandlers: Array<(props: AppendLifecycleProps) => void> = [];\n\n /**\n * Store the plugin keys.\n */\n private readonly pluginKeys: Record<string, PluginKey> = object();\n\n /**\n * Store state getters for the extension.\n */\n private readonly stateGetters = new Map<\n string | AnyExtensionConstructor,\n <State = unknown>() => State\n >();\n\n /**\n * This extension is responsible for adding state to the editor.\n */\n onCreate(): void {\n const { setStoreKey, setExtensionStore, managerSettings, extensions } = this.store;\n this.updateExtensionStore();\n\n // Retrieve the plugins passed in when creating the manager.\n const { plugins = [] } = managerSettings;\n\n // Add the plugins which were added directly to the manager.\n this.updatePlugins(plugins, this.managerPlugins);\n\n for (const extension of extensions) {\n if (extension.onApplyState) {\n this.applyStateHandlers.push(extension.onApplyState.bind(extension));\n }\n\n if (extension.onInitState) {\n this.initStateHandlers.push(extension.onInitState.bind(extension));\n }\n\n if (extension.onAppendTransaction) {\n this.appendTransactionHandlers.push(extension.onAppendTransaction.bind(extension));\n }\n\n this.extractExtensionPlugins(extension);\n }\n\n // Store the added plugins for future usage.\n this.managerPlugins = plugins;\n\n // Add all the extracted plugins to the manager store. From the manager\n // store they are automatically added to the state for use in the editor.\n this.store.setStoreKey('plugins', this.plugins);\n\n // Here set the plugins keys and state getters for retrieving plugin state.\n // These methods are later used.\n setStoreKey('pluginKeys', this.pluginKeys);\n setStoreKey('getPluginState', this.getStateByName);\n setExtensionStore('getPluginState', this.getStateByName);\n }\n\n /**\n * Create a plugin which adds the [[`onInitState`]] and [[`onApplyState`]]\n * lifecycle methods.\n */\n createPlugin(): CreateExtensionPlugin<void> {\n return {\n appendTransaction: (transactions, previousState, state) => {\n const tr = state.tr;\n const props = { previousState, tr, transactions, state };\n\n for (const handler of this.appendTransactionHandlers) {\n handler(props);\n }\n\n this.options.appendTransaction(props);\n\n // Return the transaction if it has been amended in any way.\n return tr.docChanged || tr.steps.length > 0 || tr.selectionSet || tr.storedMarksSet\n ? tr\n : undefined;\n },\n state: {\n init: (_, state) => {\n for (const handler of this.initStateHandlers) {\n handler(state);\n }\n },\n apply: (tr, _, previousState, state) => {\n const props = { previousState, state, tr };\n\n for (const handler of this.applyStateHandlers) {\n handler(props);\n }\n\n this.options.applyState(props);\n },\n },\n };\n }\n\n /**\n * Get all the plugins from the extension.\n */\n private extractExtensionPlugins(extension: AnyExtension) {\n const isNotPluginCreator = !extension.createPlugin && !extension.createExternalPlugins;\n\n if (\n isNotPluginCreator ||\n // the manager settings don't exclude plugins\n this.store.managerSettings.exclude?.plugins ||\n // The extension settings don't exclude plugins\n extension.options.exclude?.plugins\n ) {\n return;\n }\n\n // Create the custom plugin if it exists.\n if (extension.createPlugin) {\n const key = new PluginKey(extension.name);\n\n // Assign the plugin key to the extension name.\n this.pluginKeys[extension.name] = key;\n const getter = this.getPluginStateCreator(key);\n\n extension.pluginKey = key;\n extension.getPluginState = getter;\n\n this.stateGetters.set(extension.name, getter);\n this.stateGetters.set(extension.constructor, getter);\n\n const pluginSpec = {\n ...extension.createPlugin(),\n key,\n };\n const plugin = new Plugin(pluginSpec);\n\n this.updatePlugins([plugin], extension.plugin ? [extension.plugin] : undefined);\n extension.plugin = plugin;\n }\n\n if (extension.createExternalPlugins) {\n const externalPlugins = extension.createExternalPlugins();\n\n this.updatePlugins(externalPlugins, extension.externalPlugins);\n extension.externalPlugins = externalPlugins;\n }\n }\n\n private readonly getPluginStateCreator =\n (key: PluginKey) =>\n <State>(state?: EditorState): State => {\n return key.getState(state ?? this.store.getState());\n };\n\n /**\n * Add or replace a plugin.\n */\n private updatePlugins(plugins: Plugin[], previous?: Plugin[]) {\n // This is the first time plugins are being added.\n if (!previous || isEmptyArray(previous)) {\n this.plugins = [...this.plugins, ...plugins];\n return;\n }\n\n // The number of plugins and previous plugins is different.\n if (plugins.length !== previous.length) {\n // Remove previous plugins and add the new plugins to the end.\n this.plugins = [...this.plugins.filter((plugin) => !previous.includes(plugin)), ...plugins];\n return;\n }\n\n // The length of plugins is identical, therefore a replacement is possible.\n\n const pluginMap = new Map<Plugin, Plugin>();\n\n for (const [index, plugin] of plugins.entries()) {\n pluginMap.set(assertGet(previous, index), plugin);\n }\n\n this.plugins = this.plugins.map((plugin) => {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n return previous.includes(plugin) ? pluginMap.get(plugin)! : plugin;\n });\n }\n\n // Method for retrieving the plugin state by the extension name.\n private readonly getStateByName = <State>(identifier: string | AnyExtensionConstructor) => {\n const stateGetter = this.stateGetters.get(identifier);\n invariant(stateGetter, { message: 'No plugin exists for the requested extension name.' });\n\n return stateGetter<State>();\n };\n\n /**\n * Add the plugin specific properties and methods to the manager and extension\n * store.\n */\n private updateExtensionStore() {\n const { setExtensionStore } = this.store;\n\n // Allow adding, replacing and removing plugins by other extensions.\n setExtensionStore('updatePlugins', this.updatePlugins.bind(this));\n setExtensionStore('dispatchPluginUpdate', this.dispatchPluginUpdate.bind(this));\n setExtensionStore('updateExtensionPlugins', this.updateExtensionPlugins.bind(this));\n }\n\n /**\n * Reruns the `createPlugin` and `createExternalPlugins` methods of the\n * provided extension.\n *\n * ```ts\n * // From within an extension\n * this.store.updateExtensionPlugins(this);\n * ```\n */\n private updateExtensionPlugins(value: AnyExtension | AnyExtensionConstructor | string) {\n const extension = isExtension(value)\n ? value\n : isExtensionConstructor(value)\n ? this.store.manager.getExtension(value)\n : this.store.extensions.find((extension) => extension.name === value);\n\n invariant(extension, {\n code: ErrorConstant.INVALID_MANAGER_EXTENSION,\n message: `The extension ${value} does not exist within the editor.`,\n });\n\n this.extractExtensionPlugins(extension);\n this.store.setStoreKey('plugins', this.plugins);\n\n // Dispatch the plugin updates to the editor.\n this.dispatchPluginUpdate();\n }\n\n /**\n * Applies the store plugins to the state. If any have changed then it will be\n * updated.\n */\n private dispatchPluginUpdate() {\n invariant(this.store.phase >= ManagerPhase.EditorView, {\n code: ErrorConstant.MANAGER_PHASE_ERROR,\n message:\n '`dispatchPluginUpdate` should only be called after the view has been added to the manager.',\n });\n\n const { view, updateState } = this.store;\n const newState = view.state.reconfigure({ plugins: this.plugins });\n\n updateState(newState);\n }\n}\n\ndeclare global {\n namespace Remirror {\n interface ManagerSettings {\n /**\n * Add custom plugins to the manager while creating it.\n *\n * Plugins created via the manager are given priority over all extension\n * based plugins. There's scope for adding a priority based model for\n * inserting plugins, but it seems like a sane default until that's\n * available.\n */\n plugins?: ProsemirrorPlugin[];\n }\n\n interface ExtensionStore {\n /**\n * Retrieve the state for any given extension name. This will throw an\n * error if the extension identified by that name doesn't implement the\n * `createPlugin` method.\n *\n * @param name - the name of the extension\n *\n * @remarks\n *\n * ```ts\n * const pluginState = getPluginState(extension.name);\n * ```\n */\n getPluginState<State>(name: string): State;\n\n /**\n * Add the new plugins. If previous plugins are provided then also remove\n * the previous plugins.\n *\n * ```ts\n * this.store.updatePlugins(this.createExternalPlugins(), this.externalPlugins);\n * ```\n *\n * @param plugins - the plugins to add\n * @param previousPlugins - the plugins to remove\n */\n updatePlugins(plugins: ProsemirrorPlugin[], previousPlugins?: ProsemirrorPlugin[]): void;\n\n /**\n * Reruns the `createPlugin` and `createExternalPlugins` methods of the\n * provided extension.\n *\n * This will also automatically update the state with the newly generated\n * plugins by dispatching an update.\n *\n * ```ts\n * // From within an extension\n * this.store.updateExtensionPlugins(this);\n * this.store.dispatchPluginUpdate();\n * ```\n *\n * @param extension - the extension instance, constructor or name.\n */\n updateExtensionPlugins(extension: AnyExtension | AnyExtensionConstructor | string): void;\n\n /**\n * Applies the store plugins to the state. If any have changed then it\n * will be updated.\n */\n dispatchPluginUpdate(): void;\n }\n\n interface ManagerStore<Extension extends AnyExtension> {\n /**\n * All of the plugins combined together from all sources\n */\n plugins: ProsemirrorPlugin[];\n\n /**\n * Retrieve the state for a given extension name. This will throw an error\n * if the extension doesn't exist.\n *\n * @param name - the name of the extension\n */\n getPluginState: <State>(name: GetNameUnion<Extension>) => State;\n\n /**\n * All the plugin keys available to be used by plugins.\n */\n pluginKeys: Record<string, PluginKey>;\n }\n\n interface ExcludeOptions {\n /**\n * Whether to exclude the extension's plugin\n *\n * @defaultValue undefined\n */\n plugins?: boolean;\n }\n\n interface BaseExtension {\n /**\n * The plugin key for custom plugin created by this extension. This only\n * exists when there is a valid `createPlugin` method on the extension.\n *\n * This can be used to set and retrieve metadata.\n *\n * ```ts\n * const meta = tr.getMeta(this.pluginKey);\n * ```\n */\n pluginKey: PluginKey;\n\n /**\n * The plugin that was created by the `createPlugin` method. This only\n * exists for extension which implement that method.\n */\n plugin: Plugin;\n\n /**\n * The external plugins created by the `createExternalPlugins` method.\n */\n externalPlugins: Plugin[];\n\n /**\n * Retrieve the state of the custom plugin for this extension. This will\n * throw an error if the extension doesn't have a valid `createPlugin`\n * method.\n *\n * @remarks\n *\n * ```ts\n * const pluginState = this.getPluginState();\n * ```\n *\n * This is only available after the initialize stage of the editor manager\n * lifecycle.\n *\n * If you would like to use it before that e.g. in the decorations prop of\n * the `createPlugin` method, you can call it with a current state which\n * will be used to retrieve the plugin state.\n *\n * Please note that when using this in the decorations callback it is\n * advisable to pass in the `state` argument in case the callback is\n * called before the framework, or the view have been initialized.\n */\n getPluginState: <State>(state?: EditorState) => State;\n\n /**\n * Create a custom plugin directly in the editor.\n *\n * @remarks\n *\n * A unique `key` is automatically applied to enable easier retrieval of\n * the plugin state.\n *\n * ```ts\n * import { CreateExtensionPlugin } from 'remirror';\n *\n * class MyExtension extends PlainExtension {\n * get name() {\n * return 'me' as const;\n * }\n *\n * createPlugin(): CreateExtensionPlugin {\n * return {\n * props: {\n * handleKeyDown: keydownHandler({\n * Backspace: handler,\n * 'Mod-Backspace': handler,\n * Delete: handler,\n * 'Mod-Delete': handler,\n * 'Ctrl-h': handler,\n * 'Alt-Backspace': handler,\n * 'Ctrl-d': handler,\n * 'Ctrl-Alt-Backspace': handler,\n * 'Alt-Delete': handler,\n * 'Alt-d': handler,\n * }),\n * decorations: state => {\n * const pluginState = this.getPluginState(state);\n * pluginState.setDeleted(false);\n * return pluginState.decorationSet;\n * },\n * },\n * }\n * }\n * }\n * ```\n */\n createPlugin?(): CreateExtensionPlugin;\n\n /**\n * Register third party plugins when this extension is placed into the\n * editor.\n *\n * @remarks\n *\n * Some plugins (like the table plugin) consume several different plugins,\n * creator method allows you to return a list of plugins you'd like to\n * support.\n */\n createExternalPlugins?(): ProsemirrorPlugin[];\n }\n\n interface AllExtensions {\n plugins: PluginsExtension;\n }\n }\n}\n", "import {\n ErrorConstant,\n ExtensionPriority,\n ExtensionTag,\n ExtensionTagType,\n} from '@remirror/core-constants';\nimport {\n assertGet,\n entries,\n invariant,\n isArray,\n isFunction,\n isNullOrUndefined,\n isPlainObject,\n isString,\n object,\n toString,\n} from '@remirror/core-helpers';\nimport type {\n ApplySchemaAttributes,\n DynamicAttributeCreator,\n EditorSchema,\n JsonPrimitive,\n Mark,\n MarkExtensionSpec,\n MarkSpecOverride,\n NodeExtensionSpec,\n NodeMarkOptions,\n NodeSpecOverride,\n ProsemirrorAttributes,\n ProsemirrorNode,\n SchemaAttributes,\n SchemaAttributesObject,\n Static,\n Transaction,\n} from '@remirror/core-types';\nimport {\n getDefaultBlockNode,\n getMarkRange,\n isElementDomNode,\n isProsemirrorMark,\n isProsemirrorNode,\n} from '@remirror/core-utils';\nimport { MarkSpec, NodeSpec, Schema } from '@remirror/pm/model';\nimport { ignoreUpdateForSuggest } from '@remirror/pm/suggest';\n\nimport {\n AnyExtension,\n extension,\n GetMarkNameUnion,\n GetNodeNameUnion,\n isMarkExtension,\n isNodeExtension,\n PlainExtension,\n} from '../extension';\nimport type { CreateExtensionPlugin } from '../types';\nimport type { CombinedTags } from './tags-extension';\n\n/**\n * This is the schema extension which creates the schema and provides extra\n * attributes as defined in the manager or the extension settings.\n *\n * @remarks\n *\n * The schema is the most important part of the remirror editor. This is the\n * extension responsible for creating it, injecting extra attributes and\n * managing the plugin which is responsible for making sure dynamically created\n * attributes are updated.\n *\n * In order to add extra attributes the following would work.\n *\n * ```ts\n * import { RemirrorManager } from 'remirror';\n * import uuid from 'uuid';\n * import hash from 'made-up-hasher';\n *\n * const manager = RemirrorManager.create([], {\n * extraAttributes: [\n * {\n * identifiers: 'nodes',\n * attributes: {\n * awesome: {\n * default: 'awesome',\n * parseDOM: (domNode) => domNode.getAttribute('data-awesome'),\n * toDOM: (attrs) => ([ 'data-awesome', attrs.awesome ])\n * },\n * },\n * },\n * { identifiers: ['paragraph'], attributes: { id: { default: () => uuid() } } },\n * { identifiers: ['bold'], attributes: { hash: (mark) => hash(JSON.stringify(mark.attrs)) } },\n * ],\n * })\n * ```\n *\n * It is an array of identifiers and attributes. Setting the default to a\n * function allows you to set up a dynamic attribute which is updated with the\n * synchronous function that you provide to it.\n *\n * @category Builtin Extension\n */\n@extension({ defaultPriority: ExtensionPriority.Highest })\nexport class SchemaExtension extends PlainExtension {\n get name() {\n return 'schema' as const;\n }\n\n /**\n * The dynamic attributes for each node and mark extension.\n *\n * The structure will look like the following.\n *\n * ```ts\n * {\n * paragraph: { id: () => uid(), hash: (node) => hash(node) },\n * bold: { random: () => Math.random(), created: () => Date.now() },\n * };\n * ```\n *\n * This object is used by the created plugin to listen for changes to the doc,\n * and check for new nodes and marks which haven't yet applied the dynamic\n * attribute and add the attribute.\n */\n private readonly dynamicAttributes: DynamicSchemaAttributeCreators = {\n marks: object(),\n nodes: object(),\n };\n\n /**\n * This method is responsible for creating, configuring and adding the\n * `schema` to the editor. `Schema` is a special type in ProseMirror editors\n * and with `remirror` it's all just handled for you.\n */\n onCreate(): void {\n const { managerSettings, tags, markNames, nodeNames, extensions } = this.store;\n const { defaultBlockNode, disableExtraAttributes, nodeOverride, markOverride } =\n managerSettings;\n\n // True when the `defaultBlockNode` exists for this editor.\n const isValidDefaultBlockNode = (name: string | undefined): name is string =>\n !!(name && tags[ExtensionTag.Block].includes(name));\n\n // The user can override the whole schema creation process by providing\n // their own version. In that case we can exit early.\n if (managerSettings.schema) {\n const { nodes, marks } = getSpecFromSchema(managerSettings.schema);\n this.addSchema(managerSettings.schema, nodes, marks);\n\n // Exit early! \uD83D\uDE4C\n return;\n }\n\n // This nodes object is built up for each extension and then at the end it\n // will be passed to the `Schema` constructor to create a new `schema`.\n const nodes: Record<string, NodeSpec> = isValidDefaultBlockNode(defaultBlockNode)\n ? {\n doc: object(),\n // Ensure that this is the highest positioned block node by adding it\n // to the object early. Later on it will be overwritten but maintain\n // it's position.\n [defaultBlockNode]: object(),\n }\n : object();\n\n // Similar to the `nodes` object above this is passed to the `Schema`.\n const marks: Record<string, MarkSpec> = object();\n\n // Get the named extra attributes from the manager. This allows each extra\n // attribute group added to the manager to be applied to the individual\n // extensions which specified.\n const namedExtraAttributes = getNamedSchemaAttributes({\n settings: managerSettings,\n gatheredSchemaAttributes: this.gatherExtraAttributes(extensions),\n nodeNames: nodeNames,\n markNames: markNames,\n tags: tags,\n });\n\n for (const extension of extensions) {\n // Pick the current attributes from the named attributes and merge them\n // with the extra attributes which were added to the extension. Extra\n // attributes added to the extension are prioritized.\n namedExtraAttributes[extension.name] = {\n ...namedExtraAttributes[extension.name],\n ...extension.options.extraAttributes,\n };\n\n // There are several places that extra attributes can be ignored. This\n // checks them all.\n const ignoreExtraAttributes =\n disableExtraAttributes === true ||\n extension.options.disableExtraAttributes === true ||\n extension.constructor.disableExtraAttributes === true;\n\n if (isNodeExtension(extension)) {\n // Create the spec and gather dynamic attributes for this node\n // extension.\n const { spec, dynamic } = createSpec({\n createExtensionSpec: (extra, override) => extension.createNodeSpec(extra, override),\n extraAttributes: assertGet(namedExtraAttributes, extension.name),\n\n // Todo add support for setting overrides via the manager.\n override: { ...nodeOverride, ...extension.options.nodeOverride },\n ignoreExtraAttributes,\n name: extension.constructorName,\n tags: extension.tags,\n });\n\n // Store the node spec on the extension for future reference.\n extension.spec = spec;\n\n // Add the spec to the `nodes` object which is used to create the schema\n // with the same name as the extension name.\n nodes[extension.name] = spec as NodeSpec;\n\n // Keep track of the dynamic attributes. The `extension.name` is the\n // same name of the `NodeType` and is used by the plugin in this\n // extension to dynamically generate attributes for the correct nodes.\n if (Object.keys(dynamic).length > 0) {\n this.dynamicAttributes.nodes[extension.name] = dynamic;\n }\n }\n\n // Very similar to the previous conditional block except for marks rather\n // than nodes.\n if (isMarkExtension(extension)) {\n // Create the spec and gather dynamic attributes for this mark\n // extension.\n const { spec, dynamic } = createSpec({\n createExtensionSpec: (extra, override) => extension.createMarkSpec(extra, override),\n extraAttributes: assertGet(namedExtraAttributes, extension.name),\n // Todo add support for setting overrides via the manager.\n override: { ...markOverride, ...extension.options.markOverride },\n ignoreExtraAttributes,\n name: extension.constructorName,\n tags: extension.tags ?? [],\n });\n\n // Store the mark spec on the extension for future reference.\n extension.spec = spec;\n\n // Add the spec to the `marks` object which is used to create the schema\n // with the same name as the extension name.\n marks[extension.name] = spec as MarkSpec;\n\n // Keep track of the dynamic attributes. The `extension.name` is the\n // same name of the `MarkType` and is used by the plugin in this\n // extension to dynamically generate attributes for the correct marks.\n if (Object.keys(dynamic).length > 0) {\n this.dynamicAttributes.marks[extension.name] = dynamic;\n }\n }\n }\n\n // Create the schema from the gathered nodes and marks.\n const schema = new Schema({ nodes, marks, topNode: 'doc' });\n\n // Add the schema and nodes marks to the store.\n this.addSchema(\n schema,\n nodes as Record<string, NodeExtensionSpec>,\n marks as Record<string, MarkExtensionSpec>,\n );\n }\n\n /**\n * This creates the plugin that is used to automatically create the dynamic\n * attributes defined in the extra attributes object.\n */\n createPlugin(): CreateExtensionPlugin {\n return {\n appendTransaction: (transactions, _, nextState) => {\n // This creates a new transaction which will be used to update the\n // attributes of any node and marks which\n const { tr } = nextState;\n\n // The dynamic attribute updates only need to be run if the document has\n // been modified in a transaction.\n const documentHasChanged = transactions.some((tr) => tr.docChanged);\n\n if (!documentHasChanged) {\n // The document has not been changed therefore no updates are\n // required.\n return null;\n }\n\n // The find children method could potentially be quite expensive. Before\n // committing to that level of work let's check that there user has\n // actually defined some dynamic attributes.\n if (\n Object.keys(this.dynamicAttributes.nodes).length === 0 &&\n Object.keys(this.dynamicAttributes.marks).length === 0\n ) {\n return null;\n }\n\n // This function loops through every node in the document and add the\n // dynamic attributes when any relevant nodes have been added.\n tr.doc.descendants((child, pos) => {\n this.checkAndUpdateDynamicNodes(child, pos, tr);\n this.checkAndUpdateDynamicMarks(child, pos, tr);\n\n // This means that all nodes will be checked.\n return true;\n });\n\n // If the transaction has any `steps` then it has been modified and\n // should be returned i.e. appended to the additional transactions.\n // However, if there are no steps then ignore and return `null`.\n return tr.steps.length > 0 ? tr : null;\n },\n };\n }\n\n /**\n * Add the schema and nodes to the manager and extension store.\n */\n private addSchema(\n schema: EditorSchema,\n nodes: Record<string, NodeExtensionSpec>,\n marks: Record<string, MarkExtensionSpec>,\n ) {\n // Store the `nodes`, `marks` and `schema` on the manager store. For example\n // the `schema` can be accessed via `manager.store.schema`.\n this.store.setStoreKey('nodes', nodes);\n this.store.setStoreKey('marks', marks);\n this.store.setStoreKey('schema', schema);\n\n // Add the schema to the extension store, so that all extension from this\n // point have access to the schema via `this.store.schema`.\n this.store.setExtensionStore('schema', schema);\n this.store.setStoreKey('defaultBlockNode', getDefaultBlockNode(schema).name);\n\n // Set the default block node from the schema.\n for (const type of Object.values(schema.nodes)) {\n if (type.name === 'doc') {\n continue;\n }\n\n // Break as soon as the first non 'doc' block node is encountered.\n if (type.isBlock || type.isTextblock) {\n break;\n }\n }\n }\n\n /**\n * Check the dynamic nodes to see if the provided node:\n *\n * - a) is dynamic and therefore can be updated.\n * - b) has just been created and does not yet have a value for the dynamic\n * node.\n *\n * @param node - the node\n * @param pos - the node's position\n * @param tr - the mutable ProseMirror transaction which is applied to create\n * the next editor state\n */\n private checkAndUpdateDynamicNodes(node: ProsemirrorNode, pos: number, tr: Transaction) {\n // Check for matching nodes.\n for (const [name, dynamic] of entries(this.dynamicAttributes.nodes)) {\n if (node.type.name !== name) {\n continue;\n }\n\n for (const [attributeName, attributeCreator] of entries(dynamic)) {\n if (!isNullOrUndefined(node.attrs[attributeName])) {\n continue;\n }\n\n // The new attributes which will be added to the node.\n const attrs = { ...node.attrs, [attributeName]: attributeCreator(node) };\n\n // Apply the new dynamic attribute to the node via the transaction.\n tr.setNodeMarkup(pos, undefined, attrs);\n\n // Ignore this update in the `prosemirror-suggest` plugin\n ignoreUpdateForSuggest(tr);\n }\n }\n }\n\n /**\n * Loop through the dynamic marks to see if the provided node:\n *\n * - a) is wrapped by a matching mark.\n * - b) has just been added and doesn't yet have the dynamic attribute\n * applied.\n *\n * @param node - the node\n * @param pos - the node's position\n * @param tr - the mutable ProseMirror transaction which is applied to create\n * the next editor state.\n */\n private checkAndUpdateDynamicMarks(node: ProsemirrorNode, pos: number, tr: Transaction) {\n // Check for matching marks.\n for (const [name, dynamic] of entries(this.dynamicAttributes.marks)) {\n // This is needed to create the new mark. Even though a mark may already\n // exist ProseMirror requires that a new one is created and added in\n // order. More details available\n // [here](https://discuss.prosemirror.net/t/updating-mark-attributes/776/2?u=ifi).\n const type = assertGet(this.store.schema.marks, name);\n\n // Get the attrs from the mark.\n const mark = node.marks.find((mark) => mark.type.name === name);\n\n // If the mark doesn't exist within the set then move to the next\n // dynamically updated mark.\n if (!mark) {\n continue;\n }\n\n // Loop through to find if any of the required matches are missing from\n // the dynamic attribute;\n for (const [attributeName, attributeCreator] of entries(dynamic)) {\n // When the attributes for this dynamic attributeName are already\n // defined we should move onto the next item;\n if (!isNullOrUndefined(mark.attrs[attributeName])) {\n continue;\n }\n\n // Use the starting position of the node to calculate the range range of\n // the current mark.\n const range = getMarkRange(tr.doc.resolve(pos), type);\n\n if (!range) {\n continue;\n }\n\n // The { from, to } range which will be used to update the mark id\n // attribute.\n const { from, to } = range;\n\n // Create the new mark with all the existing dynamic attributes applied.\n const newMark = type.create({\n ...mark.attrs,\n [attributeName]: attributeCreator(mark),\n });\n\n // Update the value of the mark. The only way to do this right now is to\n // remove and then add it back again.\n tr.removeMark(from, to, type).addMark(from, to, newMark);\n\n // Ignore this update in the `prosemirror-suggest` plugin\n ignoreUpdateForSuggest(tr);\n }\n }\n }\n\n /**\n * Gather all the extra attributes that have been added by extensions.\n */\n private gatherExtraAttributes(extensions: readonly AnyExtension[]) {\n const extraSchemaAttributes: IdentifierSchemaAttributes[] = [];\n\n for (const extension of extensions) {\n if (!extension.createSchemaAttributes) {\n continue;\n }\n\n extraSchemaAttributes.push(...extension.createSchemaAttributes());\n }\n\n return extraSchemaAttributes;\n }\n}\n\n/**\n * With tags, you can select a specific sub selection of marks and nodes. This\n * will be the basis for adding advanced formatting to remirror.\n *\n * ```ts\n * import { ExtensionTag } from 'remirror';\n * import { createCoreManager, CorePreset } from 'remirror/extensions';\n * import { WysiwygPreset } from 'remirror/extensions';\n *\n * const manager = createCoreManager(() => [new WysiwygPreset(), new CorePreset()], {\n * extraAttributes: [\n * {\n * identifiers: {\n * tags: [ExtensionTag.NodeBlock],\n * type: 'node',\n * },\n * attributes: { role: 'presentation' },\n * },\n * ],\n * });\n * ```\n *\n * Each item in the tags array should be read as an `OR` so the following would\n * match `Tag1` OR `Tag2` OR `Tag3`.\n *\n * ```json\n * { tags: [\"Tag1\", \"Tag2\", \"Tag3\"] }\n * ```\n *\n * The `type` property (`mark | node`) is exclusive and limits the type of\n * extension names that will be matched. When `mark` is set it only matches with\n * marks.\n */\nexport interface IdentifiersObject {\n /**\n * Determines how the array of tags are combined:\n *\n * - `all` - the extension only matches when all tags are present.\n * - `any` - the extension will match if it includes any of the specified\n * tags.\n *\n * This only affects the `tags` property.\n *\n * The saddest part about this property is that, as a UK resident, I've\n * succumbed to using the Americanized spelling instead of the Oxford\n * Dictionary defined spelling of `behaviour` \uD83D\uDE22\n *\n * @defaultValue 'any'\n */\n behavior?: 'all' | 'any';\n\n /**\n * Will find relevant names based on the defined `behaviour`.\n */\n tags?: ExtensionTagType[];\n\n /**\n * Additional names to include. These will still be added even if the\n * extension name matches with `excludeTags` member.\n */\n names?: string[];\n\n /**\n * Whether to restrict by whether this is a [[`ProsemirrorNode`]] or a\n * [[`Mark`]]. Leave blank to accept all types.\n */\n type?: 'node' | 'mark';\n\n /**\n * Exclude these names from being matched.\n */\n excludeNames?: string[];\n\n /**\n * Exclude these tags from being matched. Will always exclude if any of the\n * tags\n */\n excludeTags?: string[];\n}\n\n/**\n * The extra identifiers that can be used.\n *\n * - `nodes` - match all nodes\n * - `marks` - match all marks\n * - `all` - match everything in the editor\n * - `string[]` - match the selected node and mark names\n * - [[`IdentifiersObject`]] - match by `ExtensionTag` and type name.\n */\nexport type Identifiers = 'nodes' | 'marks' | 'all' | readonly string[] | IdentifiersObject;\n\n/**\n * The interface for adding extra attributes to multiple node and mark\n * extensions.\n */\nexport interface IdentifierSchemaAttributes {\n /**\n * The nodes or marks to add extra attributes to.\n *\n * This can either be an array of the strings or the following specific\n * identifiers:\n *\n * - 'nodes' for all nodes\n * - 'marks' for all marks\n * - 'all' for all extensions which touch the schema.\n */\n identifiers: Identifiers;\n\n /**\n * The attributes to be added.\n */\n attributes: SchemaAttributes;\n}\n\n/**\n * An object of `mark` and `node` dynamic schema attribute creators.\n */\ninterface DynamicSchemaAttributeCreators {\n /**\n * The dynamic schema attribute creators for all marks in the editor.\n */\n marks: Record<string, Record<string, DynamicAttributeCreator>>;\n\n /**\n * The dynamic schema attribute creators for all nodes in the editor.\n */\n nodes: Record<string, Record<string, DynamicAttributeCreator>>;\n}\n\n/**\n * The schema attributes mapped to the names of the extension they belong to.\n */\ntype NamedSchemaAttributes = Record<string, SchemaAttributes>;\n\ninterface TransformSchemaAttributesProps {\n /**\n * The manager settings at the point of creation.\n */\n settings: Remirror.ManagerSettings;\n\n /**\n * The schema attributes which were added to the `manager`.\n */\n gatheredSchemaAttributes: IdentifierSchemaAttributes[];\n\n /**\n * The names of all the nodes within the editor.\n */\n nodeNames: readonly string[];\n\n /**\n * The names of all the marks within the editor.\n */\n markNames: readonly string[];\n\n /**\n * The tags that are being used by active extension right now.\n */\n tags: CombinedTags;\n}\n\n/**\n * Get the extension extra attributes created via the manager and convert into a\n * named object which can be added to each node and mark spec.\n */\nfunction getNamedSchemaAttributes(props: TransformSchemaAttributesProps): NamedSchemaAttributes {\n const { settings, gatheredSchemaAttributes, nodeNames, markNames, tags } = props;\n const extraAttributes: NamedSchemaAttributes = object();\n\n if (settings.disableExtraAttributes) {\n return extraAttributes;\n }\n\n const extraSchemaAttributes: IdentifierSchemaAttributes[] = [\n ...gatheredSchemaAttributes,\n ...(settings.extraAttributes ?? []),\n ];\n\n for (const attributeGroup of extraSchemaAttributes ?? []) {\n const identifiers = getIdentifiers({\n identifiers: attributeGroup.identifiers,\n nodeNames,\n markNames,\n tags,\n });\n\n for (const identifier of identifiers) {\n const currentValue = extraAttributes[identifier] ?? {};\n extraAttributes[identifier] = { ...currentValue, ...attributeGroup.attributes };\n }\n }\n\n return extraAttributes;\n}\n\ninterface GetIdentifiersProps {\n identifiers: Identifiers;\n nodeNames: readonly string[];\n markNames: readonly string[];\n tags: CombinedTags;\n}\n\n/**\n * A predicate for checking if the passed in value is an `IdentifiersObject`.\n */\nfunction isIdentifiersObject(value: Identifiers): value is IdentifiersObject {\n return isPlainObject(value) && isArray(value.tags);\n}\n\n/**\n * Get the array of names from the identifier that the extra attributes should\n * be applied to.\n */\nfunction getIdentifiers(props: GetIdentifiersProps): readonly string[] {\n const { identifiers, nodeNames, markNames, tags } = props;\n\n if (identifiers === 'nodes') {\n return nodeNames;\n }\n\n if (identifiers === 'marks') {\n return markNames;\n }\n\n if (identifiers === 'all') {\n return [...nodeNames, ...markNames];\n }\n\n // This is already an array of names to apply the attributes to.\n if (isArray(identifiers)) {\n return identifiers;\n }\n\n // Make sure the object provides is valid.\n invariant(isIdentifiersObject(identifiers), {\n code: ErrorConstant.EXTENSION_EXTRA_ATTRIBUTES,\n message: `Invalid value passed as an identifier when creating \\`extraAttributes\\`.`,\n });\n\n // Provide type aliases for easier readability.\n type Name = string; // `type` Alias for the extension name.\n type Tag = string; // The tag for this extension.\n type TagSet = Set<Tag>; // The set of tags.\n type TaggedNamesMap = Map<Name, TagSet>;\n\n const {\n tags: extensionTags = [],\n names: extensionNames = [],\n behavior = 'any',\n excludeNames,\n excludeTags,\n type,\n } = identifiers;\n\n // Keep track of the set of stored names.\n const names: Set<Name> = new Set();\n\n // Collect the array of names that are supported.\n const acceptableNames =\n type === 'mark' ? markNames : type === 'node' ? nodeNames : [...markNames, ...nodeNames];\n\n // Check if the name is valid\n const isNameValid = (name: string) =>\n acceptableNames.includes(name) && !excludeNames?.includes(name);\n\n for (const name of extensionNames) {\n if (isNameValid(name)) {\n names.add(name);\n }\n }\n\n // Create a map of extension names to their set of included tags. Then check\n // that the length of the `TagSet` for each extension name is equal to the\n // provided extension tags in this identifier.\n const taggedNamesMap: TaggedNamesMap = new Map();\n\n // Loop through every extension\n for (const tag of extensionTags) {\n if (excludeTags?.includes(tag)) {\n continue;\n }\n\n for (const name of tags[tag]) {\n if (!isNameValid(name)) {\n continue;\n }\n\n // When any tag can be an identifier simply add the name to names.\n if (behavior === 'any') {\n names.add(name);\n continue;\n }\n\n const tagSet: TagSet = taggedNamesMap.get(name) ?? new Set();\n tagSet.add(tag);\n taggedNamesMap.set(name, tagSet);\n }\n }\n\n // Only add the names that have a `TagSet` where `size` is equal to the number\n // of `extensionTags`\n for (const [name, tagSet] of taggedNamesMap) {\n if (tagSet.size === extensionTags.length) {\n names.add(name);\n }\n }\n\n return [...names];\n}\n\ninterface CreateSpecProps<Spec extends { group?: string | null }, Override extends object> {\n /**\n * The node or mark creating function.\n */\n createExtensionSpec: (extra: ApplySchemaAttributes, override: Override) => Spec;\n\n /**\n * The extra attributes object which has been passed through for this\n * extension.\n */\n extraAttributes: SchemaAttributes;\n\n /**\n * The overrides provided to the schema.\n */\n override: Override;\n\n /**\n * This is true when the extension is set to ignore extra attributes.\n */\n ignoreExtraAttributes: boolean;\n\n /**\n * The name for displaying in an error message. The name of the constructor is\n * used since it's more descriptive and easier to debug the error that may be\n * thrown if extra attributes are not applied correctly.\n */\n name: string;\n\n /**\n * The tags that were used to create this extension. These are added to the\n * node and mark groups.\n */\n tags: ExtensionTagType[];\n}\n\ninterface CreateSpecReturn<Type extends { group?: string | null }> {\n /** The created spec. */\n spec: Type;\n\n /** The dynamic attribute creators for this spec */\n dynamic: Record<string, DynamicAttributeCreator>;\n}\n\n/**\n * Create the scheme spec for a node or mark extension.\n *\n * @typeParam Type - either a [[Mark]] or a [[ProsemirrorNode]]\n * @param props - the options object [[CreateSpecProps]]\n */\nfunction createSpec<Type extends { group?: string | null }, Override extends object>(\n props: CreateSpecProps<Type, Override>,\n): CreateSpecReturn<Type> {\n const { createExtensionSpec, extraAttributes, ignoreExtraAttributes, name, tags, override } =\n props;\n\n // Keep track of the dynamic attributes which are a part of this spec.\n const dynamic: Record<string, DynamicAttributeCreator> = object();\n\n /** Called for every dynamic creator to track the dynamic attributes */\n function addDynamic(attributeName: string, creator: DynamicAttributeCreator) {\n dynamic[attributeName] = creator;\n }\n\n // Used to track whether the method has been called. If not called when the\n // extension spec is being set up then an error is thrown.\n let defaultsCalled = false;\n\n /** Called by createDefaults to track when the `defaults` has been called. */\n function onDefaultsCalled() {\n defaultsCalled = true;\n }\n\n const defaults = createDefaults(\n extraAttributes,\n ignoreExtraAttributes,\n onDefaultsCalled,\n addDynamic,\n );\n\n const parse = createParseDOM(extraAttributes, ignoreExtraAttributes);\n const dom = createToDOM(extraAttributes, ignoreExtraAttributes);\n const spec = createExtensionSpec({ defaults, parse, dom }, override);\n\n invariant(ignoreExtraAttributes || defaultsCalled, {\n code: ErrorConstant.EXTENSION_SPEC,\n message: `When creating a node specification you must call the 'defaults', and parse, and 'dom' methods. To avoid this error you can set the static property 'disableExtraAttributes' of '${name}' to 'true'.`,\n });\n\n // Add the tags to the group of the created spec.\n spec.group = [...(spec.group?.split(' ') ?? []), ...tags].join(' ') || undefined;\n\n return { spec, dynamic };\n}\n\n/**\n * Get the value of the extra attribute as an object.\n *\n * This is needed because the SchemaAttributes object can be configured as a\n * string or as an object.\n */\nfunction getExtraAttributesObject(\n value: DynamicAttributeCreator | string | SchemaAttributesObject,\n): SchemaAttributesObject {\n if (isString(value) || isFunction(value)) {\n return { default: value };\n }\n\n invariant(value, {\n message: `${toString(value)} is not supported`,\n code: ErrorConstant.EXTENSION_EXTRA_ATTRIBUTES,\n });\n\n return value;\n}\n\n/**\n * Create the `defaults()` method which is used for setting the property.\n *\n * @param extraAttributes - the extra attributes for this particular node\n * @param shouldIgnore - whether this attribute should be ignored\n * @param onCalled - the function which is called when this is run, to check\n * that it has been added to the attrs\n * @param addDynamic - A function called to add the dynamic creator and name to\n * the store\n */\nfunction createDefaults(\n extraAttributes: SchemaAttributes,\n shouldIgnore: boolean,\n onCalled: () => void,\n addDynamicCreator: (name: string, creator: DynamicAttributeCreator) => void,\n) {\n return () => {\n onCalled();\n const attributes: Record<string, { default?: JsonPrimitive }> = object();\n\n // Extra attributes can be ignored by the extension, check if that's the\n // case here.\n if (shouldIgnore) {\n return attributes;\n }\n\n // Loop through the extra attributes and attach to the attributes object.\n for (const [name, config] of entries(extraAttributes)) {\n // Make sure this is an object and not a string.\n const attributesObject = getExtraAttributesObject(config);\n let defaultValue = attributesObject.default;\n\n // When true this is a dynamic attribute creator.\n if (isFunction(defaultValue)) {\n // Store the name and method of the dynamic creator.\n addDynamicCreator(name, defaultValue);\n\n // Set the attributes for this dynamic creator to be null by default.\n defaultValue = null;\n }\n\n // When the `defaultValue` is set to `undefined`, it is set as an empty\n // object in order for ProseMirror to set it as a required attribute.\n attributes[name] = defaultValue === undefined ? {} : { default: defaultValue };\n }\n\n return attributes;\n };\n}\n\n/**\n * Create the parseDOM method to be applied to the extension `createNodeSpec`.\n */\nfunction createParseDOM(extraAttributes: SchemaAttributes, shouldIgnore: boolean) {\n return (domNode: string | Node) => {\n const attributes: ProsemirrorAttributes = object();\n\n if (shouldIgnore) {\n return attributes;\n }\n\n for (const [name, config] of entries(extraAttributes)) {\n const { parseDOM, ...other } = getExtraAttributesObject(config);\n\n if (!isElementDomNode(domNode)) {\n continue;\n }\n\n if (isNullOrUndefined(parseDOM)) {\n attributes[name] = domNode.getAttribute(name) ?? other.default;\n continue;\n }\n\n if (isFunction(parseDOM)) {\n attributes[name] = parseDOM(domNode) ?? other.default;\n continue;\n }\n\n attributes[name] = domNode.getAttribute(parseDOM) ?? other.default;\n }\n\n return attributes;\n };\n}\n\n/**\n * Create the `toDOM` method to be applied to the extension `createNodeSpec`.\n */\nfunction createToDOM(extraAttributes: SchemaAttributes, shouldIgnore: boolean) {\n return (item: ProsemirrorNode | Mark) => {\n const domAttributes: Record<string, string> = object();\n\n if (shouldIgnore) {\n return domAttributes;\n }\n\n function updateDomAttributes(\n value: string | [string, string?] | Record<string, string> | undefined | null,\n name: string,\n ) {\n if (!value) {\n return;\n }\n\n if (isString(value)) {\n domAttributes[name] = value;\n return;\n }\n\n if (isArray(value)) {\n const [attr, val] = value;\n domAttributes[attr] = val ?? (item.attrs[name] as string);\n return;\n }\n\n for (const [attr, val] of entries(value)) {\n domAttributes[attr] = val;\n }\n }\n\n for (const [name, config] of entries(extraAttributes)) {\n const { toDOM, parseDOM } = getExtraAttributesObject(config);\n\n if (isNullOrUndefined(toDOM)) {\n const key = isString(parseDOM) ? parseDOM : name;\n domAttributes[key] = item.attrs[name] as string;\n\n continue;\n }\n\n if (isFunction(toDOM)) {\n updateDomAttributes(toDOM(item.attrs, getNodeMarkOptions(item)), name);\n\n continue;\n }\n\n updateDomAttributes(toDOM, name);\n }\n\n return domAttributes;\n };\n}\n\n/**\n * Get the options object which applies should be used to obtain the node or\n * mark type.\n */\nfunction getNodeMarkOptions(item: ProsemirrorNode | Mark): NodeMarkOptions {\n if (isProsemirrorNode(item)) {\n return { node: item };\n }\n\n if (isProsemirrorMark(item)) {\n return { mark: item };\n }\n\n return {};\n}\n\n/**\n * Get the mark and node specs from provided schema.\n *\n * This is used when the user provides their own custom schema.\n */\nfunction getSpecFromSchema(schema: EditorSchema) {\n const nodes: Record<string, NodeExtensionSpec> = object();\n const marks: Record<string, MarkExtensionSpec> = object();\n\n for (const [name, type] of Object.entries(schema.nodes)) {\n nodes[name] = type.spec as NodeExtensionSpec;\n }\n\n for (const [name, type] of Object.entries(schema.marks)) {\n marks[name] = type.spec as MarkExtensionSpec;\n }\n\n return { nodes, marks };\n}\n\ndeclare global {\n namespace Remirror {\n interface BaseExtension {\n /**\n * Allows the extension to create an extra attributes array that will be\n * added to the extra attributes.\n *\n * For example the `@remirror/extension-bidi` adds a `dir` attribute to\n * all node extensions which allows them to automatically infer whether\n * the text direction should be right-to-left, or left-to-right.\n */\n createSchemaAttributes?(): IdentifierSchemaAttributes[];\n }\n interface BaseExtensionOptions {\n /**\n * Inject additional attributes into the defined mark / node schema. This\n * can only be used for `NodeExtensions` and `MarkExtensions`.\n *\n * @remarks\n *\n * Sometimes you need to add additional attributes to a node or mark. This\n * property enables this without needing to create a new extension.\n *\n * This is only applied to the `MarkExtension` and `NodeExtension`.\n *\n * @defaultValue {}\n */\n extraAttributes?: Static<SchemaAttributes>;\n\n /**\n * When true will disable extra attributes for this instance of the\n * extension.\n *\n * @remarks\n *\n * This is only applied to the `MarkExtension` and `NodeExtension`.\n *\n * @defaultValue undefined\n */\n disableExtraAttributes?: Static<boolean>;\n\n /**\n * An override for the mark spec object. This only applies for\n * `MarkExtension`.\n */\n markOverride?: Static<MarkSpecOverride>;\n\n /**\n * An override object for a node spec object. This only applies to the\n * `NodeExtension`.\n */\n nodeOverride?: Static<NodeSpecOverride>;\n }\n\n interface ManagerSettings {\n /**\n * Allows for setting extra attributes on multiple nodes and marks by\n * their name or constructor. These attributes are automatically added and\n * retrieved from from the dom by prosemirror.\n *\n * @remarks\n *\n * An example is shown below.\n *\n * ```ts\n * import { RemirrorManager } from 'remirror';\n *\n * const managerSettings = {\n * extraAttributes: [\n * {\n * identifiers: ['blockquote', 'heading'],\n * attributes: { id: 'id', alignment: '0', },\n * }, {\n * identifiers: ['mention', 'codeBlock'],\n * attributes: { 'userId': { default: null } },\n * },\n * ]\n * };\n *\n * const manager = RemirrorManager.create([], { extraAttributes })\n * ```\n */\n extraAttributes?: IdentifierSchemaAttributes[];\n\n /**\n * Overrides for the mark.\n */\n markOverride?: Record<string, MarkSpecOverride>;\n\n /**\n * Overrides for the nodes.\n */\n nodeOverride?: Record<string, NodeSpecOverride>;\n\n /**\n * Perhaps you don't need extra attributes at all in the editor. This\n * allows you to disable extra attributes when set to true.\n *\n * @defaultValue undefined\n */\n disableExtraAttributes?: boolean;\n\n /**\n * Setting this to a value will override the default behaviour of the\n * `RemirrorManager`. It overrides the created schema and ignores the\n * specs created by all extensions within your editor.\n *\n * @remarks\n *\n * This is an advanced option and should only be used in cases where there\n * is a deeper understanding of `Prosemirror`. By setting this, please\n * note that a lot of functionality just won't work which is powered by\n * the `extraAttributes`.\n */\n schema?: EditorSchema;\n\n /**\n * The name of the default block node. This node will be given a higher\n * priority when being added to the schema.\n *\n * By default this is undefined and the default block node is assigned\n * based on the extension priorities.\n *\n * @defaultValue undefined\n */\n defaultBlockNode?: string;\n }\n\n interface ManagerStore<Extension extends AnyExtension> {\n /**\n * The nodes to place on the schema.\n */\n nodes: Record<\n GetNodeNameUnion<Extension> extends never ? string : GetNodeNameUnion<Extension>,\n NodeExtensionSpec\n >;\n\n /**\n * The marks to be added to the schema.\n */\n marks: Record<\n GetMarkNameUnion<Extension> extends never ? string : GetMarkNameUnion<Extension>,\n MarkExtensionSpec\n >;\n\n /**\n * The schema created by this extension manager.\n */\n schema: EditorSchema;\n\n /**\n * The name of the default block node. This is used by all internal\n * extension when toggling block nodes. It can also be used in other\n * cases.\n *\n * This can be updated via the manager settings when first creating the\n * editor.\n *\n * @defaultValue 'paragraph'\n */\n defaultBlockNode: string;\n }\n\n interface MarkExtension {\n /**\n * Provides access to the `MarkExtensionSpec`.\n */\n spec: MarkExtensionSpec;\n }\n\n interface NodeExtension {\n /**\n * Provides access to the `NodeExtensionSpec`.\n */\n spec: NodeExtensionSpec;\n }\n\n interface ExtensionStore {\n /**\n * The Prosemirror schema being used for the current editor.\n *\n * @remarks\n *\n * The value is created when the manager initializes. So it can be used in\n * `createCommands`, `createHelpers`, `createKeymap` and most of the\n * creator methods.\n */\n schema: EditorSchema;\n }\n\n interface StaticExtensionOptions {\n /**\n * When true will disable extra attributes for all instances of this\n * extension.\n *\n * @defaultValue false\n */\n readonly disableExtraAttributes?: boolean;\n }\n\n interface AllExtensions {\n schema: SchemaExtension;\n }\n }\n}\n", "import { includes, isArray } from '@remirror/core-helpers';\nimport type { CustomHandler, EditorState, ProsemirrorPlugin } from '@remirror/core-types';\nimport {\n addSuggester,\n getSuggestPluginState,\n removeSuggester,\n suggest,\n Suggester,\n SuggestState,\n} from '@remirror/pm/suggest';\n\nimport { extension, Helper, PlainExtension } from '../extension';\nimport type { AddCustomHandler } from '../extension/base-class';\nimport { helper } from './builtin-decorators';\n\nexport interface SuggestOptions {\n /**\n * The custom handler which enables adding `suggesters`.\n */\n suggester: CustomHandler<Suggester>;\n}\n\n/**\n * This extension allows others extension to add the `createSuggesters` method\n * for adding the prosemirror-suggest functionality to your editor.\n *\n * @remarks\n *\n * This is an example of adding custom functionality to an extension via the\n * `ExtensionParameterMethods`.\n *\n * @category Builtin Extension\n */\n@extension<SuggestOptions>({ customHandlerKeys: ['suggester'] })\nexport class SuggestExtension extends PlainExtension<SuggestOptions> {\n get name() {\n return 'suggest' as const;\n }\n\n /**\n * Create the `addSuggester` method and `removeSuggester` methods to the\n * extension store.\n *\n * This can be used by extensions to conditionally add suggestion support.\n */\n onCreate(): void {\n this.store.setExtensionStore('addSuggester', (suggester) =>\n addSuggester(this.store.getState(), suggester),\n );\n\n this.store.setExtensionStore('removeSuggester', (suggester) =>\n removeSuggester(this.store.getState(), suggester),\n );\n }\n\n /**\n * Add the `prosemirror-suggest` plugin to the editor.\n */\n createExternalPlugins(): ProsemirrorPlugin[] {\n const suggesters: Suggester[] = [];\n\n for (const extension of this.store.extensions) {\n if (this.store.managerSettings.exclude?.suggesters) {\n // Exit the loop early when the manager is set to ignore suggesters.\n break;\n }\n\n if (\n // Method doesn't exist\n !extension.createSuggesters ||\n // Extension settings exclude it from running\n extension.options.exclude?.suggesters\n ) {\n continue;\n }\n\n const suggester = extension.createSuggesters();\n const suggesterList = isArray(suggester) ? suggester : [suggester];\n suggesters.push(...suggesterList);\n }\n\n return [suggest(...suggesters)];\n }\n\n /**\n * Allow additional `Suggesters` to be added to the editor. This can be used\n * by `React` to create hooks.\n */\n onAddCustomHandler: AddCustomHandler<SuggestOptions> = ({ suggester }) => {\n if (!suggester || this.store.managerSettings.exclude?.suggesters) {\n return;\n }\n\n // Update the suggesters with the provided suggester. Returns the cleanup\n // method.\n return addSuggester(this.store.getState(), suggester);\n };\n\n /**\n * Get the suggest plugin state.\n *\n * This may be removed at a later time.\n *\n * @experimental\n */\n @helper()\n getSuggestState(state?: EditorState): Helper<SuggestState> {\n return getSuggestPluginState(state ?? this.store.getState());\n }\n\n /**\n * Get some helpful methods from the SuggestPluginState.\n */\n @helper()\n getSuggestMethods(): Helper<\n Pick<\n SuggestState,\n | 'addIgnored'\n | 'clearIgnored'\n | 'removeIgnored'\n | 'ignoreNextExit'\n | 'setMarkRemoved'\n | 'findMatchAtPosition'\n | 'findNextTextSelection'\n | 'setLastChangeFromAppend'\n >\n > {\n const {\n addIgnored,\n clearIgnored,\n removeIgnored,\n ignoreNextExit,\n setMarkRemoved,\n findMatchAtPosition,\n findNextTextSelection,\n setLastChangeFromAppend,\n } = this.getSuggestState();\n\n return {\n addIgnored,\n clearIgnored,\n removeIgnored,\n ignoreNextExit,\n setMarkRemoved,\n findMatchAtPosition,\n findNextTextSelection,\n setLastChangeFromAppend,\n };\n }\n\n /**\n * Check to see whether the provided name is the currently active\n * suggester.\n *\n * @param name - the name of the suggester to include\n */\n @helper()\n isSuggesterActive(name: string | string[]): Helper<boolean> {\n return includes(isArray(name) ? name : [name], this.getSuggestState().match?.suggester.name);\n }\n}\n\ndeclare global {\n namespace Remirror {\n interface ExcludeOptions {\n /**\n * Whether to exclude the suggesters plugin configuration for the\n * extension.\n *\n * @defaultValue undefined\n */\n suggesters?: boolean;\n }\n\n interface BaseExtension {\n /**\n * Create suggesters which respond to an activation `char` or regex\n * pattern within the editor instance. The onChange handler provided is\n * called with the data around the matching text.\n *\n * @remarks\n *\n * Suggesters are a powerful way of building up the editors\n * functionality. They can support `@` mentions, `#` tagging, `/` special\n * command keys which trigger action menus and much more.\n */\n createSuggesters?(): Suggester[] | Suggester;\n }\n\n interface AllExtensions {\n suggest: SuggestExtension;\n }\n\n interface ExtensionStore {\n /**\n * Add a suggester.\n */\n addSuggester(suggester: Suggester): void;\n\n /**\n * Remove a suggester.\n */\n removeSuggester(suggester: Suggester | string): void;\n }\n\n interface AllExtensions {\n suggest: SuggestExtension;\n }\n }\n}\n", "import {\n ErrorConstant,\n ExtensionPriority,\n ExtensionTag,\n ExtensionTagType,\n} from '@remirror/core-constants';\nimport { includes, invariant, object, values } from '@remirror/core-helpers';\nimport type { UseDefault } from '@remirror/core-types';\n\nimport {\n AnyExtension,\n extension,\n GetMarkNameUnion,\n GetNameUnion,\n GetNodeNameUnion,\n GetPlainNameUnion,\n isMarkExtension,\n isNodeExtension,\n isPlainExtension,\n PlainExtension,\n} from '../extension';\n\n/**\n * Create the extension tags which are passed into each extensions method to\n * enable dynamically generated rules and commands.\n *\n * Tags on nodes and marks are automatically added to the schema as groups.\n *\n * @category Builtin Extension\n */\n@extension({ defaultPriority: ExtensionPriority.Highest })\nexport class TagsExtension extends PlainExtension {\n get name() {\n return 'tags' as const;\n }\n\n /**\n * Track the tags which have been applied to the extensions in this editor.\n */\n private allTags: CombinedTags = object();\n\n /**\n * The tags for plain extensions.\n */\n private plainTags: CombinedTags = object();\n\n /**\n * The tags for mark extensions.\n */\n private markTags: CombinedTags = object();\n\n /**\n * The tags for node extensions.\n */\n private nodeTags: CombinedTags = object();\n\n /**\n * Create the tags which are used to identify extension with particular\n * behavioral traits.\n */\n onCreate(): void {\n this.resetTags();\n\n for (const extension of this.store.extensions) {\n this.updateTagForExtension(extension);\n }\n\n this.store.setStoreKey('tags', this.allTags);\n this.store.setExtensionStore('tags', this.allTags);\n this.store.setStoreKey('plainTags', this.plainTags);\n this.store.setExtensionStore('plainTags', this.plainTags);\n this.store.setStoreKey('markTags', this.markTags);\n this.store.setExtensionStore('markTags', this.markTags);\n this.store.setStoreKey('nodeTags', this.nodeTags);\n this.store.setExtensionStore('nodeTags', this.nodeTags);\n }\n\n /**\n * Reset the tags to the empty object with empty arrays.\n */\n private resetTags() {\n const allTags: CombinedTags = object();\n const plainTags: CombinedTags = object();\n const markTags: CombinedTags = object();\n const nodeTags: CombinedTags = object();\n\n for (const tagName of values(ExtensionTag)) {\n allTags[tagName] = [];\n plainTags[tagName] = [];\n markTags[tagName] = [];\n nodeTags[tagName] = [];\n }\n\n this.allTags = allTags;\n this.plainTags = plainTags;\n this.markTags = markTags;\n this.nodeTags = nodeTags;\n }\n\n /**\n * Update the tags object for each extension.\n */\n private updateTagForExtension(extension: AnyExtension) {\n const allTags = new Set([\n // TODO remove `extension.tags` once all tags have been moved over to `createTags`\n ...(extension.tags ?? []),\n ...(extension.createTags?.() ?? []),\n ...(extension.options.extraTags ?? []),\n ...(this.store.managerSettings.extraTags?.[extension.name] ?? []),\n ]);\n\n for (const tag of allTags) {\n invariant(isExtensionTag(tag), {\n code: ErrorConstant.EXTENSION,\n message: `The tag provided by the extension: ${extension.constructorName} is not supported by the editor. To add custom tags you can use the 'mutateTag' method.`,\n });\n\n // Add tags to the combined tags stored here.\n this.allTags[tag].push(extension.name);\n\n if (isPlainExtension(extension)) {\n this.plainTags[tag].push(extension.name);\n }\n\n if (isMarkExtension(extension)) {\n this.markTags[tag].push(extension.name);\n }\n\n if (isNodeExtension(extension)) {\n this.nodeTags[tag].push(extension.name);\n }\n }\n\n // All tags available.\n extension.tags = [...allTags];\n }\n}\n\n/**\n * Check if the provided string is an extension tag.\n */\nexport function isExtensionTag(value: string): value is ExtensionTagType {\n return includes(values(ExtensionTag), value);\n}\n\n/**\n * The shape of the tag data stored by the extension manager.\n *\n * This data can be used by other extensions to dynamically determine which\n * nodes should affected by commands / plugins / keys etc...\n */\nexport type CombinedTags<Name extends string = string> = Record<ExtensionTagType, Name[]>;\n\ndeclare global {\n namespace Remirror {\n interface ManagerSettings {\n /**\n * Add extra tags to the extensions by name. This can be used to add\n * behavior traits to certain extensions.\n *\n * Please note this will change the schema since the tags are added to the\n * node and mark groups.\n *\n * ```ts\n * RemirrorManager.create(\n * [],\n * { extraTags: { bold: [ExtensionTag.Awesome] } }\n * );\n * ```\n */\n extraTags?: Record<string, ExtensionTagType[]>;\n }\n\n interface BaseExtension {\n /**\n * The generated tags for this extension are added here. Do not add this\n * property to your extensions as it will be overridden.\n */\n tags: ExtensionTagType[];\n\n /**\n * Dynamically create tags for the extension.\n *\n * Tags are a helpful tool for categorizing the behavior of an extension.\n * This behavior is later grouped in the `Manager` and passed to the\n * `extensionStore`. Tags can be used by commands that need to remove all\n * formatting and use the tag to identify which registered extensions are\n * formatters.\n *\n * @remarks\n *\n * Tags are also automatically added to the node and mark extensions as a\n * group when they are found there.\n *\n * There are internally defined tags but it's also possible to define any\n * custom string as a tag. See [[`ExtensionTag`]].\n */\n createTags?(): ExtensionTagType[];\n }\n type A = UseDefault<never, string>;\n\n interface ManagerStore<Extension extends AnyExtension> {\n /**\n * All the tags provided by the configured extensions.\n */\n tags: Readonly<\n CombinedTags<GetNameUnion<Extension> extends never ? string : GetNameUnion<Extension>>\n >;\n\n /**\n * All the plain extension tags provided for the editor.\n */\n plainTags: Readonly<\n CombinedTags<\n GetPlainNameUnion<Extension> extends never ? string : GetPlainNameUnion<Extension>\n >\n >;\n\n /**\n * All the node extension tags provided for the editor.\n */\n nodeTags: Readonly<\n CombinedTags<\n GetNodeNameUnion<Extension> extends never ? string : GetNodeNameUnion<Extension>\n >\n >;\n\n /**\n * All the mark extension tags provided for the editor.\n */\n markTags: Readonly<\n CombinedTags<\n GetMarkNameUnion<Extension> extends never ? string : GetMarkNameUnion<Extension>\n >\n >;\n }\n\n interface ExtensionStore {\n /**\n * All the tags provided by the configured extensions.\n */\n tags: CombinedTags;\n\n /**\n * All the plain extension tags provided for the editor.\n */\n plainTags: CombinedTags;\n\n /**\n * All the node extension tags provided for the editor.\n */\n nodeTags: CombinedTags;\n\n /**\n * All the mark extension tags provided for the editor.\n */\n markTags: CombinedTags;\n }\n\n interface BaseExtensionOptions {\n /**\n * Add extra tags to the extension.\n *\n * Tags can be used to unlock certain behavioural traits for nodes and\n * marks.\n *\n * Please note this will change the schema since the tags are added to the\n * node and mark groups.\n */\n extraTags?: ExtensionTagType[];\n }\n\n interface AllExtensions {\n tags: TagsExtension;\n }\n }\n}\n", "import { EditorState, Plugin, PluginKey, Transaction } from '@remirror/pm/state';\nimport { Decoration, DecorationSet } from '@remirror/pm/view';\n\nimport { ActionType, PlaceholderPluginAction } from './file-placeholder-actions';\n\ninterface UploadPlaceholderPluginData {\n set: DecorationSet;\n payloads: Map<string, any>;\n}\n\nconst key = new PluginKey<UploadPlaceholderPluginData>('remirrorFilePlaceholderPlugin');\n\nexport function createUploadPlaceholderPlugin(): Plugin<UploadPlaceholderPluginData> {\n const plugin: Plugin<UploadPlaceholderPluginData> = new Plugin<UploadPlaceholderPluginData>({\n key: key,\n state: {\n init(): UploadPlaceholderPluginData {\n return { set: DecorationSet.empty, payloads: new Map<string, any>() };\n },\n apply(tr, { set, payloads }: UploadPlaceholderPluginData): UploadPlaceholderPluginData {\n // Adjust decoration positions to changes made by the transaction\n set = set.map(tr.mapping, tr.doc);\n // See if the transaction adds or removes any placeholders\n const action = tr.getMeta(plugin) as PlaceholderPluginAction | null;\n\n if (action) {\n if (action.type === ActionType.ADD_PLACEHOLDER) {\n const widget = document.createElement('placeholder');\n const deco = Decoration.widget(action.pos, widget, { id: action.id });\n set = set.add(tr.doc, [deco]);\n payloads.set(action.id, action.payload);\n } else if (action.type === ActionType.REMOVE_PLACEHOLDER) {\n set = set.remove(set.find(undefined, undefined, (spec) => spec.id === action.id));\n payloads.delete(action.id);\n }\n }\n\n return { set, payloads };\n },\n },\n props: {\n decorations(state) {\n return plugin.getState(state)?.set ?? null;\n },\n },\n });\n return plugin;\n}\n\n/**\n * Try to find the position of the placeholder in the document based on the\n * upload placeholder id\n *\n * @remarks\n *\n * This function will first try to find the position based on the decoration set.\n * However, in some cases (e.g. `ReplaceStep`) the decoration will not be\n * available. In that case, it will then try to find every node in the document\n * recursively, which is much slower than the decoration set way in a large\n * document.\n */\nexport function findUploadPlaceholderPos(state: EditorState, id: string): number | undefined {\n const set = key.getState(state)?.set;\n\n if (set) {\n const decorations = set.find(undefined, undefined, (spec) => spec.id === id);\n const pos = decorations?.[0]?.from;\n\n if (pos != null) {\n return pos;\n }\n }\n\n let foundPos: number | undefined;\n state.doc.descendants((node, pos) => {\n if (node.attrs.id === id) {\n foundPos = pos;\n }\n\n return foundPos === undefined; // return false to stop the descent\n });\n return foundPos;\n}\n\nexport function findUploadPlaceholderPayload(state: EditorState, id: string): any | undefined {\n const payloads = key.getState(state)?.payloads;\n\n if (!payloads) {\n return undefined;\n }\n\n return payloads.get(id);\n}\n\n/**\n * Determine if there are active file uploads in the given state\n *\n * @remarks\n * This utility is useful to warn users there are still active uploads before\n * exiting or saving a document.\n *\n * @see https://remirror.vercel.app/?path=/story/extensions-file--with-upload-incomplete-warning\n *\n * @param state - the editor state\n */\nexport function hasUploadingFile(state: EditorState): boolean {\n const placeholderCount = key.getState(state)?.payloads?.size ?? 0;\n return placeholderCount > 0;\n}\n\nexport function setUploadPlaceholderAction(\n tr: Transaction,\n action: PlaceholderPluginAction,\n): Transaction {\n return tr.setMeta(key, action);\n}\n", "import { isNumber, uniqueId } from '@remirror/core-helpers';\nimport { ProsemirrorNode } from '@remirror/pm';\nimport { NodeType } from '@remirror/pm/model';\nimport { EditorView } from '@remirror/pm/view';\n\nimport { ActionType } from './file-placeholder-actions';\nimport { findUploadPlaceholderPos, setUploadPlaceholderAction } from './file-placeholder-plugin';\nimport { FileUploader } from './file-uploader';\nimport { createUploadContext, UploadContext } from './upload-context';\n\n/**\n * Any `ProsemirrorNode` can use the `uploadFile` function in this file as long\n * as its attributes implement this interface.\n */\ninterface AbstractNodeAttributes {\n // A temporary unique during the upload progress.\n id?: any;\n\n // The reason of the upload failure.\n error?: string | null;\n}\n\nexport type UploadFileHandler<NodeAttributes> = () => FileUploader<NodeAttributes>;\n\nexport interface UploadPlaceholderPayload<NodeAttributes extends AbstractNodeAttributes> {\n context: UploadContext;\n fileUploader: FileUploader<NodeAttributes>;\n}\n\nexport interface UploadFileProps<NodeAttributes extends AbstractNodeAttributes = object> {\n file: File;\n pos: number | undefined;\n view: EditorView;\n fileType: NodeType;\n uploadHandler: UploadFileHandler<NodeAttributes>;\n}\n\n/**\n * Insert a file into the editor and upload it.\n */\nexport function uploadFile<NodeAttributes extends AbstractNodeAttributes>({\n file,\n pos,\n view,\n fileType,\n uploadHandler,\n}: UploadFileProps<NodeAttributes>): void {\n const id = uniqueId('file-placeholder-');\n\n const context = createUploadContext();\n\n const fileUploader = createFilePlaceholder<NodeAttributes>({\n id,\n context,\n file,\n pos,\n view,\n fileType,\n uploadHandler,\n });\n\n fileUploader\n ?.upload(context)\n .then((attrs) => onFileLoaded({ id, fileType, view, attrs }))\n .catch((error) => onFileLoaded({ id, fileType, view, attrs: { error: error.message } }));\n}\n\n/**\n * Try to find a point where a node of the given type can be inserted\n * near `pos`, by searching up the node hierarchy when `pos` itself\n * isn't a valid place. Return null if no position was found.\n *\n * This function is similar to `insertPoint` from `prosemirror-transform`,\n * but it will also search for a valid position even if the `pos` is in the\n * middle of a node.\n */\nfunction insertFilePoint(doc: ProsemirrorNode, pos: number, nodeType: NodeType): number | null {\n const $pos = doc.resolve(pos);\n\n if ($pos.parent.canReplaceWith($pos.index(), $pos.index(), nodeType)) {\n return pos;\n }\n\n if ($pos.parentOffset === 0) {\n for (let d = $pos.depth - 1; d >= 0; d--) {\n const index = $pos.index(d);\n\n if ($pos.node(d).canReplaceWith(index, index, nodeType)) {\n return $pos.before(d + 1);\n }\n\n if (index > 0) {\n return null;\n }\n }\n }\n\n for (let d = $pos.depth - 1; d >= 0; d--) {\n const index = $pos.indexAfter(d);\n\n if ($pos.node(d).canReplaceWith(index, index, nodeType)) {\n return $pos.after(d + 1);\n }\n\n if (index < $pos.node(d).childCount) {\n return null;\n }\n }\n\n return null;\n}\n\nfunction createFilePlaceholder<NodeAttributes extends AbstractNodeAttributes>({\n id,\n context,\n file,\n pos,\n view,\n fileType,\n uploadHandler,\n}: {\n id: string;\n context: UploadContext;\n file: File;\n pos: number | undefined;\n view: EditorView;\n fileType: NodeType;\n uploadHandler: UploadFileHandler<NodeAttributes>;\n}): FileUploader<NodeAttributes> | void {\n const tr = view.state.tr;\n const insertPos = insertFilePoint(tr.doc, isNumber(pos) ? pos : tr.selection.from, fileType);\n\n if (!isNumber(insertPos)) {\n // failed to find a postition to insert the file node\n return;\n }\n\n // create a fileUploader, which will read and/or upload the file later\n const fileUploader = uploadHandler();\n\n // insert the file node\n const attrs: NodeAttributes = { ...fileUploader.insert(file), id };\n tr.insert(insertPos, fileType.createChecked(attrs));\n\n // insert the placeholder decoration\n const payload: UploadPlaceholderPayload<NodeAttributes> = { context, fileUploader };\n setUploadPlaceholderAction(tr, { type: ActionType.ADD_PLACEHOLDER, id, pos: insertPos, payload });\n\n view.dispatch(tr);\n\n return fileUploader;\n}\n\nfunction onFileLoaded<NodeAttributes extends AbstractNodeAttributes>({\n id,\n attrs,\n fileType,\n view,\n}: {\n id: string;\n attrs: NodeAttributes;\n fileType: NodeType;\n view: EditorView;\n}) {\n const placeholderPos = findUploadPlaceholderPos(view.state, id);\n\n // unexpected\n if (placeholderPos == null) {\n return;\n }\n\n const $pos = view.state.doc.resolve(placeholderPos);\n const fileNode = $pos.nodeAfter;\n\n // if the file node around the placeholder has been deleted, then delete\n // the placeholder and drop the uploaded file.\n if (!fileNode || fileNode.type !== fileType || fileNode.attrs.id !== id) {\n const tr = view.state.tr;\n setUploadPlaceholderAction(tr, { type: ActionType.REMOVE_PLACEHOLDER, id });\n view.dispatch(tr);\n return;\n }\n\n // Update the file node at the placeholder's position, and remove\n // the placeholder.\n const tr = view.state.tr;\n setUploadPlaceholderAction(tr, { type: ActionType.REMOVE_PLACEHOLDER, id });\n const fileAttrs: NodeAttributes = { ...fileNode.attrs, ...attrs, id: null };\n // We need to update the node to trigger the render function, which will accept\n // different properties during and after the upload progress.\n tr.setNodeMarkup(placeholderPos, undefined, fileAttrs);\n view.dispatch(tr);\n}\n", "import { createNanoEvents } from 'nanoevents';\n\nexport interface UploadContext {\n // Sets a key-value pair in the context.\n set: (key: string, value: unknown) => void;\n\n // Gets a value from the context. Returns undefined if the value is not set.\n get: (key: string) => unknown;\n\n // Add a listener when a key is set. Returns a function that removes the listener.\n // The listener will be called with an object with all the keys and values.\n addListener: (listener: UploadContextListener) => () => void;\n}\n\ntype UploadContextListener = (values: Record<string, unknown>) => void;\n\ninterface UploadContextEvents {\n set: UploadContextListener;\n}\n\nexport function createUploadContext(): UploadContext {\n const values: Record<string, unknown> = {};\n const emitter = createNanoEvents<UploadContextEvents>();\n\n const get = (key: string): unknown => {\n return values[key];\n };\n\n const set = (key: string, value: unknown) => {\n values[key] = value;\n emitter.emit('set', values);\n };\n\n const addListener = (listener: UploadContextListener) => {\n return emitter.on('set', listener);\n };\n\n return { set, get, addListener };\n}\n", "import { ProsemirrorPlugin } from '@remirror/pm';\n\nimport { PlainExtension } from '../../extension';\nimport { createUploadPlaceholderPlugin } from './file-placeholder-plugin';\n\ninterface DecorationsOptions {}\n\n/**\n * `UploadExtension` handle the file upload process.\n */\nexport class UploadExtension extends PlainExtension<DecorationsOptions> {\n get name() {\n return 'upload' as const;\n }\n\n /**\n * Create the extension plugin for inserting decorations into the editor.\n */\n createExternalPlugins(): ProsemirrorPlugin[] {\n return [createUploadPlaceholderPlugin()];\n }\n}\n\ndeclare global {\n namespace Remirror {\n interface AllExtensions {\n upload: UploadExtension;\n }\n }\n}\n", "import { ExtensionPriority } from '@remirror/core-constants';\nimport type { AnyFunction, CommandFunction, Static, Transaction } from '@remirror/core-types';\nimport { environment } from '@remirror/core-utils';\n\nimport { AnyExtension, extension, PlainExtension } from '../extension';\nimport type { CreateExtensionPlugin } from '../types';\n\nexport interface MetaOptions {\n /**\n * Set to true to capture meta data on commands and keybindings. This creates\n * a wrapper around every command and keybinding and as a result it may lead\n * to a performance penalty.\n */\n capture?: Static<boolean>;\n}\n\n/**\n * Support meta data for commands and key bindings.\n *\n * Metadata is dded to all commands and keybindings and that information is\n * provided to the `onChange` handle whenever the state is updated.\n *\n * @internalremarks\n *\n * TODO capture keybindings as well. This will be more difficult since\n * keybindings can dynamically be added to the editor.\n */\n@extension<MetaOptions>({\n defaultOptions: {\n capture: environment.isDevelopment,\n },\n staticKeys: ['capture'],\n defaultPriority: ExtensionPriority.Highest,\n})\nexport class MetaExtension extends PlainExtension<MetaOptions> {\n get name() {\n return 'meta' as const;\n }\n\n onCreate(): void {\n this.store.setStoreKey('getCommandMeta', this.getCommandMeta.bind(this));\n\n if (!this.options.capture) {\n return;\n }\n\n for (const extension of this.store.extensions) {\n this.captureCommands(extension);\n this.captureKeybindings(extension);\n }\n }\n\n /**\n * This is here to provide a\n */\n createPlugin(): CreateExtensionPlugin {\n return {};\n }\n\n /**\n * Intercept command names and attributes.\n */\n private captureCommands(extension: AnyExtension) {\n const decoratedCommands = extension.decoratedCommands ?? {};\n const createCommands = extension.createCommands;\n\n for (const name of Object.keys(decoratedCommands)) {\n const command: AnyFunction<CommandFunction> = (extension as any)[name];\n (extension as any)[name] =\n (...args: any[]): CommandFunction =>\n (props) => {\n const value = command(...args)(props);\n\n if (props.dispatch && value) {\n this.setCommandMeta(props.tr, {\n type: 'command',\n chain: props.dispatch !== props.view?.dispatch,\n name: name,\n extension: extension.name,\n decorated: true,\n });\n }\n\n return value;\n };\n }\n\n if (createCommands) {\n extension.createCommands = () => {\n const commandsObject = createCommands();\n\n for (const [name, command] of Object.entries(commandsObject)) {\n commandsObject[name] =\n (...args: any[]) =>\n (props) => {\n const value = command(...args)(props);\n\n if (props.dispatch && value) {\n this.setCommandMeta(props.tr, {\n type: 'command',\n chain: props.dispatch !== props.view?.dispatch,\n name: name,\n extension: extension.name,\n decorated: false,\n });\n }\n\n return value;\n };\n }\n\n return commandsObject;\n };\n }\n }\n\n /**\n * Intercept command name and attributes.\n */\n private captureKeybindings(_: AnyExtension) {}\n\n /**\n * Get the command metadata.\n */\n private getCommandMeta(tr: Transaction): Metadata[] {\n return tr.getMeta(this.pluginKey) ?? [];\n }\n\n private setCommandMeta(tr: Transaction, update: Metadata) {\n const meta = this.getCommandMeta(tr);\n tr.setMeta(this.pluginKey, [...meta, update]);\n }\n}\n\ninterface CommandMetadata {\n type: 'command';\n\n /**\n * Was this called as part of a chain?\n */\n chain: boolean;\n\n /**\n * Is this a decorated command?\n */\n decorated: boolean;\n\n /**\n * The name of the extension.\n */\n extension: string;\n\n /**\n * The name of the command that was called.\n */\n name: string;\n}\n\ninterface KeyBindingMetadata {\n type: 'keyBinding';\n\n /**\n * The name of the extension used.\n */\n extension: string;\n\n /**\n * The shortcut used to invoke this keybinding.\n */\n shortcut: string;\n}\n\nexport type Metadata = CommandMetadata | KeyBindingMetadata;\n\ndeclare global {\n namespace Remirror {\n interface ManagerStore<Extension extends AnyExtension> {\n /**\n * Get the command metadata for the transaction.\n * @internal\n */\n getCommandMeta(tr: Transaction): Metadata[];\n }\n interface AllExtensions {\n meta: MetaExtension;\n }\n }\n}\n", "import { createNanoEvents } from 'nanoevents';\nimport { ErrorConstant } from '@remirror/core-constants';\nimport {\n cx,\n invariant,\n isEmptyArray,\n isFunction,\n isNumber,\n object,\n omitUndefined,\n pick,\n uniqueArray,\n uniqueId,\n} from '@remirror/core-helpers';\nimport type {\n EditorState,\n EditorView,\n PrimitiveSelection,\n RemirrorContentType,\n Shape,\n Transaction,\n} from '@remirror/core-types';\n\nimport type { BuiltinPreset, UpdatableViewProps } from '../builtins';\nimport type { AnyExtension, CommandsFromExtensions } from '../extension';\nimport type { RemirrorManager } from '../manager';\nimport type { FocusType, StateUpdateLifecycleProps } from '../types';\nimport type {\n AddFrameworkHandler,\n BaseFramework,\n CreateStateFromContent,\n FrameworkEvents,\n FrameworkOptions,\n FrameworkOutput,\n FrameworkProps,\n ListenerProps,\n RemirrorEventListenerProps,\n TriggerChangeProps,\n UpdatableViewPropsObject,\n UpdateStateProps,\n} from './base-framework';\n\n/**\n * This is the `Framework` class which is used to create an abstract class for\n * implementing `Remirror` into the framework of your choice.\n *\n * The best way to learn how to use it is to take a look at the [[`DomFramework`]]\n * and [[`ReactFramework`]] implementations.\n *\n * @remarks\n *\n * There are two methods and one getter property which must be implemented for this\n */\nexport abstract class Framework<\n Extension extends AnyExtension = BuiltinPreset,\n Props extends FrameworkProps<Extension> = FrameworkProps<Extension>,\n Output extends FrameworkOutput<Extension> = FrameworkOutput<Extension>,\n> implements BaseFramework<Extension>\n{\n /**\n * A unique ID for the editor which can also be used as a key in frameworks\n * that need it.\n */\n readonly #uid = uniqueId();\n\n /**\n * A method which enables retrieving the props from the editor.\n */\n #getProps: () => Props;\n\n /**\n * The private reference to the previous state.\n */\n #previousState: EditorState | undefined;\n\n /**\n * A previous state that can be overridden by the framework implementation.\n */\n protected previousStateOverride?: EditorState;\n\n /**\n * True when this is the first render.\n */\n #firstRender = true;\n\n /**\n * The event listener which allows consumers to subscribe to the different\n * events taking place in the editor. Events currently supported are:\n *\n * - `destroy`\n * - `focus`\n * - `blur`\n * - `updated`\n */\n #events = createNanoEvents<FrameworkEvents<Extension>>();\n\n /**\n * The event listener which allows consumers to subscribe to the different\n * events taking place in the editor. Events currently supported are:\n *\n * - `destroy`\n * - `focus`\n * - `blur`\n * - `updated`\n */\n protected get addHandler(): AddFrameworkHandler<Extension> {\n return (this.#addHandler ??= this.#events.on.bind(this.#events));\n }\n\n /**\n * The handler which is bound to the events listener object.\n */\n #addHandler?: AddFrameworkHandler<Extension>;\n\n /**\n * The updatable view props.\n */\n protected get updatableViewProps(): UpdatableViewPropsObject {\n return {\n attributes: () => this.getAttributes(),\n editable: () => this.props.editable ?? true,\n };\n }\n\n /**\n * True when this is the first render of the editor.\n */\n protected get firstRender(): boolean {\n return this.#firstRender;\n }\n\n /**\n * Store the name of the framework.\n */\n abstract get name(): string;\n\n /**\n * The props passed in when creating or updating the `Framework` instance.\n */\n get props(): Props {\n return this.#getProps();\n }\n\n /**\n * Returns the previous editor state. On the first render it defaults to\n * returning the current state. For the first render the previous state and\n * current state will always be equal.\n */\n protected get previousState(): EditorState {\n return this.previousStateOverride ?? this.#previousState ?? this.initialEditorState;\n }\n\n /**\n * The instance of the [[`RemirrorManager`]].\n */\n protected get manager(): RemirrorManager<Extension> {\n return this.props.manager;\n }\n\n /**\n * The ProseMirror [[`EditorView`]].\n */\n protected get view(): EditorView {\n return this.manager.view;\n }\n\n /**\n * A unique id for the editor. Can be used to differentiate between editors.\n *\n * Please note that this ID is only locally unique, it should not be used as a\n * database key.\n */\n protected get uid(): string {\n return this.#uid;\n }\n\n #initialEditorState: EditorState;\n\n /**\n * The initial editor state from when the editor was first created.\n */\n get initialEditorState(): EditorState {\n return this.#initialEditorState;\n }\n\n constructor(options: FrameworkOptions<Extension, Props>) {\n const { getProps, initialEditorState, element } = options;\n\n this.#getProps = getProps;\n this.#initialEditorState = initialEditorState;\n\n // Attach the framework instance to the manager. The manager will set up the\n // update listener and manage updates to the instance of the framework\n // automatically.\n this.manager.attachFramework(this, this.updateListener.bind(this));\n\n if (this.manager.view) {\n return;\n }\n\n // Create the ProsemirrorView and initialize our editor manager with it.\n const view = this.createView(initialEditorState, element);\n this.manager.addView(view);\n }\n\n /**\n * Setup the manager event listeners which are disposed of when the manager is\n * destroyed.\n */\n private updateListener(props: StateUpdateLifecycleProps) {\n const { state, tr } = props;\n return this.#events.emit('updated', this.eventListenerProps({ state, tr }));\n }\n\n /**\n * Update the constructor props passed in. Useful for frameworks like react\n * where props are constantly changing and when using hooks function closures\n * can become stale.\n *\n * You can call the update method with the new `props` to update the internal\n * state of this instance.\n */\n update(options: FrameworkOptions<Extension, Props>): this {\n const { getProps } = options;\n this.#getProps = getProps;\n\n return this;\n }\n\n /**\n * Retrieve the editor state.\n */\n protected readonly getState = (): EditorState => this.view.state ?? this.initialEditorState;\n\n /**\n * Retrieve the previous editor state.\n */\n protected readonly getPreviousState = (): EditorState => this.previousState;\n\n /**\n * This method must be implement by the extending framework class. It returns\n * an [[`EditorView`]] which is added to the [[`RemirrorManager`]].\n */\n protected abstract createView(state: EditorState, element?: Element): EditorView;\n\n /**\n * This is used to implement how the state updates are used within your\n * application instance.\n *\n * It must be implemented.\n */\n protected abstract updateState(props: UpdateStateProps): void;\n\n /**\n * Update the view props.\n */\n protected updateViewProps(...keys: UpdatableViewProps[]): void {\n const props = pick(this.updatableViewProps, keys);\n\n this.view.setProps({ ...this.view.props, ...props });\n }\n\n /**\n * This sets the attributes for the ProseMirror Dom node.\n */\n protected getAttributes(ssr?: false): Record<string, string>;\n protected getAttributes(ssr: true): Shape;\n protected getAttributes(ssr?: boolean): Shape {\n const { attributes, autoFocus, classNames = [], label, editable } = this.props;\n const managerAttributes = this.manager.store?.attributes;\n\n // The attributes which were passed in as props.\n const propAttributes = isFunction(attributes)\n ? attributes(this.eventListenerProps())\n : attributes;\n\n // Whether or not the editor is focused.\n let focus: Shape = {};\n\n // In Chrome 84 when autofocus is set to any value including `\"false\"` it\n // will actually trigger the autofocus. This check makes sure there is no\n // `autofocus` attribute attached unless `autoFocus` is expressly a truthy\n // value.\n if (autoFocus || isNumber(autoFocus)) {\n focus = ssr ? { autoFocus: true } : { autofocus: 'true' };\n }\n\n const uniqueClasses = uniqueArray(\n cx(ssr && 'Prosemirror', 'remirror-editor', managerAttributes?.class, ...classNames).split(\n ' ',\n ),\n ).join(' ');\n\n const defaultAttributes = {\n role: 'textbox',\n ...focus,\n 'aria-multiline': 'true',\n ...(!(editable ?? true) ? { 'aria-readonly': 'true' } : {}),\n 'aria-label': label ?? '',\n ...managerAttributes,\n class: uniqueClasses,\n };\n\n return omitUndefined({ ...defaultAttributes, ...propAttributes }) as Shape;\n }\n\n /**\n * Part of the Prosemirror API and is called whenever there is state change in\n * the editor.\n *\n * @internalremarks\n * How does it work when transactions are dispatched one after the other.\n */\n protected readonly dispatchTransaction = (tr: Transaction): void => {\n // This should never happen, but it may have slipped through in the certain places.\n invariant(!this.manager.destroyed, {\n code: ErrorConstant.MANAGER_PHASE_ERROR,\n message:\n 'A transaction was dispatched to a manager that has already been destroyed. Please check your set up, or open an issue.',\n });\n\n tr = this.props.onDispatchTransaction?.(tr, this.getState()) ?? tr;\n\n const previousState = this.getState();\n const { state, transactions } = previousState.applyTransaction(tr);\n\n this.#previousState = previousState;\n\n // Use the abstract method to update the state.\n this.updateState({ state, tr, transactions });\n\n // Update the view props when an update is requested\n const forcedUpdates = this.manager.store.getForcedUpdates(tr);\n\n if (!isEmptyArray(forcedUpdates)) {\n this.updateViewProps(...forcedUpdates);\n }\n };\n\n /**\n * Adds `onBlur` and `onFocus` listeners.\n *\n * When extending this class make sure to call this method once\n * `ProsemirrorView` has been added to the dom.\n */\n protected addFocusListeners(): void {\n this.view.dom.addEventListener('blur', this.onBlur);\n this.view.dom.addEventListener('focus', this.onFocus);\n }\n\n /**\n * Remove `onBlur` and `onFocus` listeners.\n *\n * When extending this class in your framework, make sure to call this just\n * before the view is destroyed.\n */\n protected removeFocusListeners(): void {\n this.view.dom.removeEventListener('blur', this.onBlur);\n this.view.dom.removeEventListener('focus', this.onFocus);\n }\n\n /**\n * Called when the component unmounts and is responsible for cleanup.\n *\n * @remarks\n *\n * - Removes listeners for the editor `blur` and `focus` events\n */\n destroy(): void {\n // Let it clear that this instance has been destroyed.\n this.#events.emit('destroy');\n\n if (this.view) {\n // Remove the focus and blur listeners.\n this.removeFocusListeners();\n }\n }\n\n /**\n * Use this method in the `onUpdate` event to run all change handlers.\n */\n readonly onChange = (props: ListenerProps = object()): void => {\n const onChangeProps = this.eventListenerProps(props);\n\n if (this.#firstRender) {\n this.#firstRender = false;\n }\n\n this.props.onChange?.(onChangeProps);\n };\n\n /**\n * Listener for editor 'blur' events\n */\n private readonly onBlur = (event: Event) => {\n const props = this.eventListenerProps();\n\n this.props.onBlur?.(props, event);\n this.#events.emit('blur', props, event);\n };\n\n /**\n * Listener for editor 'focus' events\n */\n private readonly onFocus = (event: Event) => {\n const props = this.eventListenerProps();\n\n this.props.onFocus?.(props, event);\n this.#events.emit('focus', props, event);\n };\n\n /**\n * Sets the content of the editor. This bypasses the update function.\n *\n * @param content\n * @param triggerChange\n */\n private readonly setContent = (\n content: RemirrorContentType,\n { triggerChange = false }: TriggerChangeProps = {},\n ) => {\n const { doc } = this.manager.createState({ content });\n const previousState = this.getState();\n const { state } = this.getState().applyTransaction(\n previousState.tr.replaceRangeWith(0, previousState.doc.nodeSize - 2, doc),\n );\n\n if (triggerChange) {\n return this.updateState({ state, triggerChange });\n }\n\n this.view.updateState(state);\n };\n\n /**\n * Clear the content of the editor (reset to the default empty node).\n *\n * @param triggerChange - whether to notify the onChange handler that the\n * content has been reset\n */\n private readonly clearContent = ({ triggerChange = false }: TriggerChangeProps = {}) => {\n this.setContent(this.manager.createEmptyDoc(), { triggerChange });\n };\n\n /**\n * Creates the props passed into all event listener handlers. e.g.\n * `onChange`\n */\n protected eventListenerProps(\n props: ListenerProps = object(),\n ): RemirrorEventListenerProps<Extension> {\n const { state, tr, transactions } = props;\n\n return {\n tr,\n transactions,\n internalUpdate: !tr,\n view: this.view,\n firstRender: this.#firstRender,\n state: state ?? this.getState(),\n createStateFromContent: this.createStateFromContent,\n previousState: this.previousState,\n helpers: this.manager.store.helpers,\n };\n }\n\n protected readonly createStateFromContent: CreateStateFromContent = (content, selection) => {\n return this.manager.createState({ content, selection });\n };\n\n /**\n * Focus the editor.\n */\n protected readonly focus = (position?: FocusType): void => {\n (this.manager.store.commands as CommandsFromExtensions<BuiltinPreset>).focus(position);\n };\n\n /**\n * Blur the editor.\n */\n protected readonly blur = (position?: PrimitiveSelection): void => {\n (this.manager.store.commands as CommandsFromExtensions<BuiltinPreset>).blur(position);\n };\n\n /**\n * Methods and properties which are made available to all consumers of the\n * `Framework` class.\n */\n protected get baseOutput(): FrameworkOutput<Extension> {\n return {\n manager: this.manager,\n ...this.manager.store,\n addHandler: this.addHandler,\n\n // Commands\n focus: this.focus,\n blur: this.blur,\n\n // Properties\n uid: this.#uid,\n view: this.view,\n\n // Getter Methods\n getState: this.getState,\n getPreviousState: this.getPreviousState,\n getExtension: this.manager.getExtension.bind(this.manager),\n hasExtension: this.manager.hasExtension.bind(this.manager),\n\n // Setter Methods\n clearContent: this.clearContent,\n setContent: this.setContent,\n };\n }\n\n /**\n * Every framework implementation must provide it's own custom output.\n */\n abstract get frameworkOutput(): Output;\n}\n", "import { createNanoEvents, Unsubscribe } from 'nanoevents';\nimport {\n __INTERNAL_REMIRROR_IDENTIFIER_KEY__,\n ErrorConstant,\n ExtensionPriority,\n ManagerPhase,\n RemirrorIdentifier,\n} from '@remirror/core-constants';\nimport {\n freeze,\n getLazyArray,\n includes,\n invariant,\n isNullOrUndefined,\n isString,\n object,\n} from '@remirror/core-helpers';\nimport type {\n Dispose,\n EditorSchema,\n EditorView,\n MarkExtensionSpec,\n NodeExtensionSpec,\n PrimitiveSelection,\n ProsemirrorNode,\n RemirrorContentType,\n Replace,\n Simplify,\n Transaction,\n} from '@remirror/core-types';\nimport {\n createDocumentNode,\n CustomDocumentProps,\n getDocument,\n getTextSelection,\n InvalidContentHandler,\n isIdentifierOfType,\n isRemirrorType,\n NamedStringHandlers,\n StringHandler,\n StringHandlerProps,\n} from '@remirror/core-utils';\nimport { EditorState } from '@remirror/pm/state';\n\nimport { BuiltinPreset, builtinPreset, CombinedTags, CommandsExtension } from '../builtins';\nimport type {\n AnyExtension,\n AnyExtensionConstructor,\n AnyManagerStore,\n GetExtensions,\n GetMarkNameUnion,\n GetNameUnion,\n GetNodeNameUnion,\n GetPlainNameUnion,\n ManagerStoreKeys,\n} from '../extension';\nimport type { BaseFramework, FrameworkOutput } from '../framework';\nimport type { StateUpdateLifecycleProps } from '../types';\nimport {\n extractLifecycleMethods,\n ManagerLifecycleHandlers,\n transformExtensions,\n} from './remirror-manager-helpers';\n\n/**\n * The `Manager` has multiple hook phases which are able to hook into the\n * extension manager flow and add new functionality to the editor.\n *\n * The `ExtensionEventMethod`s\n *\n * - onCreate - when the extension manager is created and after the schema is\n * made available.\n * - onView - when the view has been received from the dom ref.\n */\n\n/**\n * A class to manage the extensions and prosemirror interactions within the\n * editor.\n *\n * @remarks\n *\n * The RemirrorManager enables the lifecycle methods of the extensions by\n * calling each method in the distinct phases of the lifecycle.\n *\n * - `onCreate` - This happens when the manager is constructed. It calls on the\n * extension which have an `onCreate` method and allows them to do their work.\n *\n * For the built in methods, this is when the `SchemaExtension` creates the\n * Schema and when the `TagsExtension` combines the tags for the editor\n * instance.\n *\n * ```ts\n * const manager = Manager.create(() => [\n * new DocExtension(),\n * new TextExtension(),\n * new ParagraphExtension(),\n * ])\n * ```\n *\n * At this point all the `onCreate` methods have been called. Including the\n * `onCreate` for the `Schema`.\n *\n * - `onView` - This is called the framework instance connects the\n * `RemirrorManager` to the ProseMirror EditorView.\n *\n * ```ts\n * manager.addView(new EditorView(...))\n * manager.store.commands.insertText('Hello world');.\n * ```\n *\n * - [[`onStateUpdate`]] - This is the method called every time the ProseMirror\n * state changes. Both the extensions and the `Framework` listen to this event\n * and can provide updates in response.\n */\nexport class RemirrorManager<Extension extends AnyExtension> {\n /**\n * Create the manager for your `Remirror` editor.\n */\n static create<Extension extends AnyExtension>(\n extensions: Extension[] | ExtensionTemplate<Extension>,\n settings: Remirror.ManagerSettings = {},\n ): RemirrorManager<Extension | BuiltinPreset> {\n return new RemirrorManager<Extension | BuiltinPreset>(\n [...getLazyArray(extensions), ...builtinPreset(settings.builtin)],\n settings,\n );\n }\n\n /**\n * Utility getter for storing the base method props which is available to\n * all extensions.\n */\n #extensionStore: Remirror.ExtensionStore;\n\n /**\n * The named string handlers\n */\n #stringHandlers: NamedStringHandlers = object();\n\n /**\n * The extension manager store.\n */\n #store: Remirror.ManagerStore<Extension> = object();\n\n /**\n * All the extensions being used within this editor.\n */\n #extensions: ReadonlyArray<GetExtensions<Extension>>;\n\n /**\n * The map of extension constructor to their extension counterparts. This is\n * what makes the `getExtension` method possible.\n */\n #extensionMap: WeakMap<AnyExtensionConstructor, GetExtensions<Extension>>;\n\n /**\n * The stage the manager is currently running.\n */\n #phase: ManagerPhase = ManagerPhase.None;\n\n /**\n * The settings used to create the manager.\n */\n #settings: Remirror.ManagerSettings;\n\n /**\n * When true this identifies this as the first state update since the view was\n * added to the editor.\n */\n #firstStateUpdate = true;\n\n /**\n * Store the handlers that will be run when for each event method.\n */\n #handlers: ManagerLifecycleHandlers = {\n create: [],\n view: [],\n update: [],\n destroy: [],\n };\n\n /**\n * The disposers for the editor.\n */\n #disposers: Array<() => void> = [];\n\n /**\n * The event listener which allows consumers to subscribe to the different\n * events without using props.\n */\n #events = createNanoEvents<ManagerEvents>();\n\n /**\n * The active framework for this manager if it exists.\n */\n #framework?: BaseFramework<Extension>;\n\n /**\n * A method for disposing the state update event listeners on the active\n * framework.\n */\n #disposeFramework?: Dispose;\n\n /**\n * Identifies this as a `Manager`.\n *\n * @internal\n */\n get [__INTERNAL_REMIRROR_IDENTIFIER_KEY__](): RemirrorIdentifier.Manager {\n return RemirrorIdentifier.Manager;\n }\n\n /**\n * Returns `true` if the manager has been destroyed.\n */\n get destroyed(): boolean {\n return this.#phase === ManagerPhase.Destroy;\n }\n\n /**\n * `true` when the view has been added to the UI layer and the editor is\n * running.\n */\n get mounted(): boolean {\n return this.#phase >= ManagerPhase.EditorView && this.#phase < ManagerPhase.Destroy;\n }\n\n /**\n * Retrieve the framework output.\n *\n * This be undefined if the manager hasn't been provided to a framework yet\n * the manager.\n *\n * With synchronous frameworks this means that it should only be accessed\n * after the manager has been applied to the editor creation function.\n *\n * For frameworks like React it is only available when the manager is provided\n * to the `Remirror` component and after the very first render. This means it\n * is available within the `onRef` callback.\n *\n * ```tsx\n * import React, { useEffect } from 'react';\n * import { useRemirror, Remirror } from '@remirror/react';\n *\n * const Editor = () => {\n * const { manager } = useRemirror();\n *\n * const callback = () => {\n * return manager.output; // \u2705 This is fine.\n * }\n *\n * useEffect(() => {\n * log(manager.output); // \u2705 This is also fine.\n * }, []);\n *\n * log(manager.output); // \u274C This will be undefined on the first render.\n *\n * return <Remirror manager={manager} />\n * }\n * ```\n */\n get output(): FrameworkOutput<Extension> | undefined {\n return this.#framework?.frameworkOutput;\n }\n\n /**\n * Returns true when a framework is attached to the manager.\n *\n * This can be used to check if it is safe to call `manager.output`.\n */\n get frameworkAttached(): boolean {\n return !!this.#framework;\n }\n\n /**\n * The extensions stored by this manager\n */\n get extensions(): ReadonlyArray<GetExtensions<Extension>> {\n return this.#extensions;\n }\n\n /**\n * The registered string handlers provided by the extensions.\n *\n * By default this includes `html` and `plainText`\n */\n get stringHandlers(): NamedStringHandlers {\n return this.#stringHandlers;\n }\n\n /**\n * Get the extension manager store which is accessible at initialization.\n */\n get store(): Remirror.ManagerStore<Extension> {\n return freeze(this.#store);\n }\n\n /**\n * Provides access to the extension store.\n */\n get extensionStore(): Remirror.ExtensionStore {\n return freeze(this.#extensionStore);\n }\n\n /**\n * Shorthand access to the active transaction from the manager. This is the\n * shared transaction available to all commands and should be used when you\n * need to make your commands chainable.\n *\n * If working with react and setting up your editor as a controlled component\n * then this is the preferred way to run custom commands, otherwise your\n * commands will end up being non-chainable and be overwritten by anything\n * that comes after.\n */\n get tr(): Transaction {\n return this.getExtension(CommandsExtension).transaction;\n }\n\n /**\n * Returns the stored nodes\n */\n get nodes(): Record<this['~N'], NodeExtensionSpec> {\n return this.#store.nodes;\n }\n\n /**\n * Returns the store marks.\n */\n get marks(): Record<this['~M'], MarkExtensionSpec> {\n return this.#store.marks;\n }\n\n /**\n * A shorthand method for retrieving the schema for this extension manager\n * from the data.\n */\n get schema(): EditorSchema {\n return this.#store.schema;\n }\n\n /**\n * A shorthand getter for retrieving the tags from the extension manager.\n */\n get extensionTags(): Readonly<CombinedTags<GetNameUnion<Extension>>> {\n return this.#store.tags;\n }\n\n /**\n * A shorthand way of retrieving the editor view.\n */\n get view(): EditorView {\n return this.#store.view;\n }\n\n /**\n * Retrieve the settings used when creating the manager.\n */\n get settings(): Remirror.ManagerSettings {\n return this.#settings;\n }\n\n /**\n * The document to use for rendering and outputting HTML.\n */\n get document(): Document {\n return this.#settings.document ?? getDocument();\n }\n\n /**\n * Creates the extension manager which is used to simplify the management of\n * the prosemirror editor.\n *\n * This is set to private to encourage using `RemirrorManager.create`\n * instead of the `new` keyword.\n */\n private constructor(\n initialExtension: readonly Extension[],\n settings: Remirror.ManagerSettings = {},\n ) {\n const { extensions, extensionMap } = transformExtensions<Extension>(initialExtension, settings);\n\n this.#settings = settings;\n this.#extensions = freeze(extensions);\n this.#extensionMap = extensionMap;\n this.#extensionStore = this.createExtensionStore();\n this.#phase = ManagerPhase.Create;\n\n this.setupLifecycleHandlers();\n\n for (const handler of this.#handlers.create) {\n const disposer = handler();\n\n if (disposer) {\n this.#disposers.push(disposer);\n }\n }\n }\n\n /**\n * Loops through all extensions to set up the lifecycle handlers.\n */\n private setupLifecycleHandlers(): void {\n const store = this.#extensionStore;\n const handlers = this.#handlers;\n\n const nodeNames: string[] = [];\n const markNames: string[] = [];\n const plainNames: string[] = [];\n\n // The names are stored as readonly arrays - which is the reason for not\n // just saying `store.nodeNames = []`.\n store.nodeNames = nodeNames;\n store.markNames = markNames;\n store.plainNames = plainNames;\n\n for (const extension of this.#extensions) {\n extractLifecycleMethods({ extension, nodeNames, markNames, plainNames, handlers, store });\n }\n }\n\n /**\n * Set the string handler to use for a given name.\n *\n * This allows users to set the string handler\n */\n private setStringHandler(name: keyof Remirror.StringHandlers, handler: StringHandler): void {\n this.#stringHandlers[name] = handler;\n }\n\n /**\n * Set the manager value for the provided key. This is used by extensions to\n * add data to the manager.\n */\n private setStoreKey<Key extends ManagerStoreKeys>(key: Key, value: AnyManagerStore[Key]): void {\n this.#store[key] = value;\n }\n\n /**\n * Get the manager value for the provided key. This is used by extensions to\n * get data from the manager.\n */\n private getStoreKey<Key extends ManagerStoreKeys>(key: Key): AnyManagerStore[Key] {\n const value = this.#store[key];\n\n invariant(!isNullOrUndefined(value), {\n code: ErrorConstant.MANAGER_PHASE_ERROR,\n message: '`getStoreKey` should not be called before the values are available.',\n });\n\n return value;\n }\n\n /**\n * A method to set values in the extension store which is made available to\n * extension.\n *\n * **NOTE** This method should only be used in the `onCreate` extension method\n * or it will throw an error.\n */\n private setExtensionStore<Key extends keyof Remirror.ExtensionStore>(\n key: Key,\n value: Remirror.ExtensionStore[Key],\n ) {\n invariant(this.#phase <= ManagerPhase.EditorView, {\n code: ErrorConstant.MANAGER_PHASE_ERROR,\n message:\n '`setExtensionStore` should only be called during the `onCreate` lifecycle hook. Make sure to only call it within the returned methods.',\n });\n\n this.#extensionStore[key] = value;\n }\n\n /**\n * Create the initial store.\n */\n private createExtensionStore(): Remirror.ExtensionStore {\n const store: Remirror.ExtensionStore = object();\n const enumerable = true;\n\n // Allow current state to default to `getState` for first access.\n // This fixed an issue with #814\n let currentState: EditorState | undefined;\n let previousState: EditorState | undefined;\n\n Object.defineProperties(store, {\n extensions: { get: () => this.#extensions, enumerable },\n phase: { get: () => this.#phase, enumerable },\n view: { get: () => this.view, enumerable },\n managerSettings: { get: () => freeze(this.#settings), enumerable },\n getState: { value: this.getState, enumerable },\n updateState: { value: this.updateState, enumerable },\n isMounted: { value: () => this.mounted, enumerable },\n getExtension: { value: this.getExtension.bind(this), enumerable },\n manager: { get: () => this, enumerable },\n document: { get: () => this.document, enumerable },\n stringHandlers: { get: () => this.#stringHandlers, enumerable },\n currentState: {\n get: () => (currentState ??= this.getState()),\n set: (state: EditorState) => {\n currentState = state;\n },\n enumerable,\n },\n previousState: {\n get: () => previousState,\n set: (state: EditorState) => {\n previousState = state;\n },\n enumerable,\n },\n });\n\n store.getStoreKey = this.getStoreKey.bind(this);\n store.setStoreKey = this.setStoreKey.bind(this);\n store.setExtensionStore = this.setExtensionStore.bind(this);\n store.setStringHandler = this.setStringHandler.bind(this);\n\n return store;\n }\n\n /**\n * A state getter method which is passed into the params.\n */\n private readonly getState = (): EditorState => {\n if (this.#phase >= ManagerPhase.EditorView) {\n return this.view.state;\n }\n\n invariant(this.#framework, {\n code: ErrorConstant.MANAGER_PHASE_ERROR,\n message:\n '`getState` can only be called after the `Framework` or the `EditorView` has been added to the manager`. Check your plugins to make sure that the decorations callback uses the state argument.',\n });\n\n return this.#framework?.initialEditorState;\n };\n\n /**\n * Stores the editor view on the manager\n *\n * @param view - the editor view\n */\n addView(view: EditorView): this {\n if (this.#phase >= ManagerPhase.EditorView) {\n // Do nothing since a view has already been added.\n return this;\n }\n\n this.#firstStateUpdate = true;\n\n // Update the lifecycle phase.\n this.#phase = ManagerPhase.EditorView;\n\n // Store the view.\n this.#store.view = view;\n\n for (const handler of this.#handlers.view) {\n const disposer = handler(view);\n\n if (disposer) {\n this.#disposers.push(disposer);\n }\n }\n\n return this;\n }\n\n /**\n * Attach a framework to the manager.\n */\n attachFramework(\n framework: BaseFramework<Extension>,\n updateHandler: (props: StateUpdateLifecycleProps) => void,\n ): void {\n if (this.#framework === framework) {\n // Do nothing if the instances are identical.\n return;\n }\n\n if (this.#framework) {\n // Destroy the old instance.\n this.#framework.destroy();\n\n // Remove the event listener. This should exist.\n this.#disposeFramework?.();\n }\n\n // Replace with the new instance.\n this.#framework = framework;\n this.#disposeFramework = this.addHandler('stateUpdate', updateHandler);\n }\n\n /* Public Methods */\n\n /**\n * Create an empty document for the editor based on the current schema.\n *\n * This automatically looks at the supported content for the doc and the\n * available nodes which fulfil that content in order to create a document\n * with only the minimal required content.\n *\n * This can be used in conjunction with the create state to reset the current\n * value of the editor.\n */\n createEmptyDoc(): ProsemirrorNode {\n const doc = this.schema.nodes.doc?.createAndFill();\n\n // Make sure the `doc` was created.\n invariant(doc, {\n code: ErrorConstant.INVALID_CONTENT,\n message: `An empty node could not be created due to an invalid schema.`,\n });\n\n return doc;\n }\n\n /**\n * Create the editor state from content passed to this extension manager.\n */\n createState(props: CreateEditorStateProps = {}): EditorState {\n const { onError, defaultSelection = 'end' } = this.settings;\n const {\n content = this.createEmptyDoc(),\n selection = defaultSelection,\n stringHandler = this.settings.stringHandler,\n } = props;\n const { schema, plugins } = this.store;\n\n const doc = createDocumentNode({\n stringHandler: isString(stringHandler) ? this.stringHandlers[stringHandler] : stringHandler,\n document: this.document,\n content,\n onError,\n schema,\n selection,\n });\n\n return EditorState.create({\n schema,\n doc,\n plugins,\n selection: getTextSelection(selection, doc),\n });\n }\n\n /**\n * Add a handler to the manager.\n *\n * Currently the only event that can be listened to is the `destroy` event.\n */\n addHandler<Key extends keyof ManagerEvents>(event: Key, cb: ManagerEvents[Key]): Unsubscribe {\n return this.#events.on(event, cb);\n }\n\n /**\n * Update the state of the view and trigger the `onStateUpdate` lifecycle\n * method as well.\n */\n private readonly updateState = (state: EditorState) => {\n const previousState = this.getState();\n\n this.view.updateState(state);\n this.onStateUpdate({ previousState, state });\n };\n\n /**\n * This method should be called by the view layer every time the state is\n * updated.\n *\n * An example usage of this is within the collaboration extension.\n */\n onStateUpdate(props: Omit<StateUpdateLifecycleProps, 'firstUpdate'>): void {\n const firstUpdate = this.#firstStateUpdate;\n\n this.#extensionStore.currentState = props.state;\n this.#extensionStore.previousState = props.previousState;\n\n if (firstUpdate) {\n this.#phase = ManagerPhase.Runtime;\n this.#firstStateUpdate = false;\n }\n\n const propsWithUpdate = { ...props, firstUpdate };\n\n for (const handler of this.#handlers.update) {\n handler(propsWithUpdate);\n }\n\n this.#events.emit('stateUpdate', propsWithUpdate);\n }\n\n /**\n * Get the extension instance matching the provided constructor from the\n * manager.\n *\n * This will throw an error if non existent.\n */\n getExtension<ExtensionConstructor extends AnyExtensionConstructor>(\n Constructor: ExtensionConstructor,\n ): InstanceType<ExtensionConstructor> {\n const extension = this.#extensionMap.get(Constructor);\n\n // Throws an error if attempting to get an extension which is not present in\n // the manager.\n invariant(extension, {\n code: ErrorConstant.INVALID_MANAGER_EXTENSION,\n message: `'${Constructor.name}' doesn't exist within this manager. Make sure it is properly added before attempting to use it.`,\n });\n\n return extension as InstanceType<ExtensionConstructor>;\n }\n\n /**\n * Determines in an extension is present by providing the desired\n * `Constructor`.\n *\n * This method can be used as a safer alternative to getExtension which\n * will throw an error if the constructor doesn't exist within the\n * extension created by this extension.\n */\n hasExtension<ExtensionConstructor extends AnyExtensionConstructor>(\n Constructor: ExtensionConstructor,\n ): boolean {\n const extension = this.#extensionMap.get(Constructor);\n return !!extension;\n }\n\n /**\n * Make a clone of the manager.\n *\n * @internalremarks What about the state stored in the extensions and presets,\n * does this need to be recreated as well?\n */\n clone(): RemirrorManager<Extension> {\n const extensions = this.#extensions.map((e) => e.clone(e.options));\n const manager = RemirrorManager.create(() => extensions, this.#settings);\n this.#events.emit('clone', manager);\n\n return manager;\n }\n\n /**\n * Recreate the manager with new settings and extensions\n */\n recreate<ExtraExtension extends AnyExtension>(\n extensions: ExtraExtension[] = [],\n settings: Remirror.ManagerSettings = {},\n ): RemirrorManager<Extension | ExtraExtension> {\n const currentExtensions = this.#extensions.map((e) => e.clone(e.initialOptions));\n const manager = RemirrorManager.create(() => [...currentExtensions, ...extensions], settings);\n this.#events.emit('recreate', manager);\n\n return manager;\n }\n\n /**\n * This method should be called to destroy the manager and remove the view.\n */\n destroy(): void {\n this.#phase = ManagerPhase.Destroy;\n\n for (const plugin of this.view?.state.plugins ?? []) {\n plugin.getState(this.view.state)?.destroy?.();\n }\n\n // Make sure to destroy the framework and it's state update listener if it\n // exists.\n this.#framework?.destroy();\n this.#disposeFramework?.();\n\n // Run all cleanup methods returned by the `onView` and `onCreate` methods.\n for (const dispose of this.#disposers) {\n dispose();\n }\n\n // TODO: prevent `dispatchTransaction` from being called again\n for (const onDestroy of this.#handlers.destroy) {\n onDestroy();\n }\n\n this.view?.destroy();\n\n this.#events.emit('destroy');\n }\n\n /**\n * Check whether the manager includes the names or constructors provided for\n * the preset and extensions.\n *\n * Returns true if all are included, returns false otherwise.\n */\n includes(mustIncludeList: Array<AnyExtensionConstructor | string>): boolean {\n // Searches can be made by either the name of the extension / preset or the\n // names of the constructor. We gather the values to check in separate\n // arrays\n const names: string[] = [];\n const extensionsAndPresets: AnyExtensionConstructor[] = [];\n\n for (const item of this.#extensions) {\n names.push(item.name, item.constructorName);\n extensionsAndPresets.push(item.constructor);\n }\n\n return mustIncludeList.every((item) =>\n isString(item) ? includes(names, item) : includes(extensionsAndPresets, item),\n );\n }\n}\n\n/**\n * A function that returns the extension to be used in the RemirrorManager. This\n * is similar to a preset function except that it takes no arguments.\n *\n * ```ts\n * import { RemirrorManager } from 'remirror';\n * import { BoldExtension, ItalicExtension } from 'remirror/extensions';\n *\n * const template = () => [new BoldExtension(), new ItalicExtension()]\n * const manager = RemirrorManager.create(template);\n * ```\n *\n * If the template is mixed in with other manager creators it will add the\n * relevant extension provided.\n */\nexport type ExtensionTemplate<Extension extends AnyExtension> = () => Extension[];\n\nexport interface ManagerEvents {\n /**\n * Called when the state is updated.\n */\n stateUpdate: (props: StateUpdateLifecycleProps) => void;\n\n /**\n * Called whenever the manager is cloned with the newly created manager\n * instance.\n *\n * This is mainly used for testing so that the RemirrorTester can always\n * reference the latest manager.\n */\n clone: (manager: AnyRemirrorManager) => void;\n\n /**\n * Called whenever the manager is recreated with the newly created manager\n * instance.\n *\n * This is mainly used for testing so that the RemirrorTester can always\n * reference the latest manager.\n */\n recreate: (manager: AnyRemirrorManager) => void;\n\n /**\n * An event listener which is called whenever the manager is destroyed.\n */\n destroy: () => void;\n}\n\nexport type AnyRemirrorManager = Simplify<\n Replace<\n RemirrorManager<AnyExtension>,\n {\n clone: () => AnyRemirrorManager;\n store: Replace<Remirror.ManagerStore<AnyExtension>, { chain: any }>;\n output:\n | Replace<FrameworkOutput<AnyExtension>, { chain: any; manager: AnyRemirrorManager }>\n | undefined;\n // output: any;\n // store: any;\n // attachFramework: any;\n view: EditorView;\n addView: (view: EditorView) => void;\n attachFramework: (\n framework: any,\n updateHandler: (props: StateUpdateLifecycleProps) => void,\n ) => void;\n /** @internal */\n ['~E']: AnyExtension;\n /** @internal */\n ['~AN']: string;\n /** @internal */\n ['~N']: string;\n /** @internal */\n ['~M']: string;\n /** @internal */\n ['~P']: string;\n }\n >\n>;\n\n/**\n * Checks to see whether the provided value is a `RemirrorManager` instance.\n *\n * An optional parameter `mustIncludeList` is available if you want to check\n * that the manager includes all the listed extensions.\n *\n * @param value - the value to check\n * @param mustIncludeList - an array of presets and extension the manager must\n * include to pass the test. The identifier can either be the Extension / Preset\n * name e.g. `bold`, or the Extension / Preset constructor `BoldExtension`\n */\nexport function isRemirrorManager<Extension extends AnyExtension = AnyExtension>(\n value: unknown,\n mustIncludeList?: Array<AnyExtensionConstructor | string>,\n): value is RemirrorManager<Extension> {\n if (!isRemirrorType(value) || !isIdentifierOfType(value, RemirrorIdentifier.Manager)) {\n return false;\n }\n\n // We can return true since there are no other checks to make.\n if (!mustIncludeList) {\n return true;\n }\n\n return (value as AnyRemirrorManager).includes(mustIncludeList);\n}\n\nexport interface CreateEditorStateProps extends Omit<StringHandlerProps, 'stringHandler'> {\n /**\n * This is where content can be supplied to the Editor.\n *\n * @remarks\n *\n * Content can either be\n * - a string (which will be parsed by the stringHandler)\n * - JSON object matching Prosemirror expected shape\n * - A top level ProsemirrorNode\n *\n * If this is left undefined then the editor will use the default empty `doc`.\n */\n content?: RemirrorContentType;\n\n /**\n * The selection that the user should have in the created node.\n *\n * @defaultValue 'end'\n */\n selection?: PrimitiveSelection;\n\n /**\n * A function which transforms a string into a prosemirror node.\n *\n * @remarks\n *\n * Can be used to transform markdown / html or any other string format into a\n * prosemirror node.\n *\n * See [[`fromHTML`]] for an example of how this could work.\n */\n stringHandler?: keyof Remirror.StringHandlers | StringHandler;\n}\n\ninterface RemirrorManagerConstructor extends Function {\n create<Extension extends AnyExtension>(\n extension: Extension[],\n settings?: Remirror.ManagerSettings,\n ): RemirrorManager<Extension | BuiltinPreset>;\n}\n\nexport interface RemirrorManager<Extension extends AnyExtension> {\n /**\n * The constructor for the [[`RemirrorManager`]].\n */\n constructor: RemirrorManagerConstructor;\n\n /**\n * Pseudo type property which contains the recursively extracted `Extension`\n * stored by this manager.\n *\n * @internal\n */\n ['~E']: Extension;\n\n /**\n * `AllNames`\n *\n * Get all the names of the extensions within this editor.\n *\n * @internal\n */\n ['~AN']: GetNameUnion<Extension> extends never ? string : GetNameUnion<Extension>;\n\n /**\n * `NodeNames`\n *\n * Type inference hack for node extension names. This is the only way I know\n * to store types on a class.\n *\n * @internal\n */\n ['~N']: GetNodeNameUnion<Extension> extends never ? string : GetNodeNameUnion<Extension>;\n\n /**\n * `MarkNames`\n *\n * Type inference hack for mark extension names. This is the only way I know\n * to store types on a class.\n *\n * @internal\n */\n ['~M']: GetMarkNameUnion<Extension> extends never ? string : GetMarkNameUnion<Extension>;\n\n /**\n * `PlainNames`\n *\n * Type inference hack for all the plain extension names. This is the only way\n * I know to store types on a class.\n *\n * @internal\n */\n ['~P']: GetPlainNameUnion<Extension> extends never ? string : GetPlainNameUnion<Extension>;\n}\n\ndeclare global {\n namespace Remirror {\n /**\n * Settings which can be passed into the manager.\n */\n interface ManagerSettings extends Partial<CustomDocumentProps> {\n /**\n * Set the extension priority for extension's by their name.\n */\n priority?: Record<string, ExtensionPriority>;\n\n /**\n * An object which excludes certain functionality from all extensions\n * within the manager.\n */\n exclude?: ExcludeOptions;\n\n /**\n * The error handler which is called when the JSON passed is invalid.\n *\n * @remarks\n *\n * The following can be used to setup the `onError` handler on the the\n * manager.\n *\n * ```tsx\n * import React from 'react';\n * import { Remirror, InvalidContentHandler } from 'remirror';\n * import { Remirror, useManager } from '@remirror/react';\n * import { WysiwygPreset } from 'remirror/extensions';\n *\n * const Editor = () => {\n * const onError: InvalidContentHandler = useCallback(({ json, invalidContent, transformers }) => {\n * // Automatically remove all invalid nodes and marks.\n * return transformers.remove(json, invalidContent);\n * }, []);\n *\n * const manager = useManager(() => [new WysiwygPreset()], { onError });\n *\n * return (\n * <Remirror manager={manager}>\n * <div />\n * </Remirror>\n * );\n * };\n * ```\n */\n onError?: InvalidContentHandler;\n\n /**\n * A function which transforms a string into a prosemirror node.\n *\n * @remarks\n *\n * Can be used to transform markdown / html or any other string format into a\n * prosemirror node.\n *\n * See [[`fromHTML`]] for an example of how this could work.\n */\n stringHandler?: keyof Remirror.StringHandlers | StringHandler;\n\n /**\n * The default named selection. This is used when `manager.createState` is\n * called without providing a selection.\n *\n * @defaultValue 'end'\n */\n defaultSelection?: 'start' | 'end' | 'all';\n }\n\n /**\n * Describes the object where the extension manager stores it's data.\n *\n * @remarks\n *\n * Since this is a global namespace, you can extend the store if your\n * extension is modifying the shape of the `Manager.store` property.\n */\n interface ManagerStore<Extension extends AnyExtension> {\n /**\n * The editor view stored by this instance.\n */\n view: EditorView;\n }\n\n interface ExtensionStore {\n /**\n * Make the remirror manager available to the editor.\n */\n manager: AnyRemirrorManager;\n\n /**\n * The list of all extensions included in the editor.\n */\n readonly extensions: AnyExtension[];\n\n /**\n * The stage the manager is currently at.\n */\n readonly phase: ManagerPhase;\n\n /**\n * The view available to extensions once `addView` has been called on the\n * `RemirrorManager` instance.\n */\n readonly view: EditorView;\n\n /**\n * The latest state.\n */\n currentState: EditorState;\n\n /**\n * The previous state. Will be undefined when the view is first created.\n */\n previousState?: EditorState;\n\n /**\n * The root document to be used for the editor. This is mainly used for\n * non-browser environment.\n */\n readonly document: Document;\n\n /**\n * The settings passed to the manager.\n */\n readonly managerSettings: ManagerSettings;\n\n /**\n * The names of every node extension.\n */\n nodeNames: readonly string[];\n\n /**\n * The names of every mark extension.\n */\n markNames: readonly string[];\n\n /**\n * The names of every plain extension.\n */\n plainNames: readonly string[];\n\n /**\n * The named string handlers which are supported by the current editor\n * implementation.\n */\n readonly stringHandlers: NamedStringHandlers;\n\n /**\n * Return true when the editor view has been created.\n */\n readonly isMounted: () => boolean;\n\n /**\n * A helper method for retrieving the state of the editor\n */\n readonly getState: () => EditorState;\n\n /**\n * Allow extensions to trigger an update in the prosemirror state. This\n * should not be used often. It's here in case you need it in an\n * emergency.\n *\n * Internally it's used by the [[`PluginsExtension`]] to create a new\n * state when the plugins are updated at runtime.\n */\n readonly updateState: (state: EditorState) => void;\n\n /**\n * Get the extension instance matching the provided constructor from the\n * manager.\n *\n * This will throw an error if not defined.\n */\n readonly getExtension: <ExtensionConstructor extends AnyExtensionConstructor>(\n Constructor: ExtensionConstructor,\n ) => InstanceType<ExtensionConstructor>;\n\n /**\n * Get the value of a key from the manager store.\n */\n getStoreKey: <Key extends ManagerStoreKeys>(key: Key) => AnyManagerStore[Key];\n\n /**\n * Update the store with a specific key.\n */\n setStoreKey: <Key extends ManagerStoreKeys>(key: Key, value: AnyManagerStore[Key]) => void;\n\n /**\n * Set a value on the extension store. One of the design decisions in this `1.0.0`\n * version of `remirror` was to move away from passing elaborate arguments to each extension\n * method and allow extensions to interact with a store shared by all\n * extensions.\n *\n * The extension store object is immutable and will throw an error if updated directly.\n *\n * ```ts\n * class MyExtension extends PlainExtension {\n * get name() {}\n * }\n * ```\n */\n setExtensionStore: <Key extends keyof ExtensionStore>(\n key: Key,\n value: ExtensionStore[Key],\n ) => void;\n\n /**\n * Set the string handler to use for a given name.\n *\n * This allows users to set the string handler\n */\n setStringHandler: (name: keyof StringHandlers, handler: StringHandler) => void;\n }\n }\n}\n", "import warning from 'tiny-warning';\nimport { ErrorConstant } from '@remirror/core-constants';\nimport { invariant, isEmptyArray, sort } from '@remirror/core-helpers';\nimport type { Dispose, EditorView } from '@remirror/core-types';\n\nimport {\n AnyExtension,\n AnyExtensionConstructor,\n GetExtensions,\n isExtension,\n isMarkExtension,\n isNodeExtension,\n isPlainExtension,\n} from '../extension';\nimport type { GetConstructor, StateUpdateLifecycleProps } from '../types';\n\n/**\n * Transforms the unsorted array of presets and extension into presets and\n * sorted extensions. Handles uniqueness of extensions and automatically throws\n * an error when required extensions are missing.\n *\n * @internalremarks Currently matching by constructor - what if different\n * versions exist in the same app\n *\n * @param initialExtensions - the extensions to be transformed. This includes\n * the extension that are parents to other extensions.\n *\n * @returns the list of extension instances sorted by priority\n */\nexport function transformExtensions<RawExtensions extends AnyExtension>(\n initialExtensions: readonly RawExtensions[],\n settings: Remirror.ManagerSettings,\n): ExtensionTransformation<RawExtensions> {\n type Extension = GetExtensions<RawExtensions>;\n type ExtensionConstructor = GetConstructor<Extension>;\n\n // This is the holder for the sorted and cleaned extensions returned by this\n // function.\n const extensions: Extension[] = [];\n const extensionMap = new WeakMap<ExtensionConstructor, Extension>();\n\n // All the extensions which provide child extensions.\n const parentExtensions: Extension[] = [];\n\n // Used to track duplicates and the extension holders they were added by.\n const duplicateMap = new WeakMap<AnyExtensionConstructor, Extension[]>();\n\n // The unsorted, de-duped, unrefined extensions.\n let gatheredExtensions: Extension[] = [];\n\n // The mutable objects and the manager settings which are used to gather all\n // the deeply nested extensions.\n const gatherRawExtensionConfig = { duplicateMap, parentExtensions, gatheredExtensions, settings };\n\n for (const extension of initialExtensions) {\n gatherRawExtensions(gatherRawExtensionConfig, { extension: extension as Extension });\n }\n\n // Sort the extensions.\n gatheredExtensions = sort(gatheredExtensions, (a, z) => z.priority - a.priority);\n\n // Keep track of added constructors for uniqueness.\n const found = new WeakSet<AnyExtensionConstructor>();\n const names = new Set<string>();\n\n // Remove extension duplicates and update the parent extension with the\n // highest priority identical extension.\n for (const extension of gatheredExtensions) {\n const key = extension.constructor;\n const name = extension.name;\n const duplicates = duplicateMap.get(key);\n\n invariant(duplicates, {\n message: `No entries were found for the ExtensionConstructor ${extension.name}`,\n code: ErrorConstant.INTERNAL,\n });\n\n if (found.has(key) || names.has(name)) {\n continue;\n }\n\n found.add(key);\n names.add(name);\n extensions.push(extension);\n extensionMap.set(key, extension);\n\n // Replace the extensions for all presets that referenced this constructor.\n duplicates.forEach((parent) => parent?.replaceChildExtension(key, extension));\n }\n\n const missing: Array<MissingConstructor<Extension>> = [];\n\n // Throw if any required extensions are missing.\n for (const extension of extensions) {\n findMissingExtensions({ extension, found, missing });\n }\n\n invariant(isEmptyArray(missing), {\n code: ErrorConstant.MISSING_REQUIRED_EXTENSION,\n message: missing\n .map(\n ({ Constructor, extension }) =>\n `The extension '${extension.name}' requires '${Constructor.name} in order to run correctly.`,\n )\n .join('\\n'),\n });\n\n return { extensions, extensionMap };\n}\n\ninterface GatherAllExtensionsConfig<Extension extends AnyExtension> {\n /** The list of gathered raw extensions, updated by mutation. */\n gatheredExtensions: Extension[];\n\n /** The duplicate map which is updated by mutation. */\n duplicateMap: WeakMap<AnyExtensionConstructor, Extension[]>;\n\n /** The parent extensions which are updated by mutation */\n parentExtensions: Extension[];\n\n /** The settings passed into the manager. */\n settings: Remirror.ManagerSettings;\n}\n\ninterface GatherAllExtensionsProps<Extension extends AnyExtension> {\n /** The extension to check and gather children from. */\n extension: Extension;\n\n /** Used to check if there there is a circular dependency encountered. */\n names?: string[];\n\n /** The parent of this extension. */\n parentExtension?: Extension;\n}\n\n/**\n * Dive into the current extension and gather all child extensions including\n * those which are deeply nested.\n *\n * It also automatically handles circular dependencies. And logs a warning when\n * one is encountered.\n *\n * @param config - the configuration and mutable objects which are updated by\n * this function.\n * @param props - the extension, gathered names and parent extension.\n */\nfunction gatherRawExtensions<Extension extends AnyExtension>(\n config: GatherAllExtensionsConfig<Extension>,\n props: GatherAllExtensionsProps<Extension>,\n) {\n const { gatheredExtensions, duplicateMap, parentExtensions, settings } = config;\n const { extension, parentExtension } = props;\n\n // Get the list of parent names of the current extension. This is used to\n // track circular dependencies.\n let { names = [] } = props;\n\n invariant(isExtension(extension), {\n code: ErrorConstant.INVALID_MANAGER_EXTENSION,\n message: `An invalid extension: ${extension} was provided to the [[\\`RemirrorManager\\`]].`,\n });\n\n // The children provided by this extension.\n const childExtensions = extension.extensions;\n\n // Override the priority if the user has done so in the settings passed to the\n // [[`RemirrorManager`]].\n extension.setPriority(settings.priority?.[extension.name]);\n\n // Update the gathered extension list in this block\n gatheredExtensions.push(extension);\n\n // Keep track of the extensions which have been added multiple times by\n // separate extension parents. Later on, the highest priority extension will\n // be added to each parent instead of the one that they may have been\n // configured with.\n updateExtensionDuplicates({ duplicateMap, extension, parentExtension });\n\n // Check if there are any children extensions to be added an if not move onto\n // the next provided extension.\n if (childExtensions.length === 0) {\n return;\n }\n\n if (names.includes(extension.name)) {\n warning(\n false,\n `Circular dependency encountered when loading extensions: ${names.join(' > ')} > ${\n extension.name\n }`,\n );\n return;\n }\n\n names = [...names, extension.name];\n parentExtensions.push(extension);\n\n for (const child of childExtensions) {\n // Recursively gather all the children extension from the current extension\n // level.\n gatherRawExtensions(config, { names, extension: child, parentExtension: extension });\n }\n}\n\ninterface FindMissingProps<Extension extends AnyExtension> {\n extension: Extension;\n found: WeakSet<AnyExtensionConstructor>;\n missing: Array<MissingConstructor<Extension>>;\n}\n\n/**\n * Populate missing Constructors.\n *\n * If any missing extensions are identified then it is the responsibility of the\n * calling method to deal with the error. Currently the action is to `throw` an\n * error.\n */\nfunction findMissingExtensions<Extension extends AnyExtension>(props: FindMissingProps<Extension>) {\n const { extension, found, missing } = props;\n\n if (!extension.requiredExtensions) {\n return;\n }\n\n for (const Constructor of extension.requiredExtensions ?? []) {\n if (found.has(Constructor)) {\n continue;\n }\n\n missing.push({ Constructor: Constructor, extension });\n }\n}\n\ninterface UpdateExtensionDuplicatesProps<Extension extends AnyExtension> {\n /**\n * The map of all duplicates.\n */\n duplicateMap: WeakMap<AnyExtensionConstructor, Extension[]>;\n\n /**\n * The extension to associate to the multiple presets that have added it..\n */\n extension: Extension;\n\n /**\n * The preset which was responsible for adding the extension (if it exists).\n */\n parentExtension?: Extension;\n}\n\n/**\n * Adds the values to the duplicate map which identifies each unique extension\n * in the manager and tracks the presets responsible for adding them. This is\n * used to make sure that only one instance of each extension is shared amongst\n * the presets which require it.\n *\n * At the moment, the highest priority extension is the one that is to all\n * presets which require it. This is done by checking the `duplicateMap` for\n * each extension, and replacing the instance of the required extension within\n * the preset with the highest priority instance.\n */\nfunction updateExtensionDuplicates<Extension extends AnyExtension>(\n props: UpdateExtensionDuplicatesProps<Extension>,\n) {\n const { duplicateMap, extension, parentExtension } = props;\n\n // The extension constructor is used as the identifier for lookups.\n const key = extension.constructor;\n\n const duplicate = duplicateMap.get(key);\n const parentToAdd: Extension[] = parentExtension ? [parentExtension] : [];\n\n duplicateMap.set(key, duplicate ? [...duplicate, ...parentToAdd] : parentToAdd);\n}\n\n/**\n * This is the object shape that is returned from the combined transformation.\n */\nexport interface ExtensionTransformation<\n Extension extends AnyExtension,\n Expanded extends AnyExtension = GetExtensions<Extension>,\n> {\n /**\n * The list of extensions sorted by priority and original extension. Every\n * extension passed in and those contained by presets are placed here.\n */\n extensions: Expanded[];\n\n /**\n * A map where the key is the [[`ExtensionConstructor`]] and the value is the\n * [[`Extension`]] instance. This is used to lookup extensions contained\n * within a manager. It is a weak map so that values can be garbage collected\n * when references to the constructor are lost.\n */\n extensionMap: WeakMap<GetConstructor<Expanded>, Expanded>;\n}\n\ninterface MissingConstructor<Extension extends AnyExtension> {\n Constructor: AnyExtensionConstructor;\n extension: Extension;\n}\n\nexport interface ManagerLifecycleHandlers {\n /**\n * Contains the methods run when the manager is first created.\n */\n create: Array<() => Dispose | void>;\n\n /**\n * Holds the methods to run once the Editor has received the view from the\n * attached.\n */\n view: Array<(view: EditorView) => Dispose | void>;\n\n /**\n * The update method is called every time the state updates. This allows\n * extensions to listen to updates.\n */\n update: Array<(props: StateUpdateLifecycleProps) => void>;\n\n /**\n * Called when the manager is being destroyed.\n */\n destroy: Array<() => void>;\n}\n\ninterface SetupExtensionProps {\n extension: AnyExtension;\n nodeNames: string[];\n markNames: string[];\n plainNames: string[];\n store: Remirror.ExtensionStore;\n handlers: ManagerLifecycleHandlers;\n}\n\n/**\n * This helper function extracts all the lifecycle methods from the provided\n * extension and adds them to the provided `handler` container.\n */\nexport function extractLifecycleMethods(props: SetupExtensionProps): void {\n const { extension, nodeNames, markNames, plainNames, store, handlers } = props;\n\n // Add the store to the extension. The store is used by extensions to access\n // all the data included in `Remirror.ExtensionStore`. I decided on this\n // pattern because passing around parameters into each call method was\n // tedious. Why not just access `this.store` within your extension to get\n // whatever you need? Also using the store allows developers to extend the\n // behaviour of their editor by adding different behaviour to the global\n // namespace [[`Remirror.ExtensionStore`]].\n extension.setStore(store);\n\n // Gather all the handlers and add them where they exist.\n\n const createHandler = extension.onCreate?.bind(extension);\n const viewHandler = extension.onView?.bind(extension);\n const stateUpdateHandler = extension.onStateUpdate?.bind(extension);\n const destroyHandler = extension.onDestroy?.bind(extension);\n\n if (createHandler) {\n handlers.create.push(createHandler);\n }\n\n if (viewHandler) {\n handlers.view.push(viewHandler);\n }\n\n if (stateUpdateHandler) {\n handlers.update.push(stateUpdateHandler);\n }\n\n if (destroyHandler) {\n handlers.destroy.push(destroyHandler);\n }\n\n // Keep track of the names of the different types of extension held by this\n // manager. This is already in use by the [[`TagsExtension`]].\n\n if (isMarkExtension(extension)) {\n markNames.push(extension.name);\n }\n\n // Don't include the `doc` as a node since it is a requirement for all editors\n // and doesn't behave in the same way as other nodes.\n if (isNodeExtension(extension) && extension.name !== 'doc') {\n nodeNames.push(extension.name);\n }\n\n if (isPlainExtension(extension)) {\n plainNames.push(extension.name);\n }\n}\n", "export * from './builtins';\nexport type { DelayedPromiseCreator, DelayedValue } from './commands';\nexport { DelayedCommand, delayedCommand, insertText, isDelayedValue, toggleMark } from './commands';\nexport * from './extension';\nexport type {\n AddCustomHandler,\n AddHandler,\n CustomHandlerMethod,\n HandlerKeyOptions,\n} from './extension/base-class';\nexport type {\n AttributePropFunction,\n BaseFramework,\n CreateStateFromContent,\n FrameworkOptions,\n FrameworkOutput,\n FrameworkProps,\n ListenerProps,\n PlaceholderConfig,\n RemirrorEventListener,\n RemirrorEventListenerProps,\n TriggerChangeProps,\n UpdateStateProps,\n} from './framework';\nexport { Framework } from './framework';\nexport type { AnyRemirrorManager, CreateEditorStateProps, ManagerEvents } from './manager';\nexport { isRemirrorManager, RemirrorManager } from './manager';\nexport type {\n AppendLifecycleProps,\n ApplyStateLifecycleProps,\n BaseExtensionOptions,\n ChangedOptions,\n CommandShape,\n CreateExtensionPlugin,\n DynamicOptionsOfConstructor,\n ExcludeOptions,\n ExtensionCommandFunction,\n ExtensionCommandReturn,\n ExtensionHelperReturn,\n ExtensionStore,\n FocusType,\n GetChangeOptionsReturn,\n GetCommands,\n GetConstructor,\n GetHelpers,\n GetOptions,\n OnSetOptionsProps,\n OptionsOfConstructor,\n PickChanged,\n StateUpdateLifecycleProps,\n TypedPropertyDescriptor,\n UpdateReason,\n UpdateReasonProps,\n} from './types';\nexport * from '@remirror/core-constants';\nexport * from '@remirror/core-helpers';\nexport * from '@remirror/core-types';\nexport * from '@remirror/core-utils';\nexport type { CoreIcon } from '@remirror/icons';\n"],
6 "names": []