From b0695421645ac6deed70d1b847b26bfb2c28d03b Mon Sep 17 00:00:00 2001 From: Palanikannan M Date: Wed, 13 Nov 2024 09:29:09 +0530 Subject: [PATCH] feat: trying out heading lists, nested styles and diving deeper --- packages/editor/package.json | 1 + .../src/core/extensions/core-without-props.ts | 2 +- .../editor/src/core/extensions/extensions.tsx | 4 +- .../flat-list/commands/dedent-list.spec.ts | 575 ++++++++ .../flat-list/commands/dedent-list.ts | 223 +++ .../flat-list/commands/enter-without-lift.ts | 19 + .../flat-list/commands/indent-list.spec.ts | 787 +++++++++++ .../flat-list/commands/indent-list.ts | 153 ++ .../commands/join-collapsed-backward.ts | 56 + .../flat-list/commands/join-list-up.ts | 76 + .../commands/join-textblocks-around.ts | 36 + .../flat-list/commands/keymap.spec.ts | 208 +++ .../extensions/flat-list/commands/keymap.ts | 91 ++ .../flat-list/commands/move-list.spec.ts | 84 ++ .../flat-list/commands/move-list.ts | 86 ++ .../commands/protect-collapsed.spec.ts | 41 + .../flat-list/commands/protect-collapsed.ts | 43 + .../commands/set-safe-selection.spec.ts | 154 ++ .../flat-list/commands/set-safe-selection.ts | 70 + .../flat-list/commands/split-list.spec.ts | 516 +++++++ .../flat-list/commands/split-list.ts | 234 +++ .../commands/toggle-collapsed.spec.ts | 63 + .../flat-list/commands/toggle-collapsed.ts | 60 + .../flat-list/commands/toggle-list.spec.ts | 46 + .../flat-list/commands/toggle-list.ts | 26 + .../flat-list/commands/unwrap-list.spec.ts | 77 + .../flat-list/commands/unwrap-list.ts | 86 ++ .../flat-list/commands/wrap-in-list.spec.ts | 163 +++ .../flat-list/commands/wrap-in-list.ts | 89 ++ .../extensions/flat-list/flat-heading-list.ts | 156 ++ .../extensions/{ => flat-list}/flat-list.ts | 16 + .../list-serializer.spec.ts.snap | 175 +++ .../flat-list/utils/at-textblock-end.ts | 19 + .../flat-list/utils/at-textblock-start.ts | 17 + .../flat-list/utils/auto-fix-list.spec.ts | 50 + .../flat-list/utils/auto-fix-list.ts | 104 ++ .../flat-list/utils/block-boundary.spec.ts | 51 + .../flat-list/utils/block-boundary.ts | 32 + .../extensions/flat-list/utils/browser.ts | 12 + .../flat-list/utils/create-and-fill.ts | 15 + .../flat-list/utils/cut-by-index.ts | 10 + .../flat-list/utils/get-list-type.ts | 24 + .../flat-list/utils/in-collapsed-list.spec.ts | 38 + .../flat-list/utils/in-collapsed-list.ts | 17 + .../utils/is-block-node-selection.ts | 9 + .../flat-list/utils/is-collapsed-list-node.ts | 9 + .../flat-list/utils/is-list-node.ts | 9 + .../flat-list/utils/is-list-type.ts | 8 + .../flat-list/utils/is-node-selection.ts | 7 + .../flat-list/utils/is-text-selection.ts | 5 + .../extensions/flat-list/utils/list-range.ts | 47 + .../flat-list/utils/list-serializer.spec.ts | 90 ++ .../flat-list/utils/list-serializer.ts | 68 + .../extensions/flat-list/utils/map-pos.ts | 16 + .../extensions/flat-list/utils/max-open.ts | 33 + .../flat-list/utils/parse-integer.spec.ts | 44 + .../flat-list/utils/parse-integer.ts | 7 + .../flat-list/utils/patch-command.ts | 15 + .../flat-list/utils/range-to-string.ts | 13 + .../extensions/flat-list/utils/safe-lift.ts | 24 + .../flat-list/utils/set-list-attributes.ts | 21 + .../flat-list/utils/set-node-attributes.ts | 18 + .../flat-list/utils/split-boundary.spec.ts | 370 +++++ .../flat-list/utils/split-boundary.ts | 34 + .../flat-list/utils/unwrap-list-slice.ts | 26 + .../flat-list/utils/zoom-in-range.ts | 19 + packages/editor/src/core/types/editor.ts | 6 +- packages/editor/src/index.ts | 2 - packages/editor/src/styles/editor.css | 136 +- packages/editor/src/styles/tailwind.css | 6 +- yarn.lock | 1259 +++++++++++++++-- 71 files changed, 7002 insertions(+), 104 deletions(-) create mode 100644 packages/editor/src/core/extensions/flat-list/commands/dedent-list.spec.ts create mode 100644 packages/editor/src/core/extensions/flat-list/commands/dedent-list.ts create mode 100644 packages/editor/src/core/extensions/flat-list/commands/enter-without-lift.ts create mode 100644 packages/editor/src/core/extensions/flat-list/commands/indent-list.spec.ts create mode 100644 packages/editor/src/core/extensions/flat-list/commands/indent-list.ts create mode 100644 packages/editor/src/core/extensions/flat-list/commands/join-collapsed-backward.ts create mode 100644 packages/editor/src/core/extensions/flat-list/commands/join-list-up.ts create mode 100644 packages/editor/src/core/extensions/flat-list/commands/join-textblocks-around.ts create mode 100644 packages/editor/src/core/extensions/flat-list/commands/keymap.spec.ts create mode 100644 packages/editor/src/core/extensions/flat-list/commands/keymap.ts create mode 100644 packages/editor/src/core/extensions/flat-list/commands/move-list.spec.ts create mode 100644 packages/editor/src/core/extensions/flat-list/commands/move-list.ts create mode 100644 packages/editor/src/core/extensions/flat-list/commands/protect-collapsed.spec.ts create mode 100644 packages/editor/src/core/extensions/flat-list/commands/protect-collapsed.ts create mode 100644 packages/editor/src/core/extensions/flat-list/commands/set-safe-selection.spec.ts create mode 100644 packages/editor/src/core/extensions/flat-list/commands/set-safe-selection.ts create mode 100644 packages/editor/src/core/extensions/flat-list/commands/split-list.spec.ts create mode 100644 packages/editor/src/core/extensions/flat-list/commands/split-list.ts create mode 100644 packages/editor/src/core/extensions/flat-list/commands/toggle-collapsed.spec.ts create mode 100644 packages/editor/src/core/extensions/flat-list/commands/toggle-collapsed.ts create mode 100644 packages/editor/src/core/extensions/flat-list/commands/toggle-list.spec.ts create mode 100644 packages/editor/src/core/extensions/flat-list/commands/toggle-list.ts create mode 100644 packages/editor/src/core/extensions/flat-list/commands/unwrap-list.spec.ts create mode 100644 packages/editor/src/core/extensions/flat-list/commands/unwrap-list.ts create mode 100644 packages/editor/src/core/extensions/flat-list/commands/wrap-in-list.spec.ts create mode 100644 packages/editor/src/core/extensions/flat-list/commands/wrap-in-list.ts create mode 100644 packages/editor/src/core/extensions/flat-list/flat-heading-list.ts rename packages/editor/src/core/extensions/{ => flat-list}/flat-list.ts (84%) create mode 100644 packages/editor/src/core/extensions/flat-list/utils/__snapshots__/list-serializer.spec.ts.snap create mode 100644 packages/editor/src/core/extensions/flat-list/utils/at-textblock-end.ts create mode 100644 packages/editor/src/core/extensions/flat-list/utils/at-textblock-start.ts create mode 100644 packages/editor/src/core/extensions/flat-list/utils/auto-fix-list.spec.ts create mode 100644 packages/editor/src/core/extensions/flat-list/utils/auto-fix-list.ts create mode 100644 packages/editor/src/core/extensions/flat-list/utils/block-boundary.spec.ts create mode 100644 packages/editor/src/core/extensions/flat-list/utils/block-boundary.ts create mode 100644 packages/editor/src/core/extensions/flat-list/utils/browser.ts create mode 100644 packages/editor/src/core/extensions/flat-list/utils/create-and-fill.ts create mode 100644 packages/editor/src/core/extensions/flat-list/utils/cut-by-index.ts create mode 100644 packages/editor/src/core/extensions/flat-list/utils/get-list-type.ts create mode 100644 packages/editor/src/core/extensions/flat-list/utils/in-collapsed-list.spec.ts create mode 100644 packages/editor/src/core/extensions/flat-list/utils/in-collapsed-list.ts create mode 100644 packages/editor/src/core/extensions/flat-list/utils/is-block-node-selection.ts create mode 100644 packages/editor/src/core/extensions/flat-list/utils/is-collapsed-list-node.ts create mode 100644 packages/editor/src/core/extensions/flat-list/utils/is-list-node.ts create mode 100644 packages/editor/src/core/extensions/flat-list/utils/is-list-type.ts create mode 100644 packages/editor/src/core/extensions/flat-list/utils/is-node-selection.ts create mode 100644 packages/editor/src/core/extensions/flat-list/utils/is-text-selection.ts create mode 100644 packages/editor/src/core/extensions/flat-list/utils/list-range.ts create mode 100644 packages/editor/src/core/extensions/flat-list/utils/list-serializer.spec.ts create mode 100644 packages/editor/src/core/extensions/flat-list/utils/list-serializer.ts create mode 100644 packages/editor/src/core/extensions/flat-list/utils/map-pos.ts create mode 100644 packages/editor/src/core/extensions/flat-list/utils/max-open.ts create mode 100644 packages/editor/src/core/extensions/flat-list/utils/parse-integer.spec.ts create mode 100644 packages/editor/src/core/extensions/flat-list/utils/parse-integer.ts create mode 100644 packages/editor/src/core/extensions/flat-list/utils/patch-command.ts create mode 100644 packages/editor/src/core/extensions/flat-list/utils/range-to-string.ts create mode 100644 packages/editor/src/core/extensions/flat-list/utils/safe-lift.ts create mode 100644 packages/editor/src/core/extensions/flat-list/utils/set-list-attributes.ts create mode 100644 packages/editor/src/core/extensions/flat-list/utils/set-node-attributes.ts create mode 100644 packages/editor/src/core/extensions/flat-list/utils/split-boundary.spec.ts create mode 100644 packages/editor/src/core/extensions/flat-list/utils/split-boundary.ts create mode 100644 packages/editor/src/core/extensions/flat-list/utils/unwrap-list-slice.ts create mode 100644 packages/editor/src/core/extensions/flat-list/utils/zoom-in-range.ts diff --git a/packages/editor/package.json b/packages/editor/package.json index 999e7fdc311..a174f0b2f2d 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -50,6 +50,7 @@ "@tiptap/extension-task-list": "^2.9.1", "@tiptap/extension-text-style": "^2.9.1", "@tiptap/extension-underline": "^2.9.1", + "@tiptap/extension-text-align": "^2.9.1", "@tiptap/pm": "^2.9.1", "@tiptap/react": "^2.9.1", "@tiptap/starter-kit": "^2.9.1", diff --git a/packages/editor/src/core/extensions/core-without-props.ts b/packages/editor/src/core/extensions/core-without-props.ts index bc124ee8676..b90037faa2d 100644 --- a/packages/editor/src/core/extensions/core-without-props.ts +++ b/packages/editor/src/core/extensions/core-without-props.ts @@ -19,7 +19,7 @@ import { TableHeader, TableCell, TableRow, Table } from "./table"; import { CustomTextAlignExtension } from "./text-align"; import { CustomCalloutExtensionConfig } from "./callout/extension-config"; import { CustomColorExtension } from "./custom-color"; -import { FlatListExtension } from "./flat-list"; +import { FlatListExtension } from "./flat-list/flat-list"; export const CoreEditorExtensionsWithoutProps = [ StarterKit.configure({ diff --git a/packages/editor/src/core/extensions/extensions.tsx b/packages/editor/src/core/extensions/extensions.tsx index fb18a64ac1c..1d0b7b50882 100644 --- a/packages/editor/src/core/extensions/extensions.tsx +++ b/packages/editor/src/core/extensions/extensions.tsx @@ -33,7 +33,8 @@ import { import { isValidHttpUrl } from "@/helpers/common"; // types import { IMentionHighlight, IMentionSuggestion, TFileHandler } from "@/types"; -import { FlatListExtension } from "./flat-list"; +import { FlatListExtension } from "./flat-list/flat-list"; +import { FlatHeadingListExtension } from "./flat-list/flat-heading-list"; type TArguments = { enableHistory: boolean; @@ -167,5 +168,6 @@ export const CoreEditorExtensions = (args: TArguments) => { CustomCalloutExtension, CustomColorExtension, FlatListExtension, + // FlatHeadingListExtension, ]; }; diff --git a/packages/editor/src/core/extensions/flat-list/commands/dedent-list.spec.ts b/packages/editor/src/core/extensions/flat-list/commands/dedent-list.spec.ts new file mode 100644 index 00000000000..401c6806b5a --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/commands/dedent-list.spec.ts @@ -0,0 +1,575 @@ +import { type TaggedProsemirrorNode } from 'jest-remirror' +import { type Node as ProsemirrorNode } from 'prosemirror-model' +import { describe, expect, it } from 'vitest' + +import { setupTestingEditor } from '../../test/setup-editor' + +import { createDedentListCommand } from './dedent-list' + +describe('dedentList', () => { + const t = setupTestingEditor() + const markdown = t.markdown + + it('can dedent a list node to outer list', () => { + t.applyCommand( + createDedentListCommand(), + markdown` + - A1 + - B1 + `, + markdown` + - A1 + - B1 + `, + ) + + t.applyCommand( + createDedentListCommand(), + markdown` + - - B1 + - A1 + `, + markdown` + - B1 + - A1 + `, + ) + }) + + it('can dedent a paragraph node to outer list', () => { + t.applyCommand( + createDedentListCommand(), + markdown` + - A1 + + - B1a + + B1b + `, + markdown` + - A1 + + - B1a + + B1b + `, + ) + }) + + it('can unwrap a list node', () => { + t.applyCommand( + createDedentListCommand(), + markdown` + - A1 + + paragraph + `, + markdown` + A1 + + paragraph + `, + ) + + t.applyCommand( + createDedentListCommand(), + markdown` + - A1 + - A2 + `, + markdown` + A1 + + - A2 + `, + ) + + t.applyCommand( + createDedentListCommand(), + markdown` + - A1 + - A2 + `, + markdown` + - A1 + + A2 + `, + ) + }) + + it('can unwrap multiple list nodes', () => { + t.applyCommand( + createDedentListCommand(), + markdown` + - A1 + - A2 + `, + markdown` + A1 + + A2 + `, + ) + + t.applyCommand( + createDedentListCommand(), + markdown` + - A1 + - A2 + - A3 + - A4 + `, + markdown` + - A1 + + A2 + + A3 + + - A4 + `, + ) + }) + + it('can keep siblings after the lifted items at the same position', () => { + t.applyCommand( + createDedentListCommand(), + markdown` + - A1 + + - B1 + + - B2 + + - B3 + + - C1 + + B3 + + - B4 + `, + markdown` + - A1 + + - B1 + + - B2 + + - B3 + + - C1 + + B3 + + - B4 + `, + ) + + t.applyCommand( + createDedentListCommand(), + markdown` + - A1 + + - B1 + + - B2 + + A1 + `, + markdown` + - A1 + + - B1 + + - B2 + + A1 + `, + ) + }) + + it('can only dedent selected part when the selection across multiple depth of a nested lists', () => { + t.applyCommand( + createDedentListCommand(), + markdown` + - A1 + + - B1 + + - B2 + + - C1 + + - B3 + `, + markdown` + - A1 + + - B1 + + - B2 + + - C1 + + - B3 + `, + ) + + t.applyCommand( + createDedentListCommand(), + markdown` + - A1 + + - B1 + + - B2 + + - C1 + + - B3 + + - C2 + `, + markdown` + - A1 + + - B1 + + - B2 + + - C1 + + - B3 + + - - C2 + `, + ) + }) + + it('can wrap unselected paragraphs with a list node if necessary', () => { + t.applyCommand( + createDedentListCommand(), + markdown` + - A1 + + - B1 + + - B2 + + - B3 + + B3 + + B3 + + - B4 + `, + markdown` + - A1 + + - B1 + + - B2 + + - B3 + + - B3 + + B3 + + - B4 + `, + ) + }) + + it('can keep the indentation of sub list', () => { + t.applyCommand( + createDedentListCommand(), + markdown` + - A1 + + - B1 + + - C1 + `, + markdown` + - A1 + + - B1 + + - - C1 + `, + ) + + t.applyCommand( + createDedentListCommand(), + markdown` + - A1 + + - B1 + `, + markdown` + A1 + + - - B1 + `, + ) + }) + + it('do nothing when not inside a list', () => { + t.applyCommand( + createDedentListCommand(), + markdown` + Hello + + paragraph + `, + markdown` + Hello + + paragraph + `, + ) + }) + + it('can dedent a nested list item', () => { + t.applyCommand( + createDedentListCommand(), + markdown` + - - B1 + + B1 + + A1 + `, + markdown` + - B1 + + - B1 + + A1 + `, + ) + }) + + it('can dedent a blockquote inside a list', () => { + t.applyCommand( + createDedentListCommand(), + markdown` + - > A1 + > + > A2 + `, + markdown` + - > A1 + + A2 + `, + ) + }) + + it('can accept custom positions', () => { + t.applyCommand( + createDedentListCommand({ from: 13, to: 17 }), + t.doc( + /*0*/ + t.bulletList(/*1*/ t.p('A1') /*5*/), + /*6*/ + t.bulletList(/*7*/ t.p('A2') /*11*/), + /*12*/ + t.bulletList(/*13*/ t.p('A3') /*17*/), + /*18*/ + ), + t.doc( + // + t.bulletList(t.p('A1')), + t.bulletList(t.p('A2')), + t.p('A3'), + ), + ) + + t.applyCommand( + createDedentListCommand({ from: 10, to: 14 }), + t.doc( + /*0*/ + t.bulletList(/*1*/ t.p('A1') /*5*/), + /*6*/ + t.bulletList(/*7*/ t.p('A2') /*11*/), + /*12*/ + t.bulletList(/*13*/ t.p('A3') /*17*/), + /*18*/ + ), + t.doc( + // + t.bulletList(t.p('A1')), + t.p('A2'), + t.p('A3'), + ), + ) + }) + + it('can handle some complex nested lists', () => { + t.applyCommand( + createDedentListCommand(), + markdown` + - A1 + - B1 + - B2 + - A2 + - B3 + - C1 + - D1 + - B4 + `, + markdown` + - A1 + - B1 + - B2 + + A2 + + - B3 + - C1 + - - D1 + - B4 + `, + ) + + t.applyCommand( + createDedentListCommand(), + markdown` + - A1 + + - B1 + + - B2 + + - C1 + + - D1 + + D1 + + - A2 + + - B3 + + - C2 + + C2 + + - D2 + + C2 + + - C3 + `, + markdown` + - A1 + + - B1 + + - B2 + + - C1 + + - D1 + + D1 + + A2 + + - B3 + + - C2 + + C2 + + - D2 + + C2 + + - C3 + `, + ) + }) + + it('only needs one step for some of the most comment indent action', () => { + const countSteps = ( + doc: TaggedProsemirrorNode, + expected: TaggedProsemirrorNode, + ) => { + t.add(doc) + const state = t.view.state + const command = createDedentListCommand() + let count = -1 + let actual: ProsemirrorNode | null = null + command(state, (tr) => { + count = tr.steps.length + actual = tr.doc + }) + expect(actual).not.equal(null) + expect(actual).toEqualRemirrorDocument(expected) + return count + } + + expect( + countSteps( + markdown` + - A1 + - B1 + `, + markdown` + - A1 + - B1 + `, + ), + ).toBe(1) + + expect( + countSteps( + markdown` + - A1 + - A2 + `, + markdown` + - A1 + + A2 + `, + ), + ).toBe(1) + + expect( + countSteps( + markdown` + # heading + + - A1 + `, + markdown` + # heading + + A1 + `, + ), + ).toBe(1) + + expect( + countSteps( + markdown` + # heading + + - - A1 + `, + markdown` + # heading + + - A1 + `, + ), + ).toBe(1) + }) +}) diff --git a/packages/editor/src/core/extensions/flat-list/commands/dedent-list.ts b/packages/editor/src/core/extensions/flat-list/commands/dedent-list.ts new file mode 100644 index 00000000000..905636ef4e4 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/commands/dedent-list.ts @@ -0,0 +1,223 @@ +import { Fragment, NodeRange, Slice } from "prosemirror-model"; +import { type Command, type Transaction } from "prosemirror-state"; +import { ReplaceAroundStep } from "prosemirror-transform"; + +import { withVisibleSelection } from "./set-safe-selection"; +import { findListsRange, isListNode, isListsRange, getListType } from "prosemirror-flat-list"; +import { atStartBlockBoundary, atEndBlockBoundary } from "../utils/block-boundary"; +import { mapPos } from "../utils/map-pos"; +import { safeLift } from "../utils/safe-lift"; +import { zoomInRange } from "../utils/zoom-in-range"; + +/** + * @public + */ +export interface DedentListOptions { + /** + * A optional from position to indent. + * + * @defaultValue `state.selection.from` + */ + from?: number; + + /** + * A optional to position to indent. + * + * @defaultValue `state.selection.to` + */ + to?: number; +} + +/** + * Returns a command function that decreases the indentation of selected list nodes. + * + * @public @group Commands + */ +export function createDedentListCommand(options?: DedentListOptions): Command { + const dedentListCommand: Command = (state, dispatch): boolean => { + const tr = state.tr; + + const $from = options?.from == null ? tr.selection.$from : tr.doc.resolve(options.from); + const $to = options?.to == null ? tr.selection.$to : tr.doc.resolve(options.to); + + const range = findListsRange($from, $to); + if (!range) return false; + + if (dedentRange(range, tr)) { + dispatch?.(tr); + return true; + } + return false; + }; + + return withVisibleSelection(dedentListCommand); +} + +function dedentRange(range: NodeRange, tr: Transaction, startBoundary?: boolean, endBoundary?: boolean): boolean { + const { depth, $from, $to } = range; + + startBoundary = startBoundary || atStartBlockBoundary($from, depth + 1); + + if (!startBoundary) { + const { startIndex, endIndex } = range; + if (endIndex - startIndex === 1) { + const contentRange = zoomInRange(range); + return contentRange ? dedentRange(contentRange, tr) : false; + } else { + return splitAndDedentRange(range, tr, startIndex + 1); + } + } + + endBoundary = endBoundary || atEndBlockBoundary($to, depth + 1); + + if (!endBoundary) { + fixEndBoundary(range, tr); + const endOfParent = $to.end(depth); + range = new NodeRange(tr.doc.resolve($from.pos), tr.doc.resolve(endOfParent), depth); + return dedentRange(range, tr, undefined, true); + } + + if (range.startIndex === 0 && range.endIndex === range.parent.childCount && isListNode(range.parent)) { + return dedentNodeRange(new NodeRange($from, $to, depth - 1), tr); + } + + return dedentNodeRange(range, tr); +} + +/** + * Split a range into two parts, and dedent them separately. + */ +function splitAndDedentRange(range: NodeRange, tr: Transaction, splitIndex: number): boolean { + const { $from, $to, depth } = range; + + const splitPos = $from.posAtIndex(splitIndex, depth); + + const range1 = $from.blockRange(tr.doc.resolve(splitPos - 1)); + if (!range1) return false; + + const getRange2From = mapPos(tr, splitPos + 1); + const getRange2To = mapPos(tr, $to.pos); + + dedentRange(range1, tr, undefined, true); + + let range2 = tr.doc.resolve(getRange2From()).blockRange(tr.doc.resolve(getRange2To())); + + if (range2 && range2.depth >= depth) { + range2 = new NodeRange(range2.$from, range2.$to, depth); + dedentRange(range2, tr, true, undefined); + } + return true; +} + +export function dedentNodeRange(range: NodeRange, tr: Transaction) { + if (isListNode(range.parent)) { + return safeLiftRange(tr, range); + } else if (isListsRange(range)) { + return dedentOutOfList(tr, range); + } else { + return safeLiftRange(tr, range); + } +} + +function safeLiftRange(tr: Transaction, range: NodeRange): boolean { + if (moveRangeSiblings(tr, range)) { + const $from = tr.doc.resolve(range.$from.pos); + const $to = tr.doc.resolve(range.$to.pos); + range = new NodeRange($from, $to, range.depth); + } + return safeLift(tr, range); +} + +function moveRangeSiblings(tr: Transaction, range: NodeRange): boolean { + const listType = getListType(tr.doc.type.schema); + const { $to, depth, end, parent, endIndex } = range; + const endOfParent = $to.end(depth); + + if (end < endOfParent) { + // There are siblings after the lifted items, which must become + // children of the last item + const lastChild = parent.maybeChild(endIndex - 1); + if (!lastChild) return false; + + const canAppend = + endIndex < parent.childCount && + lastChild.canReplace(lastChild.childCount, lastChild.childCount, parent.content, endIndex, parent.childCount); + + if (canAppend) { + tr.step( + new ReplaceAroundStep( + end - 1, + endOfParent, + end, + endOfParent, + new Slice(Fragment.from(listType.create(null)), 1, 0), + 0, + true + ) + ); + return true; + } else { + tr.step( + new ReplaceAroundStep( + end, + endOfParent, + end, + endOfParent, + new Slice(Fragment.from(listType.create(null)), 0, 0), + 1, + true + ) + ); + return true; + } + } + return false; +} + +function fixEndBoundary(range: NodeRange, tr: Transaction): void { + if (range.endIndex - range.startIndex >= 2) { + range = new NodeRange( + range.$to.doc.resolve(range.$to.posAtIndex(range.endIndex - 1, range.depth)), + range.$to, + range.depth + ); + } + + const contentRange = zoomInRange(range); + if (contentRange) { + fixEndBoundary(contentRange, tr); + range = new NodeRange(tr.doc.resolve(range.$from.pos), tr.doc.resolve(range.$to.pos), range.depth); + } + + moveRangeSiblings(tr, range); +} + +export function dedentOutOfList(tr: Transaction, range: NodeRange): boolean { + const { startIndex, endIndex, parent } = range; + + const getRangeStart = mapPos(tr, range.start); + const getRangeEnd = mapPos(tr, range.end); + + // Merge the list nodes into a single big list node + for (let end = getRangeEnd(), i = endIndex - 1; i > startIndex; i--) { + end -= parent.child(i).nodeSize; + tr.delete(end - 1, end + 1); + } + + const $start = tr.doc.resolve(getRangeStart()); + const listNode = $start.nodeAfter; + + if (!listNode) return false; + + const start = range.start; + const end = start + listNode.nodeSize; + + if (getRangeEnd() !== end) return false; + + if (!$start.parent.canReplace(startIndex, startIndex + 1, Fragment.from(listNode))) { + return false; + } + + tr.step(new ReplaceAroundStep(start, end, start + 1, end - 1, new Slice(Fragment.empty, 0, 0), 0, true)); + return true; +} diff --git a/packages/editor/src/core/extensions/flat-list/commands/enter-without-lift.ts b/packages/editor/src/core/extensions/flat-list/commands/enter-without-lift.ts new file mode 100644 index 00000000000..d6d1639b0cc --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/commands/enter-without-lift.ts @@ -0,0 +1,19 @@ +import { + chainCommands, + createParagraphNear, + newlineInCode, + splitBlock, +} from 'prosemirror-commands' +import { type Command } from 'prosemirror-state' + +/** + * This command has the same behavior as the `Enter` keybinding from + * `prosemirror-commands`, but without the `liftEmptyBlock` command. + * + * @internal + */ +export const enterWithoutLift: Command = chainCommands( + newlineInCode, + createParagraphNear, + splitBlock, +) diff --git a/packages/editor/src/core/extensions/flat-list/commands/indent-list.spec.ts b/packages/editor/src/core/extensions/flat-list/commands/indent-list.spec.ts new file mode 100644 index 00000000000..3561d469030 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/commands/indent-list.spec.ts @@ -0,0 +1,787 @@ +import { type TaggedProsemirrorNode } from 'jest-remirror' +import { type Node as ProsemirrorNode } from 'prosemirror-model' +import { describe, expect, it } from 'vitest' + +import { setupTestingEditor } from '../../test/setup-editor' + +import { createIndentListCommand } from './indent-list' + +describe('indentList', () => { + const t = setupTestingEditor() + const markdown = t.markdown + + const indentList = createIndentListCommand() + + it('can indent a list node and append it to the previous list node', () => { + t.applyCommand( + indentList, + markdown` + - A1 + - A2 + `, + markdown` + - A1 + - A2 + `, + ) + + t.applyCommand( + indentList, + markdown` + - A1 + - ## A2 + `, + markdown` + - A1 + - ## A2 + `, + ) + + t.applyCommand( + indentList, + markdown` + - A1 + - > ## A2 + `, + markdown` + - A1 + - > ## A2 + `, + ) + }) + + it('can indent multiple list nodes and append them to the previous list node', () => { + t.applyCommand( + indentList, + markdown` + - A1 + - A2 + - A3 + `, + markdown` + - A1 + - A2 + - A3 + `, + ) + }) + + it('should not wrap a paragraph with a new list node when it will bring a new visual bullet', () => { + t.applyCommand( + indentList, + markdown` + - A1 + - A2a + + A2b + `, + markdown` + - A1 + + - A2a + + A2b + `, + ) + + t.applyCommand( + indentList, + markdown` + - A1 + - A2a + + A2b + + A2c + `, + markdown` + - A1 + + - A2a + + A2b + + A2c + `, + ) + }) + + it('can indent a paragraph and append it to the previous sibling list node', () => { + t.applyCommand( + indentList, + markdown` + - A1 + - A2a + + - B1 + + A2b + `, + markdown` + - A1 + + - A2a + + - B1 + + A2b + `, + ) + + t.applyCommand( + indentList, + markdown` + - A1 + - A2a + + - B1 + + A2b + + - B2 + `, + markdown` + - A1 + + - A2a + + - B1 + + A2b + + - B2 + `, + ) + }) + + it('can only indent selected part when the selection across multiple depth of a nested lists', () => { + t.applyCommand( + indentList, + markdown` + - A1a + + - B1a + + - C1 + + B1b + + A1b + `, + markdown` + - A1a + + - B1a + + - C1 + + B1b + + A1b + `, + ) + + t.applyCommand( + indentList, + markdown` + - A1a + + - B1a + + - C1 + + B1b + + - B2 + + - B3 + + - B4 + + A1b + `, + markdown` + - A1a + + - B1a + + - C1 + + B1b + + - B2 + + - B3 + + - B4 + + A1b + `, + ) + }) + + it('can indent multiple list nodes and append them to the previous list node', () => { + t.applyCommand( + indentList, + markdown` + - A1 + - A2 + - A3 + `, + markdown` + - A1 + - A2 + - A3 + `, + ) + }) + + it('can add ambitious indentations', () => { + t.applyCommand( + indentList, + markdown` + - A1 + - B2 + `, + markdown` + - A1 + - - B2 + `, + ) + }) + + it('can split the list when necessary', () => { + t.applyCommand( + indentList, + markdown` + - A1 + + - B2a + + B2b + + B2c + `, + markdown` + - A1 + + - - B2a + + - B2b + + B2c + `, + ) + }) + + it('can keep attributes', () => { + t.applyCommand( + indentList, + markdown` + - [ ] A1 + - [x] A2 + `, + markdown` + - [ ] A1 + - [x] A2 + `, + ) + + t.applyCommand( + indentList, + markdown` + 1. A1 + + 2. A2 + + - B1 + `, + markdown` + 1. A1 + + 1. A2 + + - B1 + `, + ) + + t.applyCommand( + indentList, + markdown` + - [x] A1 + - B1 + - [x] A2 + 1. B2 + `, + markdown` + - [x] A1 + - B1 + - [x] A2 + 1. B2 + `, + ) + }) + + it('can keep the indentation of sub list nodes', () => { + t.applyCommand( + indentList, + markdown` + - A1 + - A2 + - A3 + - B1 + - B2 + - B3 + `, + markdown` + - A1 + - A2 + - A3 + - B1 + - B2 + - B3 + `, + ) + }) + + it('can move all collapsed content', () => { + t.applyCommand( + indentList, + t.doc( + t.bulletList(t.p('A1')), + t.bulletList(t.p('A2')), + t.collapsedToggleList( + t.p('A3'), + t.bulletList(t.p('B1')), + t.bulletList(t.p('B2')), + t.bulletList(t.p('B3')), + ), + ), + t.doc( + t.bulletList(t.p('A1')), + t.bulletList( + t.p('A2'), + t.collapsedToggleList( + t.p('A3'), + t.bulletList(t.p('B1')), + t.bulletList(t.p('B2')), + t.bulletList(t.p('B3')), + ), + ), + ), + ) + }) + + it('can expand a collapsed list node if something is indent into it', () => { + t.applyCommand( + indentList, + t.doc( + t.collapsedToggleList( + t.p('A1'), + t.bulletList(t.p('B1')), + t.bulletList(t.p('B2')), + t.bulletList(t.p('B3')), + ), + t.p(''), + ), + t.doc( + t.expandedToggleList( + t.p('A1'), + t.bulletList(t.p('B1')), + t.bulletList(t.p('B2')), + t.bulletList(t.p('B3')), + t.p(''), + ), + ), + ) + }) + + it('can keep the indentation of sub list nodes when moving multiple list', () => { + t.applyCommand( + indentList, + markdown` + - A1 + - A2 + - A3 + - B1 + - B2 + - B3 + `, + markdown` + - A1 + - A2 + - A3 + - B1 + - B2 + - B3 + `, + ) + + t.applyCommand( + indentList, + markdown` + - A1 + - B1 + - A2 + - B2 + - C1 + `, + markdown` + - A1 + - B1 + - A2 + - B2 + - C1 + `, + ) + }) + + it('can keep the indentation of siblings around the indented item', () => { + t.applyCommand( + indentList, + markdown` + - A1 + + - A2 + + A2 + `, + markdown` + - A1 + + - A2 + + A2 + `, + ) + + t.applyCommand( + indentList, + markdown` + - A1 + + - A2 + + A2 + + - B1 + `, + markdown` + - A1 + + - A2 + + A2 + + - B1 + `, + ) + + t.applyCommand( + indentList, + markdown` + - A1 + + - A2 + + - B1 + + A2 + + A2 + + - B1 + `, + markdown` + - A1 + + - A2 + + - B1 + + A2 + + A2 + + - B1 + `, + ) + + t.applyCommand( + indentList, + markdown` + - A1 + + A1 + + - A2 + `, + markdown` + - A1 + + A1 + + - A2 + `, + ) + }) + + it('can indent a paragraph that not inside a list node', () => { + t.applyCommand( + indentList, + markdown` + - A1 + + P1 + `, + markdown` + - A1 + + P1 + `, + ) + + t.applyCommand( + indentList, + markdown` + - A1 + + P1 + + P2 + + P3 + `, + markdown` + - A1 + + P1 + + P2 + + P3 + `, + ) + }) + + it('can accept custom positions', () => { + t.applyCommand( + createIndentListCommand({ from: 13, to: 17 }), + t.doc( + /*0*/ + t.bulletList(/*1*/ t.p('A1') /*5*/), + /*6*/ + t.bulletList(/*7*/ t.p('A2') /*11*/), + /*12*/ + t.bulletList(/*13*/ t.p('A3') /*17*/), + /*18*/ + ), + t.doc( + t.bulletList(t.p('A1')), + t.bulletList(t.p('A2'), t.bulletList(t.p('A3'))), + ), + ) + + t.applyCommand( + createIndentListCommand({ from: 10, to: 17 }), + t.doc( + /*0*/ + t.bulletList(/*1*/ t.p('A1') /*5*/), + /*6*/ + t.bulletList(/*7*/ t.p('A2') /*11*/), + /*12*/ + t.bulletList(/*13*/ t.p('A3') /*17*/), + /*18*/ + ), + t.doc( + t.bulletList( + t.p('A1'), + t.bulletList(t.p('A2')), + t.bulletList(t.p('A3')), + ), + ), + ) + }) + + it('can handle some complex nested lists', () => { + t.applyCommand( + indentList, + markdown` + - A1 + - B1 + - B2 + - A2 + - B3 + - C1 + - D1 + - B4 + `, + markdown` + - A1 + - B1 + - B2 + - A2 + - B3 + - C1 + - D1 + - B4 + `, + ) + }) + + it('only needs one step for some of the most comment indent action', () => { + const countSteps = ( + doc: TaggedProsemirrorNode, + expected: TaggedProsemirrorNode, + ) => { + t.add(doc) + const state = t.view.state + const command = createIndentListCommand() + let count = -1 + let actual: ProsemirrorNode | null = null + command(state, (tr) => { + count = tr.steps.length + actual = tr.doc + }) + expect(actual).not.equal(null) + expect(actual).toEqualRemirrorDocument(expected) + return count + } + + expect( + countSteps( + markdown` + - A1 + - A2 + `, + markdown` + - A1 + - A2 + `, + ), + ).toBe(1) + + expect( + countSteps( + markdown` + - A1 + - [ ] A2 + - [x] A3 + `, + markdown` + - A1 + - [ ] A2 + - [x] A3 + `, + ), + ).toBe(1) + + expect( + countSteps( + markdown` + 1. A1 + 2. A2 + 3. A3 + 4. A4 + `, + markdown` + 1. A1 + 1. A2 + 2. A3 + 2. A4 + `, + ), + ).toBe(1) + + expect( + countSteps( + markdown` + 1. A1 + - B1 + - B2 + - B3 + - B4 + `, + markdown` + 1. A1 + - B1 + - B2 + - B3 + - B4 + `, + ), + ).toBe(1) + + // For more complex (and less common) cases, more steps is acceptable + expect( + countSteps( + markdown` + - A1 + + - B1 + + - C1 + + - D1 + + D1b + + - D2 + + C1b + + C1c + + - B2 + + - C2 + + - A2 + + - A3 + + - B3 + + B3b + + - B4 + + A3b + `, + markdown` + - A1 + + - B1 + + - C1 + + - D1 + + D1b + + - D2 + + C1b + + C1c + + - B2 + + - C2 + + - A2 + + - A3 + + - B3 + + B3b + + - B4 + + A3b + `, + ), + ).toBeGreaterThan(1) + }) +}) diff --git a/packages/editor/src/core/extensions/flat-list/commands/indent-list.ts b/packages/editor/src/core/extensions/flat-list/commands/indent-list.ts new file mode 100644 index 00000000000..7961b9bcf4d --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/commands/indent-list.ts @@ -0,0 +1,153 @@ +import { Fragment, type NodeRange, Slice } from "prosemirror-model"; +import { type Command, type Transaction } from "prosemirror-state"; +import { ReplaceAroundStep } from "prosemirror-transform"; + +import { withAutoFixList } from "../utils/auto-fix-list"; +import { atEndBlockBoundary, atStartBlockBoundary } from "../utils/block-boundary"; +import { getListType } from "../utils/get-list-type"; +import { inCollapsedList } from "../utils/in-collapsed-list"; +import { isListNode } from "../utils/is-list-node"; +import { findListsRange } from "../utils/list-range"; +import { mapPos } from "../utils/map-pos"; +import { zoomInRange } from "../utils/zoom-in-range"; + +import { withVisibleSelection } from "./set-safe-selection"; +import { ListAttributes } from "prosemirror-flat-list"; + +/** + * @public + */ +export interface IndentListOptions { + /** + * A optional from position to indent. + * + * @defaultValue `state.selection.from` + */ + from?: number; + + /** + * A optional to position to indent. + * + * @defaultValue `state.selection.to` + */ + to?: number; +} + +/** + * Returns a command function that increases the indentation of selected list + * nodes. + * + * @public @group Commands + */ +export function createIndentListCommand(options?: IndentListOptions): Command { + const indentListCommand: Command = (state, dispatch): boolean => { + const tr = state.tr; + + const $from = options?.from == null ? tr.selection.$from : tr.doc.resolve(options.from); + const $to = options?.to == null ? tr.selection.$to : tr.doc.resolve(options.to); + + const range = findListsRange($from, $to) || $from.blockRange($to); + if (!range) return false; + + if (indentRange(range, tr)) { + dispatch?.(tr); + return true; + } + return false; + }; + + return withVisibleSelection(withAutoFixList(indentListCommand)); +} + +function indentRange(range: NodeRange, tr: Transaction, startBoundary?: boolean, endBoundary?: boolean): boolean { + const { depth, $from, $to } = range; + + startBoundary = startBoundary || atStartBlockBoundary($from, depth + 1); + + if (!startBoundary) { + const { startIndex, endIndex } = range; + if (endIndex - startIndex === 1) { + const contentRange = zoomInRange(range); + return contentRange ? indentRange(contentRange, tr) : false; + } else { + return splitAndIndentRange(range, tr, startIndex + 1); + } + } + + endBoundary = endBoundary || atEndBlockBoundary($to, depth + 1); + + if (!endBoundary && !inCollapsedList($to)) { + const { startIndex, endIndex } = range; + if (endIndex - startIndex === 1) { + const contentRange = zoomInRange(range); + return contentRange ? indentRange(contentRange, tr) : false; + } else { + return splitAndIndentRange(range, tr, endIndex - 1); + } + } + + return indentNodeRange(range, tr); +} + +/** + * Split a range into two parts, and indent them separately. + */ +function splitAndIndentRange(range: NodeRange, tr: Transaction, splitIndex: number): boolean { + const { $from, $to, depth } = range; + + const splitPos = $from.posAtIndex(splitIndex, depth); + + const range1 = $from.blockRange(tr.doc.resolve(splitPos - 1)); + if (!range1) return false; + + const getRange2From = mapPos(tr, splitPos + 1); + const getRange2To = mapPos(tr, $to.pos); + + indentRange(range1, tr, undefined, true); + + const range2 = tr.doc.resolve(getRange2From()).blockRange(tr.doc.resolve(getRange2To())); + + if (range2) { + indentRange(range2, tr, true, undefined); + } + return true; +} + +/** + * Increase the indentation of a block range. + */ +function indentNodeRange(range: NodeRange, tr: Transaction): boolean { + const listType = getListType(tr.doc.type.schema); + const { parent, startIndex } = range; + const prevChild = startIndex >= 1 && parent.child(startIndex - 1); + + // If the previous node before the range is a list node, move the range into + // the previous list node as its children + if (prevChild && isListNode(prevChild)) { + const { start, end } = range; + tr.step( + new ReplaceAroundStep(start - 1, end, start, end, new Slice(Fragment.from(listType.create(null)), 1, 0), 0, true) + ); + return true; + } + + // If we can avoid to add a new bullet visually, we can wrap the range with a + // new list node. + const isParentListNode = isListNode(parent); + const isFirstChildListNode = isListNode(parent.maybeChild(startIndex)); + if ((startIndex === 0 && isParentListNode) || isFirstChildListNode) { + const { start, end } = range; + const listAttrs: ListAttributes | null = isFirstChildListNode + ? parent.child(startIndex).attrs + : isParentListNode + ? parent.attrs + : null; + tr.step( + new ReplaceAroundStep(start, end, start, end, new Slice(Fragment.from(listType.create(listAttrs)), 0, 0), 1, true) + ); + return true; + } + + // Otherwise we cannot indent + return false; +} diff --git a/packages/editor/src/core/extensions/flat-list/commands/join-collapsed-backward.ts b/packages/editor/src/core/extensions/flat-list/commands/join-collapsed-backward.ts new file mode 100644 index 00000000000..9b4c5613050 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/commands/join-collapsed-backward.ts @@ -0,0 +1,56 @@ +import { type ResolvedPos } from "prosemirror-model"; +import { type Command, TextSelection } from "prosemirror-state"; + +import { atTextblockStart } from "../utils/at-textblock-start"; +import { isListNode } from "../utils/is-list-node"; + +import { joinTextblocksAround } from "./join-textblocks-around"; +import { ListAttributes } from "prosemirror-flat-list"; + +/** + * If the selection is empty and at the start of a block, and there is a + * collapsed list node right before the cursor, move current block and append it + * to the first child of the collapsed list node (i.e. skip the hidden content). + * + * @public @group Commands + */ +export const joinCollapsedListBackward: Command = (state, dispatch, view) => { + const $cursor = atTextblockStart(state, view); + if (!$cursor) return false; + + const $cut = findCutBefore($cursor); + if (!$cut) return false; + + const { nodeBefore, nodeAfter } = $cut; + + if ( + nodeBefore && + nodeAfter && + isListNode(nodeBefore) && + (nodeBefore.attrs as ListAttributes).collapsed && + nodeAfter.isBlock + ) { + const tr = state.tr; + const listPos = $cut.pos - nodeBefore.nodeSize; + tr.delete($cut.pos, $cut.pos + nodeAfter.nodeSize); + const insert = listPos + 1 + nodeBefore.child(0).nodeSize; + tr.insert(insert, nodeAfter); + const $insert = tr.doc.resolve(insert); + tr.setSelection(TextSelection.near($insert)); + if (joinTextblocksAround(tr, $insert, dispatch)) { + return true; + } + } + + return false; +}; + +// https://github.com/prosemirror/prosemirror-commands/blob/e607d5abda0fcc399462e6452a82450f4118702d/src/commands.ts#L150 +function findCutBefore($pos: ResolvedPos): ResolvedPos | null { + if (!$pos.parent.type.spec.isolating) + for (let i = $pos.depth - 1; i >= 0; i--) { + if ($pos.index(i) > 0) return $pos.doc.resolve($pos.before(i + 1)); + if ($pos.node(i).type.spec.isolating) break; + } + return null; +} diff --git a/packages/editor/src/core/extensions/flat-list/commands/join-list-up.ts b/packages/editor/src/core/extensions/flat-list/commands/join-list-up.ts new file mode 100644 index 00000000000..e59cad37540 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/commands/join-list-up.ts @@ -0,0 +1,76 @@ +import { NodeRange, type ResolvedPos } from 'prosemirror-model' +import { + type Command, + type EditorState, + type Transaction, +} from 'prosemirror-state' + +import { atTextblockStart } from '../utils/at-textblock-start' +import { isListNode } from '../utils/is-list-node' +import { safeLift } from '../utils/safe-lift' + +/** + * If the text cursor is at the start of the first child of a list node, lift + * all content inside the list. If the text cursor is at the start of the last + * child of a list node, lift this child. + * + * @public @group Commands + */ +export const joinListUp: Command = (state, dispatch, view) => { + const $cursor = atTextblockStart(state, view) + if (!$cursor) return false + + const { depth } = $cursor + if (depth < 2) return false + const listDepth = depth - 1 + + const listNode = $cursor.node(listDepth) + if (!isListNode(listNode)) return false + + const indexInList = $cursor.index(listDepth) + + if (indexInList === 0) { + if (dispatch) { + liftListContent(state, dispatch, $cursor) + } + return true + } + + if (indexInList === listNode.childCount - 1) { + if (dispatch) { + liftParent(state, dispatch, $cursor) + } + return true + } + + return false +} + +function liftListContent( + state: EditorState, + dispatch: (tr: Transaction) => void, + $cursor: ResolvedPos, +) { + const tr = state.tr + const listDepth = $cursor.depth - 1 + const range = new NodeRange( + $cursor, + tr.doc.resolve($cursor.end(listDepth)), + listDepth, + ) + if (safeLift(tr, range)) { + dispatch(tr) + } +} + +function liftParent( + state: EditorState, + dispatch: (tr: Transaction) => void, + $cursor: ResolvedPos, +) { + const tr = state.tr + const range = $cursor.blockRange() + if (range && safeLift(tr, range)) { + dispatch(tr) + } +} diff --git a/packages/editor/src/core/extensions/flat-list/commands/join-textblocks-around.ts b/packages/editor/src/core/extensions/flat-list/commands/join-textblocks-around.ts new file mode 100644 index 00000000000..cf05586c4aa --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/commands/join-textblocks-around.ts @@ -0,0 +1,36 @@ +/* eslint-disable prefer-const */ + +import { type ResolvedPos, Slice } from 'prosemirror-model' +import { TextSelection, type Transaction } from 'prosemirror-state' +import { replaceStep, ReplaceStep } from 'prosemirror-transform' + +// prettier-ignore +// https://github.com/prosemirror/prosemirror-commands/blob/e607d5abda0fcc399462e6452a82450f4118702d/src/commands.ts#L94 +function joinTextblocksAround(tr: Transaction, $cut: ResolvedPos, dispatch?: (tr: Transaction) => void) { + let before = $cut.nodeBefore!, beforeText = before, beforePos = $cut.pos - 1 + for (; !beforeText.isTextblock; beforePos--) { + if (beforeText.type.spec.isolating) return false + let child = beforeText.lastChild + if (!child) return false + beforeText = child + } + let after = $cut.nodeAfter!, afterText = after, afterPos = $cut.pos + 1 + for (; !afterText.isTextblock; afterPos++) { + if (afterText.type.spec.isolating) return false + let child = afterText.firstChild + if (!child) return false + afterText = child + } + let step = replaceStep(tr.doc, beforePos, afterPos, Slice.empty) as ReplaceStep | null + if (!step || step.from != beforePos || + step instanceof ReplaceStep && step.slice.size >= afterPos - beforePos) return false + if (dispatch) { + tr.step(step) + tr.setSelection(TextSelection.create(tr.doc, beforePos)) + dispatch(tr.scrollIntoView()) + } + return true + +} + +export { joinTextblocksAround } diff --git a/packages/editor/src/core/extensions/flat-list/commands/keymap.spec.ts b/packages/editor/src/core/extensions/flat-list/commands/keymap.spec.ts new file mode 100644 index 00000000000..cc3bd9f1486 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/commands/keymap.spec.ts @@ -0,0 +1,208 @@ +import { describe, expect, it } from 'vitest' + +import { setupTestingEditor } from '../../test/setup-editor' + +import { backspaceCommand } from './keymap' + +describe('Keymap', () => { + const t = setupTestingEditor() + const markdown = t.markdown + + describe('Backspace', () => { + it('should delete the empty paragraph between two list nodes', () => { + t.applyCommand( + backspaceCommand, + t.doc( + t.bulletList(t.p('A1')), + t.p(''), + t.bulletList(t.p('A2')), + ), + t.doc(t.bulletList(t.p('A1')), t.bulletList(t.p('A2'))), + ) + }) + + it('can handle nested list', () => { + const doc1 = markdown` + - A1 + + - B1 + + - B2 + ` + const doc2 = markdown` + - A1 + + - B1 + + B2 + ` + const doc3 = markdown` + - A1 + + - B1 + + B2 + ` + const doc4 = markdown` + - A1 + + - B1B2 + ` + + t.add(doc1) + t.editor.press('Backspace') + expect(t.editor.state).toEqualRemirrorState(doc2) + t.editor.press('Backspace') + expect(t.editor.state).toEqualRemirrorState(doc3) + t.editor.press('Backspace') + expect(t.editor.state).toEqualRemirrorState(doc4) + }) + + it('can handle nested list with multiple children', () => { + const doc1 = markdown` + - A1 + + - B1 + + - B2a + + B2b + + B2c + ` + const doc2 = markdown` + - A1 + + - B1 + + B2a + + B2b + + B2c + ` + const doc3 = markdown` + - A1 + + - B1B2a + + B2b + + B2c + ` + + t.add(doc1) + t.editor.press('Backspace') + expect(t.editor.state).toEqualRemirrorState(doc2) + t.editor.press('Backspace') + expect(t.editor.state).toEqualRemirrorState(doc3) + }) + + it('can handle cursor in the middle child', () => { + const doc1 = markdown` + - A1 + + - B1 + + - B2a + + B2b + + B2c + ` + const doc2 = markdown` + - A1 + + - B1 + + - B2aB2b + + B2c + ` + t.add(doc1) + t.editor.press('Backspace') + expect(t.editor.state).toEqualRemirrorState(doc2) + }) + + it('can handle cursor in the last child', () => { + const doc1 = markdown` + - A1 + + - B1 + + - B2a + + B2b + + B2c + ` + const doc2 = markdown` + - A1 + + - B1 + + - B2a + + B2b + + B2c + ` + const doc3 = markdown` + - A1 + + - B1 + + - B2a + + B2b + + B2c + ` + t.add(doc1) + t.editor.press('Backspace') + expect(t.editor.state).toEqualRemirrorState(doc2) + t.editor.press('Backspace') + expect(t.editor.state).toEqualRemirrorState(doc3) + }) + + it('can skip collapsed content', () => { + t.applyCommand( + backspaceCommand, + t.doc( + t.collapsedToggleList( + // + t.p('A1'), + t.bulletList(t.p('B1')), + ), + t.p('A2'), + ), + t.doc( + t.collapsedToggleList( + // + t.p('A1A2'), + t.bulletList(t.p('B1')), + ), + ), + ) + + t.applyCommand( + backspaceCommand, + t.doc( + t.collapsedToggleList( + // + t.p('A1'), + t.bulletList(t.p('B1')), + ), + t.blockquote(t.p('A2')), + ), + t.doc( + t.collapsedToggleList( + // + t.p('A1A2'), + t.bulletList(t.p('B1')), + ), + ), + ) + }) + }) +}) diff --git a/packages/editor/src/core/extensions/flat-list/commands/keymap.ts b/packages/editor/src/core/extensions/flat-list/commands/keymap.ts new file mode 100644 index 00000000000..b0c94fa0285 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/commands/keymap.ts @@ -0,0 +1,91 @@ +import { + chainCommands, + deleteSelection, + joinTextblockBackward, + joinTextblockForward, + selectNodeBackward, + selectNodeForward, +} from 'prosemirror-commands' + +import { createDedentListCommand } from './dedent-list' +import { createIndentListCommand } from './indent-list' +import { joinCollapsedListBackward } from './join-collapsed-backward' +import { joinListUp } from './join-list-up' +import { protectCollapsed } from './protect-collapsed' +import { createSplitListCommand } from './split-list' + +/** + * Keybinding for `Enter`. It's chained with following commands: + * + * - {@link protectCollapsed} + * - {@link createSplitListCommand} + * + * @public @group Commands + */ +export const enterCommand = chainCommands( + protectCollapsed, + createSplitListCommand(), +) + +/** + * Keybinding for `Backspace`. It's chained with following commands: + * + * - {@link protectCollapsed} + * - [deleteSelection](https://prosemirror.net/docs/ref/#commands.deleteSelection) + * - {@link joinListUp} + * - {@link joinCollapsedListBackward} + * - [joinTextblockBackward](https://prosemirror.net/docs/ref/#commands.joinTextblockBackward) + * - [selectNodeBackward](https://prosemirror.net/docs/ref/#commands.selectNodeBackward) + * + * @public @group Commands + * + */ +export const backspaceCommand = chainCommands( + protectCollapsed, + deleteSelection, + joinListUp, + joinCollapsedListBackward, + joinTextblockBackward, + selectNodeBackward, +) + +/** + * Keybinding for `Delete`. It's chained with following commands: + * + * - {@link protectCollapsed} + * - [deleteSelection](https://prosemirror.net/docs/ref/#commands.deleteSelection) + * - [joinTextblockForward](https://prosemirror.net/docs/ref/#commands.joinTextblockForward) + * - [selectNodeForward](https://prosemirror.net/docs/ref/#commands.selectNodeForward) + * + * @public @group Commands + * + */ +export const deleteCommand = chainCommands( + protectCollapsed, + deleteSelection, + joinTextblockForward, + selectNodeForward, +) + +/** + * Returns an object containing the keymap for the list commands. + * + * - `Enter`: See {@link enterCommand}. + * - `Backspace`: See {@link backspaceCommand}. + * - `Delete`: See {@link deleteCommand}. + * - `Mod-[`: Decrease indentation. See {@link createDedentListCommand}. + * - `Mod-]`: Increase indentation. See {@link createIndentListCommand}. + * + * @public @group Commands + */ +export const listKeymap = { + Enter: enterCommand, + + Backspace: backspaceCommand, + + Delete: deleteCommand, + + 'Mod-[': createDedentListCommand(), + + 'Mod-]': createIndentListCommand(), +} diff --git a/packages/editor/src/core/extensions/flat-list/commands/move-list.spec.ts b/packages/editor/src/core/extensions/flat-list/commands/move-list.spec.ts new file mode 100644 index 00000000000..9992703d7ee --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/commands/move-list.spec.ts @@ -0,0 +1,84 @@ +import { describe, it } from 'vitest' + +import { setupTestingEditor } from '../../test/setup-editor' + +import { createMoveListCommand } from './move-list' + +describe('moveList', () => { + const t = setupTestingEditor() + const markdown = t.markdown + const moveUp = createMoveListCommand('up') + const moveDown = createMoveListCommand('down') + + it('can move up list nodes', () => { + t.applyCommand( + moveUp, + markdown` + - A1 + - A2 + - A3 + `, + markdown` + - A2 + - A3 + - A1 + `, + ) + }) + + it('can move up and dedent list nodes to parent list', () => { + t.applyCommand( + moveUp, + markdown` + - A1 + - A2 + - B1 + - B2 + - B3 + `, + markdown` + - A1 + - B1 + - B2 + - A2 + - B3 + `, + ) + }) + + it('can move down list nodes', () => { + t.applyCommand( + moveDown, + markdown` + - A1 + - A2 + - A3 + `, + markdown` + - A3 + - A1 + - A2 + `, + ) + }) + + it('can move down and dedent list nodes to parent list', () => { + t.applyCommand( + moveDown, + markdown` + - A1 + - A2 + - B1 + - B2 + - A3 + `, + markdown` + - A1 + - A2 + - A3 + - B1 + - B2 + `, + ) + }) +}) diff --git a/packages/editor/src/core/extensions/flat-list/commands/move-list.ts b/packages/editor/src/core/extensions/flat-list/commands/move-list.ts new file mode 100644 index 00000000000..b1f1758eae1 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/commands/move-list.ts @@ -0,0 +1,86 @@ +import { type Command, type Transaction } from 'prosemirror-state' + +import { withAutoFixList } from '../utils/auto-fix-list' +import { cutByIndex } from '../utils/cut-by-index' +import { isListNode } from '../utils/is-list-node' +import { findListsRange } from '../utils/list-range' +import { safeLift } from '../utils/safe-lift' + +/** + * Returns a command function that moves up or down selected list nodes. + * + * @public @group Commands + * + */ +export function createMoveListCommand(direction: 'up' | 'down'): Command { + const moveList: Command = (state, dispatch): boolean => { + const tr = state.tr + if (doMoveList(tr, direction, true, !!dispatch)) { + dispatch?.(tr) + return true + } + return false + } + + return withAutoFixList(moveList) +} + +/** @internal */ +export function doMoveList( + tr: Transaction, + direction: 'up' | 'down', + canDedent: boolean, + dispatch: boolean, +): boolean { + const { $from, $to } = tr.selection + const range = findListsRange($from, $to) + if (!range) return false + + const { parent, depth, startIndex, endIndex } = range + + if (direction === 'up') { + if (startIndex >= 2 || (startIndex === 1 && isListNode(parent.child(0)))) { + const before = cutByIndex(parent.content, startIndex - 1, startIndex) + const selected = cutByIndex(parent.content, startIndex, endIndex) + if ( + parent.canReplace(startIndex - 1, endIndex, selected.append(before)) + ) { + if (dispatch) { + tr.insert($from.posAtIndex(endIndex, depth), before) + tr.delete( + $from.posAtIndex(startIndex - 1, depth), + $from.posAtIndex(startIndex, depth), + ) + } + return true + } else { + return false + } + } else if (canDedent && isListNode(parent)) { + return safeLift(tr, range) && doMoveList(tr, direction, false, dispatch) + } else { + return false + } + } else { + if (endIndex < parent.childCount) { + const selected = cutByIndex(parent.content, startIndex, endIndex) + const after = cutByIndex(parent.content, endIndex, endIndex + 1) + if (parent.canReplace(startIndex, endIndex + 1, after.append(selected))) { + if (dispatch) { + tr.delete( + $from.posAtIndex(endIndex, depth), + $from.posAtIndex(endIndex + 1, depth), + ) + tr.insert($from.posAtIndex(startIndex, depth), after) + } + return true + } else { + return false + } + } else if (canDedent && isListNode(parent)) { + return safeLift(tr, range) && doMoveList(tr, direction, false, dispatch) + } else { + return false + } + } +} diff --git a/packages/editor/src/core/extensions/flat-list/commands/protect-collapsed.spec.ts b/packages/editor/src/core/extensions/flat-list/commands/protect-collapsed.spec.ts new file mode 100644 index 00000000000..3b52a956995 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/commands/protect-collapsed.spec.ts @@ -0,0 +1,41 @@ +import { describe, expect, it } from 'vitest' + +import { setupTestingEditor } from '../../test/setup-editor' + +describe('protectCollapsed', () => { + const { add, doc, p, editor, collapsedToggleList, expandedToggleList } = + setupTestingEditor() + + it('can skip collapsed content', () => { + // Cursor in the last paragraph of the item + add( + doc( + collapsedToggleList( + // + p('123'), + p('456'), + ), + collapsedToggleList( + // + p('123'), + p('456'), + ), + ), + ) + editor.press('Enter') + expect(editor.state).toEqualRemirrorState( + doc( + expandedToggleList( + // + p('123'), + p('456'), + ), + expandedToggleList( + // + p('123'), + p('456'), + ), + ), + ) + }) +}) diff --git a/packages/editor/src/core/extensions/flat-list/commands/protect-collapsed.ts b/packages/editor/src/core/extensions/flat-list/commands/protect-collapsed.ts new file mode 100644 index 00000000000..0e09d6a187f --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/commands/protect-collapsed.ts @@ -0,0 +1,43 @@ +import type { Command } from 'prosemirror-state' + +import { isCollapsedListNode } from '../utils/is-collapsed-list-node' + +/** + * This command will protect the collapsed items from being deleted. + * + * If current selection contains a collapsed item, we don't want the user to + * delete this selection by pressing Backspace or Delete, because this could + * be unintentional. + * + * In such case, we will stop the delete action and expand the collapsed items + * instead. Therefore the user can clearly know what content he is trying to + * delete. + * + * @public @group Commands + * + */ +export const protectCollapsed: Command = (state, dispatch): boolean => { + const tr = state.tr + let found = false + const { from, to } = state.selection + + state.doc.nodesBetween(from, to, (node, pos, parent, index) => { + if (found && !dispatch) { + return false + } + if (parent && isCollapsedListNode(parent) && index >= 1) { + found = true + if (!dispatch) { + return false + } + + const $pos = state.doc.resolve(pos) + tr.setNodeAttribute($pos.before($pos.depth), 'collapsed', false) + } + }) + + if (found) { + dispatch?.(tr) + } + return found +} diff --git a/packages/editor/src/core/extensions/flat-list/commands/set-safe-selection.spec.ts b/packages/editor/src/core/extensions/flat-list/commands/set-safe-selection.spec.ts new file mode 100644 index 00000000000..34797d8582c --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/commands/set-safe-selection.spec.ts @@ -0,0 +1,154 @@ +import { type Command } from 'prosemirror-state' +import { describe, it } from 'vitest' + +import { setupTestingEditor } from '../../test/setup-editor' + +import { setSafeSelection } from './set-safe-selection' + +describe('setSafeSelection', () => { + const { + doc, + p, + collapsedToggleList, + expandedToggleList, + bulletList, + applyCommand, + } = setupTestingEditor() + + const command: Command = (state, dispatch) => { + dispatch?.(setSafeSelection(state.tr)) + return true + } + + it('can move cursor outside of collapsed content', () => { + applyCommand( + command, + doc( + collapsedToggleList( + // + p('123'), + p('456'), + ), + ), + doc( + collapsedToggleList( + // + p('123'), + p('456'), + ), + ), + ) + }) + + it('can move cursor outside of collapsed and deep sub list', () => { + applyCommand( + command, + doc( + bulletList( + bulletList( + bulletList( + collapsedToggleList( + // + p('123'), + p('456'), + ), + ), + ), + ), + ), + doc( + bulletList( + bulletList( + bulletList( + collapsedToggleList( + // + p('123'), + p('456'), + ), + ), + ), + ), + ), + ) + }) + + it('does not change if the cursor is visible ', () => { + applyCommand( + command, + doc( + collapsedToggleList( + // + p('123'), + p('456'), + ), + ), + doc( + collapsedToggleList( + // + p('123'), + p('456'), + ), + ), + ) + }) + + it('can handle from position', () => { + applyCommand( + command, + doc( + collapsedToggleList( + // + p('123'), + p('456'), + ), + expandedToggleList( + // + p('123'), + p('456'), + ), + ), + doc( + collapsedToggleList( + // + p('123'), + p('456'), + ), + expandedToggleList( + // + p('123'), + p('456'), + ), + ), + ) + }) + + it('can handle to position', () => { + applyCommand( + command, + doc( + expandedToggleList( + // + p('123'), + p('456'), + ), + collapsedToggleList( + // + p('123'), + p('456'), + ), + ), + doc( + expandedToggleList( + // + p('123'), + p('456'), + ), + collapsedToggleList( + // + p('123'), + p('456'), + ), + ), + ) + }) +}) diff --git a/packages/editor/src/core/extensions/flat-list/commands/set-safe-selection.ts b/packages/editor/src/core/extensions/flat-list/commands/set-safe-selection.ts new file mode 100644 index 00000000000..67049729027 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/commands/set-safe-selection.ts @@ -0,0 +1,70 @@ +import { type ResolvedPos } from 'prosemirror-model' +import { + type Selection, + TextSelection, + type Transaction, +} from 'prosemirror-state' + +import { isCollapsedListNode } from '../utils/is-collapsed-list-node' +import { patchCommand } from '../utils/patch-command' +import { setListAttributes } from '../utils/set-list-attributes' + +function moveOutOfCollapsed( + $pos: ResolvedPos, + minDepth: number, +): Selection | null { + for (let depth = minDepth; depth <= $pos.depth; depth++) { + if (isCollapsedListNode($pos.node(depth)) && $pos.index(depth) >= 1) { + const before = $pos.posAtIndex(1, depth) + const $before = $pos.doc.resolve(before) + return TextSelection.near($before, -1) + } + } + return null +} + +/** + * If one of the selection's end points is inside a collapsed node, move the selection outside of it + * + * @internal + */ +export function setSafeSelection(tr: Transaction): Transaction { + const { $from, $to, to } = tr.selection + const selection = + moveOutOfCollapsed($from, 0) || + moveOutOfCollapsed($to, $from.sharedDepth(to)) + if (selection) { + tr.setSelection(selection) + } + return tr +} + +export const withSafeSelection = patchCommand(setSafeSelection) + +function getCollapsedPosition($pos: ResolvedPos, minDepth: number) { + for (let depth = minDepth; depth <= $pos.depth; depth++) { + if (isCollapsedListNode($pos.node(depth)) && $pos.index(depth) >= 1) { + return $pos.before(depth) + } + } + return null +} + +/** + * If one of the selection's end points is inside a collapsed node, expand it + * + * @internal + */ +export function setVisibleSelection(tr: Transaction): Transaction { + const { $from, $to, to } = tr.selection + const pos = + getCollapsedPosition($from, 0) ?? + getCollapsedPosition($to, $from.sharedDepth(to)) + if (pos != null) { + tr.doc.resolve(pos) + setListAttributes(tr, pos, { collapsed: false }) + } + return tr +} + +export const withVisibleSelection = patchCommand(setVisibleSelection) diff --git a/packages/editor/src/core/extensions/flat-list/commands/split-list.spec.ts b/packages/editor/src/core/extensions/flat-list/commands/split-list.spec.ts new file mode 100644 index 00000000000..53837e2ef1f --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/commands/split-list.spec.ts @@ -0,0 +1,516 @@ +import { NodeSelection } from 'prosemirror-state' +import { describe, expect, it } from 'vitest' + +import { setupTestingEditor } from '../../test/setup-editor' + +import { enterCommand } from './keymap' + +describe('splitList', () => { + const { + add, + doc, + p, + bulletList, + blockquote, + editor, + markdown, + applyCommand, + collapsedToggleList, + expandedToggleList, + checkedTaskList, + uncheckedTaskList, + } = setupTestingEditor() + + it('can split non-empty item', () => { + applyCommand( + enterCommand, + markdown` + - 123 + - 234 + + paragraph + `, + markdown` + - 123 + - 234 + - + + paragraph + `, + ) + + applyCommand( + enterCommand, + markdown` + - 123 + - 234 + `, + markdown` + - 123 + - 23 + - 4 + `, + ) + + applyCommand( + enterCommand, + markdown` + - 123 + - 234 + `, + markdown` + - 1 + - 23 + - 234 + `, + ) + }) + + it('can split non-empty sub item', () => { + applyCommand( + enterCommand, + markdown` + - 123 + - 456 + + paragraph + `, + markdown` + - 123 + - 456 + - + + paragraph + `, + ) + }) + + it('can delete empty item', () => { + applyCommand( + enterCommand, + markdown` + - 123 + - + + paragraph + `, + markdown` + - 123 + + + + paragraph + `, + ) + + applyCommand( + enterCommand, + markdown` + - 123 + - + - 456 + `, + markdown` + - 123 + + + + - 456 + `, + ) + + applyCommand( + enterCommand, + markdown` + - + - 123 + `, + markdown` + + + - 123 + `, + ) + }) + + it('can dedent the last empty sub item', () => { + applyCommand( + enterCommand, + markdown` + - A1 + + - + + paragraph + `, + markdown` + - A1 + + - + + paragraph + `, + ) + + applyCommand( + enterCommand, + markdown` + - A1 + + - B1 + + - + + paragraph + `, + markdown` + - A1 + + - B1 + + - + + paragraph + `, + ) + }) + + it('can delete selected text', () => { + applyCommand( + enterCommand, + markdown` + - 123 + - 456 + `, + markdown` + - + - + - 456 + `, + ) + }) + + it('can set attributes correctly', () => { + applyCommand( + enterCommand, + doc( + checkedTaskList(p('A1')), + uncheckedTaskList(p('A2')), + uncheckedTaskList(p('A3')), + ), + doc( + uncheckedTaskList(p('')), + checkedTaskList(p('A1')), + uncheckedTaskList(p('A2')), + uncheckedTaskList(p('A3')), + ), + ) + + applyCommand( + enterCommand, + doc( + uncheckedTaskList(p('A1')), + checkedTaskList(p('A2')), + uncheckedTaskList(p('A3')), + ), + doc( + uncheckedTaskList(p('A1')), + checkedTaskList(p('A2')), + uncheckedTaskList(p('')), + uncheckedTaskList(p('A3')), + ), + ) + + applyCommand( + enterCommand, + doc( + uncheckedTaskList(p('A1')), + checkedTaskList(p('A2')), + uncheckedTaskList(p('A3')), + ), + doc( + uncheckedTaskList(p('A1')), + checkedTaskList(p('A')), + uncheckedTaskList(p('2')), + uncheckedTaskList(p('A3')), + ), + ) + }) + + it('escapes the item when the cursor is in the first paragraph of the item', () => { + applyCommand( + enterCommand, + markdown` + - 123 + + 456 + + 789 + `, + markdown` + - 123 + + - + + 456 + + 789 + `, + ) + + // Nested list item + applyCommand( + enterCommand, + markdown` + - Parent + + - 123 + + 456 + + 789 + `, + markdown` + - Parent + + - 123 + + - + + 456 + + 789 + `, + ) + }) + + it('can create new paragraph when the caret is not inside the first child of the list', () => { + // Cursor in the last paragraph of the item + add( + doc( + bulletList( + // + p('123'), + p('456'), + ), + ), + ) + editor.press('Enter') + expect(editor.state).toEqualRemirrorState( + doc( + bulletList( + // + p('123'), + p('456'), + p(''), + ), + ), + ) + + // Cursor in the middle paragraph of the item + add( + doc( + bulletList( + // + p('123'), + p('456'), + p('789'), + ), + ), + ) + editor.press('Enter') + expect(editor.state).toEqualRemirrorState( + doc( + bulletList( + // + p('123'), + p('456'), + p(''), + p('789'), + ), + ), + ) + + // Cursor in the last paragraph of the item (nested list item) + add( + doc( + bulletList( + p('parent'), + bulletList( + // + p('123'), + p('456'), + ), + ), + ), + ) + editor.press('Enter') + expect(editor.state).toEqualRemirrorState( + doc( + bulletList( + p('parent'), + bulletList( + // + p('123'), + p(''), + p('456'), + ), + ), + ), + ) + + add( + doc( + bulletList( + // + p('123'), + p(''), + ), + ), + ) + editor.press('Enter') + expect(editor.state).toEqualRemirrorState( + doc( + bulletList( + // + p('123'), + p(''), + p(''), + ), + ), + ) + + add( + doc( + bulletList( + // + p('123'), + p(''), + p('456'), + ), + ), + ) + editor.press('Enter') + expect(editor.state).toEqualRemirrorState( + doc( + bulletList( + // + p('123'), + p(''), + p(''), + p('456'), + ), + ), + ) + }) + + it('can skip collapsed content', () => { + // Cursor in the last paragraph of the item + add( + doc( + collapsedToggleList( + // + p('123'), + p('456'), + ), + ), + ) + editor.press('Enter') + expect(editor.state).toEqualRemirrorState( + doc( + collapsedToggleList( + // + p('1'), + p('456'), + ), + expandedToggleList( + // + p(''), + ), + ), + ) + }) + + it("won't effect non-list document", () => { + applyCommand( + enterCommand, + markdown` + # h1 + + 123 + `, + null, + ) + + applyCommand( + enterCommand, + markdown` + # h1 + + 123 + + > 456 + `, + null, + ) + + add( + doc( + blockquote( + p('123'), + blockquote( + // + p('456'), + ), + ), + ), + ) + editor.press('Enter') + expect(editor.state).toEqualRemirrorState( + doc( + blockquote( + p('123'), + blockquote( + // + p('4'), + p('56'), + ), + ), + ), + ) + }) + + it('can split list node for a block node selection', () => { + add(markdown` + # h1 + + 1. *** + `) + + let hrPos = -1 + editor.doc.descendants((node, pos) => { + if (node.type.name === 'horizontalRule') { + hrPos = pos + } + }) + + expect(hrPos > -1).toBe(true) + const nodeSelection = NodeSelection.create(editor.state.doc, hrPos) + editor.view.dispatch(editor.view.state.tr.setSelection(nodeSelection)) + expect(editor.view.state.selection.toJSON()).toMatchInlineSnapshot(` + { + "anchor": 5, + "type": "node", + } + `) + + editor.press('Enter') + + expect(editor.state).toEqualRemirrorState(markdown` + # h1 + + 1. *** + 2. \n + `) + }) +}) diff --git a/packages/editor/src/core/extensions/flat-list/commands/split-list.ts b/packages/editor/src/core/extensions/flat-list/commands/split-list.ts new file mode 100644 index 00000000000..f41677224f2 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/commands/split-list.ts @@ -0,0 +1,234 @@ +import { chainCommands } from "@tiptap/pm/commands"; +import { isTextSelection } from "@tiptap/core"; +import { canSplit } from "@tiptap/pm/transform"; +import { + type NodeSelection, + type Command, + type EditorState, + Selection, + TextSelection, + type Transaction, +} from "@tiptap/pm/state"; + +import { enterWithoutLift } from "./enter-without-lift"; +import { NodeType, Attrs, Mark, Fragment, NodeRange, type Node as ProsemirrorNode, Slice } from "@tiptap/pm/model"; + +import { ListAttributes, isListNode } from "prosemirror-flat-list"; +import { dedentNodeRange } from "./dedent-list"; + +/** + * Returns a command that split the current list node. + * + * @public @group Commands + * + */ +export function createSplitListCommand(): Command { + return chainCommands(splitBlockNodeSelectionInListCommand, splitListCommand); +} + +function deriveListAttributes(listNode: ProsemirrorNode): ListAttributes { + // For the new list node, we don't want to inherit any list attribute (For example: `checked`) other than `kind` + return { kind: (listNode.attrs as ListAttributes).kind }; +} + +const splitBlockNodeSelectionInListCommand: Command = (state, dispatch) => { + if (!isBlockNodeSelection(state.selection)) { + return false; + } + console.log("aaya 1"); + + const selection = state.selection; + const { $to, node } = selection; + const parent = $to.parent; + + // We only cover the case that + // 1. the list node only contains one child node + // 2. this child node is not a list node + if (isListNode(node) || !isListNode(parent) || parent.childCount !== 1 || parent.firstChild !== node) { + return false; + } + + const listType = parent.type; + const nextList = listType.createAndFill(deriveListAttributes(parent)); + if (!nextList) { + return false; + } + + if (dispatch) { + const tr = state.tr; + const cutPoint = $to.pos; + tr.replace(cutPoint, cutPoint, new Slice(Fragment.fromArray([listType.create(), nextList]), 1, 1)); + const newSelection = TextSelection.near(tr.doc.resolve(cutPoint)); + if (isTextSelection(newSelection)) { + tr.setSelection(newSelection); + dispatch(tr); + } + } + + return true; +}; + +const splitListCommand: Command = (state, dispatch): boolean => { + if (isBlockNodeSelection(state.selection)) { + return false; + } + + console.log("sadf"); + const { $from, $to } = state.selection; + + if (!$from.sameParent($to)) { + return false; + } + + if ($from.depth < 2) { + return false; + } + + const listDepth = $from.depth - 1; + const listNode = $from.node(listDepth); + + if (!isListNode(listNode)) { + return false; + } + + const parent = $from.parent; + + const indexInList = $from.index(listDepth); + const parentEmpty = parent.content.size === 0; + + // When the cursor is inside the first child of the list: + // If the parent block is empty, dedent the list; + // otherwise split and create a new list node. + // When the cursor is inside the second or further children of the list: + // Create a new paragraph. + + if (indexInList === 0) { + console.log("running 2"); + return doSplitList(state, listNode, dispatch); + } else { + return doSplitList(state, listNode, dispatch); + } +}; + +/** + * @internal + */ +export function doSplitList( + state: EditorState, + listNode: ProsemirrorNode, + dispatch?: (tr: Transaction) => void +): boolean { + const tr = state.tr; + const listType = listNode.type; + const attrs: ListAttributes = listNode.attrs; + const newAttrs: ListAttributes = deriveListAttributes(listNode); + + tr.delete(tr.selection.from, tr.selection.to); + + const { $from, $to } = tr.selection; + + const { parentOffset } = $to; + + const atStart = parentOffset == 0; + const atEnd = parentOffset == $to.parent.content.size; + + if (atStart) { + if (dispatch) { + const pos = $from.before(-1); + tr.insert(pos, createAndFill(listType, newAttrs)); + dispatch(tr.scrollIntoView()); + } + return true; + } + + if (atEnd && attrs.collapsed) { + if (dispatch) { + const pos = $from.after(-1); + tr.insert(pos, createAndFill(listType, newAttrs)); + tr.setSelection(Selection.near(tr.doc.resolve(pos))); + dispatch(tr.scrollIntoView()); + } + return true; + } + + // If split the list at the start or at the middle, we want to inherit the + // current parent type (e.g. heading); otherwise, we want to create a new + // default block type (typically paragraph) + const nextType = atEnd ? listNode.contentMatchAt(0).defaultType : undefined; + const typesAfter = [{ type: listType, attrs: newAttrs }, nextType ? { type: nextType } : null]; + + if (!canSplit(tr.doc, $from.pos, 2, typesAfter)) { + return false; + } + + dispatch?.(tr.split($from.pos, 2, typesAfter).scrollIntoView()); + return true; +} + +export function createAndFill( + type: NodeType, + attrs?: Attrs | null, + content?: Fragment | ProsemirrorNode | readonly ProsemirrorNode[] | null, + marks?: readonly Mark[] +) { + const node = type.createAndFill(attrs, content, marks); + if (!node) { + throw new RangeError(`Failed to create '${type.name}' node`); + } + node.check(); + return node; +} + +export function isBlockNodeSelection(selection: Selection): selection is NodeSelection { + const isNodeSelectionBool = isNodeSelection(selection) && selection.node.type.isBlock; + return isNodeSelectionBool; +} + +export function isNodeSelection(selection: Selection): selection is NodeSelection { + return Boolean((selection as NodeSelection).node); +} + +// const splitListCommand: Command = (state, dispatch): boolean => { +// if (isBlockNodeSelection(state.selection)) { +// return false; +// } +// console.log("aaya 2"); +// +// const { $from, $to } = state.selection; +// +// if (!$from.sameParent($to)) { +// console.log("not same parent"); +// return false; +// } +// +// if ($from.depth < 2) { +// console.log("depth is less than 2"); +// return false; +// } +// +// const listDepth = $from.depth - 1; +// const listNode = $from.node(listDepth); +// +// if (!isListNode(listNode)) { +// console.log("not a list node"); +// // return enterWithoutLift(state, dispatch); +// return false; +// } +// +// const parent = $from.parent; +// +// const indexInList = $from.index(listDepth); +// +// // When the cursor is inside the first child of the list: +// // If the parent block is empty, dedent the list; +// // otherwise split and create a new list node. +// // When the cursor is inside the second or further children of the list: +// // Create a new paragraph. +// +// if (indexInList === 0) { +// console.log("running 2"); +// return doSplitList(state, listNode, dispatch); +// } else { +// return doSplitList(state, listNode, dispatch); +// } +// }; diff --git a/packages/editor/src/core/extensions/flat-list/commands/toggle-collapsed.spec.ts b/packages/editor/src/core/extensions/flat-list/commands/toggle-collapsed.spec.ts new file mode 100644 index 00000000000..e114fb9c9ff --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/commands/toggle-collapsed.spec.ts @@ -0,0 +1,63 @@ +import { describe, it } from 'vitest' + +import { setupTestingEditor } from '../../test/setup-editor' + +import { createToggleCollapsedCommand } from './toggle-collapsed' + +describe('toggleCollapsed', () => { + const t = setupTestingEditor() + + it('can toggle collapsed attribute', () => { + t.applyCommand( + createToggleCollapsedCommand(), + t.doc(t.collapsedToggleList(t.p('A1'), t.p('A1'))), + t.doc(t.expandedToggleList(t.p('A1'), t.p('A1'))), + ) + + t.applyCommand( + createToggleCollapsedCommand(), + t.doc(t.expandedToggleList(t.p('A1'), t.p('A1'))), + t.doc(t.collapsedToggleList(t.p('A1'), t.p('A1'))), + ) + }) + + it('can set collapsed value', () => { + t.applyCommand( + createToggleCollapsedCommand({ collapsed: true }), + t.doc(t.collapsedToggleList(t.p('A1'), t.p('A1'))), + t.doc(t.collapsedToggleList(t.p('A1'), t.p('A1'))), + ) + + t.applyCommand( + createToggleCollapsedCommand({ collapsed: true }), + t.doc(t.expandedToggleList(t.p('A1'), t.p('A1'))), + t.doc(t.collapsedToggleList(t.p('A1'), t.p('A1'))), + ) + + t.applyCommand( + createToggleCollapsedCommand({ collapsed: false }), + t.doc(t.collapsedToggleList(t.p('A1'), t.p('A1'))), + t.doc(t.expandedToggleList(t.p('A1'), t.p('A1'))), + ) + + t.applyCommand( + createToggleCollapsedCommand({ collapsed: false }), + t.doc(t.expandedToggleList(t.p('A1'), t.p('A1'))), + t.doc(t.expandedToggleList(t.p('A1'), t.p('A1'))), + ) + }) + + it('can skip non-collapsed node', () => { + t.applyCommand( + createToggleCollapsedCommand(), + t.doc(t.expandedToggleList(t.p('A1'))), + t.doc(t.expandedToggleList(t.p('A1'))), + ) + + t.applyCommand( + createToggleCollapsedCommand(), + t.doc(t.expandedToggleList(t.p('A1'), t.orderedList(t.p('B1')))), + t.doc(t.collapsedToggleList(t.p('A1'), t.orderedList(t.p('B1')))), + ) + }) +}) diff --git a/packages/editor/src/core/extensions/flat-list/commands/toggle-collapsed.ts b/packages/editor/src/core/extensions/flat-list/commands/toggle-collapsed.ts new file mode 100644 index 00000000000..d1e9ae4924f --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/commands/toggle-collapsed.ts @@ -0,0 +1,60 @@ +import { type Command } from "prosemirror-state"; + +import { isListNode } from "../utils/is-list-node"; + +import { setSafeSelection } from "./set-safe-selection"; +import { ProsemirrorNode, ListAttributes } from "prosemirror-flat-list"; + +/** + * @public + */ +export interface ToggleCollapsedOptions { + /** + * If this value exists, the command will set the `collapsed` attribute to + * this value instead of toggle it. + */ + collapsed?: boolean; + + /** + * An optional function to accept a list node and return whether or not this + * node can toggle its `collapsed` attribute. + */ + isToggleable?: (node: ProsemirrorNode) => boolean; +} + +/** + * Return a command function that toggle the `collapsed` attribute of the list node. + * + * @public @group Commands + */ +export function createToggleCollapsedCommand({ + collapsed = undefined, + isToggleable = defaultIsToggleable, +}: ToggleCollapsedOptions = {}): Command { + const toggleCollapsed: Command = (state, dispatch) => { + const { $from } = state.selection; + + for (let depth = $from.depth; depth >= 0; depth--) { + const node = $from.node(depth); + if (isListNode(node) && isToggleable(node)) { + if (dispatch) { + const pos = $from.before(depth); + const attrs = node.attrs as ListAttributes; + const tr = state.tr; + tr.setNodeAttribute(pos, "collapsed", collapsed ?? !attrs.collapsed); + dispatch(setSafeSelection(tr)); + } + return true; + } + } + return false; + }; + + return toggleCollapsed; +} + +function defaultIsToggleable(node: ProsemirrorNode): boolean { + const attrs = node.attrs as ListAttributes; + + return attrs.kind === "toggle" && node.childCount >= 2 && !isListNode(node.firstChild); +} diff --git a/packages/editor/src/core/extensions/flat-list/commands/toggle-list.spec.ts b/packages/editor/src/core/extensions/flat-list/commands/toggle-list.spec.ts new file mode 100644 index 00000000000..e008d2eedf9 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/commands/toggle-list.spec.ts @@ -0,0 +1,46 @@ +import { describe, it } from 'vitest' + +import { setupTestingEditor } from '../../test/setup-editor' + +import { createToggleListCommand } from './toggle-list' + +describe('toggleList', () => { + const t = setupTestingEditor() + const { doc, p, orderedList, bulletList, uncheckedTaskList } = t + const toggleList = createToggleListCommand({ kind: 'ordered' }) + + it('can toggle list', () => { + const doc1 = doc(p('P1'), p('P2')) + const doc2 = doc(orderedList(p('P1')), p('P2')) + + t.applyCommand(toggleList, doc1, doc2) + t.applyCommand(toggleList, doc2, doc1) + }) + + it('can toggle list with multiple selected paragraphs', () => { + const doc1 = doc(p('P1'), p('P2'), p('P3'), p('P4')) + const doc2 = doc( + p('P1'), + orderedList(p('P2')), + orderedList(p('P3')), + p('P4'), + ) + + t.applyCommand(toggleList, doc1, doc2) + t.applyCommand(toggleList, doc2, doc1) + }) + + it('can toggle a list to another kind', () => { + const toggleBullet = createToggleListCommand({ kind: 'bullet' }) + const toggleTask = createToggleListCommand({ kind: 'task' }) + + const doc1 = doc(p('P1'), p('P2')) + const doc2 = doc(uncheckedTaskList(p('P1')), p('P2')) + const doc3 = doc(bulletList(p('P1')), p('P2')) + + t.applyCommand(toggleTask, doc1, doc2) + t.applyCommand(toggleBullet, doc2, doc3) + t.applyCommand(toggleTask, doc3, doc2) + t.applyCommand(toggleTask, doc2, doc1) + }) +}) diff --git a/packages/editor/src/core/extensions/flat-list/commands/toggle-list.ts b/packages/editor/src/core/extensions/flat-list/commands/toggle-list.ts new file mode 100644 index 00000000000..a8b35e5dbd2 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/commands/toggle-list.ts @@ -0,0 +1,26 @@ +import { chainCommands } from "prosemirror-commands"; +import { type Command } from "prosemirror-state"; + +import { createUnwrapListCommand } from "./unwrap-list"; +import { createWrapInListCommand } from "./wrap-in-list"; +import { ListAttributes } from "prosemirror-flat-list"; + +/** + * Returns a command function that wraps the selection in a list with the given + * type and attributes, or change the list kind if the selection is already in + * another kind of list, or unwrap the selected list if otherwise. + * + * @public + */ +export function createToggleListCommand( + /** + * The list node attributes to toggle. + * + * @public + */ + attrs: T +): Command { + const unwrapList = createUnwrapListCommand({ kind: attrs.kind }); + const wrapInList = createWrapInListCommand(attrs); + return chainCommands(unwrapList, wrapInList); +} diff --git a/packages/editor/src/core/extensions/flat-list/commands/unwrap-list.spec.ts b/packages/editor/src/core/extensions/flat-list/commands/unwrap-list.spec.ts new file mode 100644 index 00000000000..d8caee99406 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/commands/unwrap-list.spec.ts @@ -0,0 +1,77 @@ +import { NodeSelection } from 'prosemirror-state' +import { describe, expect, it } from 'vitest' + +import { setupTestingEditor } from '../../test/setup-editor' +import { type ListAttributes } from '../types' + +import { createUnwrapListCommand } from './unwrap-list' + +describe('unwrapList', () => { + const t = setupTestingEditor() + const { doc, p, bulletList, orderedList, checkedTaskList } = t + const unwrapList = createUnwrapListCommand() + + it('can unwrap a list node selection', () => { + const doc1 = doc(bulletList(p('P1'), p('P2'))) + const doc2 = doc(p('P1'), p('P2')) + + t.add(doc1) + const selection = new NodeSelection(t.view.state.doc.resolve(0)) + expect(selection.node.type.name).toEqual('list') + t.view.dispatch(t.view.state.tr.setSelection(selection)) + + expect(t.dispatchCommand(unwrapList)).toEqual(true) + expect(t.editor.state).toEqualRemirrorState(doc2) + }) + + it('can unwrap a list node selection in a nested list', () => { + const doc1 = doc(orderedList(checkedTaskList(p('P1'), p('P2')))) + const doc2 = doc(orderedList(p('P1'), p('P2'))) + + t.add(doc1) + const selection = new NodeSelection(t.view.state.doc.resolve(1)) + expect(selection.node.type.name).toEqual('list') + expect((selection.node.attrs as ListAttributes).kind).toEqual('task') + t.view.dispatch(t.view.state.tr.setSelection(selection)) + + expect(t.dispatchCommand(unwrapList)).toEqual(true) + expect(t.editor.state).toEqualRemirrorState(doc2) + }) + + it('can unwrap a paragraph inside a list node', () => { + const doc1 = doc(orderedList(p('P1'))) + const doc2 = doc(p('P1')) + t.applyCommand(unwrapList, doc1, doc2) + }) + + it('can unwrap all paragraphs inside a list node event only part of them are selected', () => { + const doc1 = doc(orderedList(p('P1'), p('P2'), p('P3'))) + const doc2 = doc(p('P1'), p('P2'), p('P3')) + t.applyCommand(unwrapList, doc1, doc2) + }) + + it('can unwrap all paragraphs inside a list node', () => { + const doc1 = doc(orderedList(p('P1'), p('P2'), p('P3'))) + const doc2 = doc(p('P1'), p('P2'), p('P3')) + t.applyCommand(unwrapList, doc1, doc2) + }) + + it('can unwrap multiple lists', () => { + const doc1 = doc( + p('P1'), + orderedList(p('P2')), + orderedList(p('P3')), + orderedList(p('P4'), p('P5')), + orderedList(p('P6')), + ) + const doc2 = doc( + p('P1'), + p('P2'), + p('P3'), + p('P4'), + p('P5'), + orderedList(p('P6')), + ) + t.applyCommand(unwrapList, doc1, doc2) + }) +}) diff --git a/packages/editor/src/core/extensions/flat-list/commands/unwrap-list.ts b/packages/editor/src/core/extensions/flat-list/commands/unwrap-list.ts new file mode 100644 index 00000000000..92820f076a5 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/commands/unwrap-list.ts @@ -0,0 +1,86 @@ +import { type NodeRange } from "prosemirror-model"; +import { type Command } from "prosemirror-state"; + +import { isListNode } from "../utils/is-list-node"; +import { isNodeSelection } from "../utils/is-node-selection"; +import { safeLiftFromTo } from "../utils/safe-lift"; + +import { dedentOutOfList } from "./dedent-list"; +import { ProsemirrorNode, ListAttributes } from "prosemirror-flat-list"; + +/** + * @public + */ +export interface UnwrapListOptions { + /** + * If given, only this kind of list will be unwrap. + */ + kind?: string; +} + +/** + * Returns a command function that unwraps the list around the selection. + * + * @public + */ +export function createUnwrapListCommand(options?: UnwrapListOptions): Command { + const kind = options?.kind; + + const unwrapList: Command = (state, dispatch) => { + const selection = state.selection; + + if (isNodeSelection(selection) && isTargetList(selection.node, kind)) { + if (dispatch) { + const tr = state.tr; + safeLiftFromTo(tr, tr.selection.from + 1, tr.selection.to - 1); + dispatch(tr.scrollIntoView()); + } + return true; + } + + const range = selection.$from.blockRange(selection.$to); + + if (range && isTargetListsRange(range, kind)) { + const tr = state.tr; + if (dedentOutOfList(tr, range)) { + dispatch?.(tr); + return true; + } + } + + if (range && isTargetList(range.parent, kind)) { + if (dispatch) { + const tr = state.tr; + safeLiftFromTo(tr, range.$from.start(range.depth), range.$to.end(range.depth)); + dispatch(tr.scrollIntoView()); + } + return true; + } + + return false; + }; + + return unwrapList; +} + +function isTargetList(node: ProsemirrorNode, kind: string | undefined) { + if (isListNode(node)) { + if (kind) { + return (node.attrs as ListAttributes).kind === kind; + } + return true; + } + return false; +} + +function isTargetListsRange(range: NodeRange, kind: string | undefined): boolean { + const { startIndex, endIndex, parent } = range; + + for (let i = startIndex; i < endIndex; i++) { + if (!isTargetList(parent.child(i), kind)) { + return false; + } + } + + return true; +} diff --git a/packages/editor/src/core/extensions/flat-list/commands/wrap-in-list.spec.ts b/packages/editor/src/core/extensions/flat-list/commands/wrap-in-list.spec.ts new file mode 100644 index 00000000000..563a1d3e2dc --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/commands/wrap-in-list.spec.ts @@ -0,0 +1,163 @@ +import { Selection } from 'prosemirror-state' +import { describe, expect, it } from 'vitest' + +import { setupTestingEditor } from '../../test/setup-editor' + +import { createWrapInListCommand } from './wrap-in-list' + +describe('wrapInList', () => { + const t = setupTestingEditor() + const markdown = t.markdown + + const wrapInBulletList = createWrapInListCommand({ kind: 'bullet' }) + const wrapInOrderedList = createWrapInListCommand({ kind: 'ordered' }) + const wrapInTaskList = createWrapInListCommand({ kind: 'task' }) + + it('can wrap a paragraph node to a list node', () => { + t.applyCommand( + wrapInBulletList, + markdown` + P1 + + P2 + `, + markdown` + P1 + + - P2 + `, + ) + }) + + it('can wrap multiple paragraph nodes to list nodes', () => { + t.applyCommand( + wrapInTaskList, + markdown` + P1 + + P2 + + P3 + `, + markdown` + P1 + + - [ ] P2 + - [ ] P3 + `, + ) + }) + + it('can change the type of an existing list node', () => { + t.applyCommand( + wrapInOrderedList, + markdown` + - P1 + + - P2 + `, + markdown` + - P1 + + 1. P2 + `, + ) + }) + + it('can change the type of multiple existing list nodes', () => { + t.applyCommand( + wrapInTaskList, + markdown` + - P1 + + - P2 + + 1. P3 + `, + markdown` + - P1 + - [ ] P2 + - [ ] P3 + `, + ) + }) + + it('can keep the type of a list node with multiple paragraphs', () => { + t.applyCommand( + wrapInBulletList, + markdown` + - P1 + + P2 + + P3 + + P4 + `, + markdown` + - P1 + + P2 + + P3 + + P4 + `, + ) + }) + + it('can wrap a paragraph inside a list node to a sub-list node', () => { + t.applyCommand( + wrapInBulletList, + markdown` + - P1 + + P2 + + P3 + `, + markdown` + - P1 + + - P2 + + P3 + `, + ) + }) + + it('can wrap multiple paragraphs inside a list node to a sub-list node', () => { + t.applyCommand( + wrapInBulletList, + markdown` + - P1 + + P2 + + P3 + `, + markdown` + - P1 + + - P2 + + - P3 + `, + ) + }) + + it('should handle block node without content', () => { + const doc1 = t.doc(/*0*/ t.p() /*2*/, t.horizontalRule() /*3*/) + const doc2 = t.doc(t.p(), t.bulletList(t.horizontalRule())) + + t.add(doc1) + const view = t.view + const selection = Selection.atEnd(view.state.doc) + expect(selection.from).toBe(2) + view.dispatch(view.state.tr.setSelection(selection)) + + wrapInBulletList(view.state, view.dispatch.bind(view), view) + + expect(view.state.doc).toEqualRemirrorDocument(doc2) + }) +}) diff --git a/packages/editor/src/core/extensions/flat-list/commands/wrap-in-list.ts b/packages/editor/src/core/extensions/flat-list/commands/wrap-in-list.ts new file mode 100644 index 00000000000..c92725d4f27 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/commands/wrap-in-list.ts @@ -0,0 +1,89 @@ +import { NodeRange } from "prosemirror-model"; +import { type Command } from "prosemirror-state"; +import { findWrapping } from "prosemirror-transform"; + +import { getListType } from "../utils/get-list-type"; +import { isListNode } from "../utils/is-list-node"; +import { setNodeAttributes } from "../utils/set-node-attributes"; +import { ListAttributes } from "prosemirror-flat-list"; + +/** + * The list node attributes or a callback function to take the current + * selection block range and return list node attributes. If this callback + * function returns null, the command won't do anything. + * + * @public + */ +export type WrapInListGetAttrs = T | ((range: NodeRange) => T | null); + +/** + * Returns a command function that wraps the selection in a list with the given + * type and attributes. + * + * @public @group Commands + */ +export function createWrapInListCommand( + getAttrs: WrapInListGetAttrs +): Command { + const wrapInList: Command = (state, dispatch): boolean => { + const { $from, $to } = state.selection; + + let range = $from.blockRange($to); + if (!range) { + return false; + } + + if (rangeAllowInlineContent(range) && isListNode(range.parent) && range.depth > 0 && range.startIndex === 0) { + range = new NodeRange($from, $to, range.depth - 1); + } + + const attrs: T | null = typeof getAttrs === "function" ? getAttrs(range) : getAttrs; + if (!attrs) { + return false; + } + + const { parent, startIndex, endIndex, depth } = range; + const tr = state.tr; + const listType = getListType(state.schema); + + for (let i = endIndex - 1; i >= startIndex; i--) { + const node = parent.child(i); + if (isListNode(node)) { + const oldAttrs: T = node.attrs as T; + const newAttrs: T = { ...oldAttrs, ...attrs }; + setNodeAttributes(tr, $from.posAtIndex(i, depth), oldAttrs, newAttrs); + } else { + const beforeNode = $from.posAtIndex(i, depth); + const afterNode = $from.posAtIndex(i + 1, depth); + + let nodeStart = beforeNode + 1; + let nodeEnd = afterNode - 1; + if (nodeStart > nodeEnd) { + [nodeStart, nodeEnd] = [nodeEnd, nodeStart]; + } + + const range = new NodeRange(tr.doc.resolve(nodeStart), tr.doc.resolve(nodeEnd), depth); + + const wrapping = findWrapping(range, listType, attrs); + if (wrapping) { + tr.wrap(range, wrapping); + } + } + } + + dispatch?.(tr); + return true; + }; + + return wrapInList; +} + +function rangeAllowInlineContent(range: NodeRange): boolean { + const { parent, startIndex, endIndex } = range; + for (let i = startIndex; i < endIndex; i++) { + if (parent.child(i).inlineContent) { + return true; + } + } + return false; +} diff --git a/packages/editor/src/core/extensions/flat-list/flat-heading-list.ts b/packages/editor/src/core/extensions/flat-list/flat-heading-list.ts new file mode 100644 index 00000000000..5185fd9fc7d --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/flat-heading-list.ts @@ -0,0 +1,156 @@ +import { Node } from "@tiptap/core"; +import { + createListSpec, + createListPlugins, + listKeymap, + listInputRules, + ListAttributes, + createWrapInListCommand, + DedentListOptions, + IndentListOptions, + createIndentListCommand, + createDedentListCommand, + enterWithoutLift, +} from "prosemirror-flat-list"; +import { keymap } from "@tiptap/pm/keymap"; +import { inputRules } from "@tiptap/pm/inputrules"; +import { createSplitListCommand } from "./commands/split-list"; + +declare module "@tiptap/core" { + interface Commands { + flatHeadingListComponent: { + createList: (attrs: ListAttributes) => ReturnType; + indentList: (attrs: IndentListOptions) => ReturnType; + dedentList: (attrs: DedentListOptions) => ReturnType; + splitList: () => ReturnType; + createHeadedList: (attrs: ListAttributes & { title: string }) => ReturnType; + }; + } +} + +const { attrs, parseDOM, toDOM, group, definingForContent, definingAsContext } = createListSpec(); +const listKeymapPlugin = keymap(listKeymap); +const listInputRulePlugin = inputRules({ rules: listInputRules }); + +export const FlatHeadingListExtension = Node.create({ + name: "headingList", + content: "heading block*", + group, + definingForContent, + definingAsContext, + addAttributes() { + return attrs; + }, + parseHTML() { + return parseDOM; + }, + renderHTML({ node }) { + return toDOM(node); + }, + addCommands() { + return { + createList: + (attrs: ListAttributes) => + ({ state, view }) => { + const wrapInList = createWrapInListCommand(attrs); + return wrapInList(state, view.dispatch); + }, + indentList: + (attrs: IndentListOptions) => + ({ state, view }) => { + const indentList = createIndentListCommand(attrs); + return indentList(state, view.dispatch); + }, + dedentList: + (attrs: DedentListOptions) => + ({ state, view }) => { + const dedentList = createDedentListCommand(attrs); + return dedentList(state, view.dispatch); + }, + splitList: + () => + ({ state, view }) => { + const splitList = createSplitListCommand(); + return splitList(state, view.dispatch); + }, + createHeadedList: + (attrs: ListAttributes & { title: string }) => + ({ state, chain, commands }) => { + try { + chain() + .focus() + .setHeading({ level: 1 }) + .setTextSelection(state.selection.from - 1) + .run(); + + return commands.createList({ + kind: attrs.kind || "bullet", + order: attrs.order, + checked: attrs.checked, + collapsed: attrs.collapsed, + }); + } catch (error) { + console.error("Error in creating heading list", error); + return false; + } + }, + }; + }, + addKeyboardShortcuts(this) { + return { + Tab: ({ editor }) => { + const { selection } = editor.state; + const { $from } = selection; + if (editor.isActive(this.name)) { + editor.chain().focus().indentList({ from: $from.pos }); + return true; + } + return false; + }, + "Shift-Tab": ({ editor }) => { + const { selection } = editor.state; + const { $from } = selection; + if (editor.isActive(this.name)) { + editor.chain().focus().dedentList({ from: $from.pos }); + return true; + } + return false; + }, + Enter: ({ editor }) => { + if (editor.isActive(this.name)) { + console.log("called"); + editor.chain().focus().splitList(); + return true; + } + return false; + }, + "Shift-Enter": ({ editor }) => { + if (editor.isActive(this.name)) { + return enterWithoutLift(editor.state, editor.view.dispatch); + } + return false; + }, + "Mod-Shift-7": ({ editor }) => { + try { + console.log("asfd"); + return editor.commands.createHeadedList({ title: "a", kind: "bullet" }); + } catch (error) { + console.error("Error in creating heading list", error); + return false; + } + }, + "Mod-Shift-8": ({ editor }) => { + try { + console.log("asfd"); + return editor.commands.createHeadedList({ title: "a", kind: "ordered" }); + } catch (error) { + console.error("Error in creating heading list", error); + return false; + } + }, + }; + }, + addProseMirrorPlugins() { + return [...createListPlugins({ schema: this.editor.schema }), listKeymapPlugin, listInputRulePlugin]; + }, +}); diff --git a/packages/editor/src/core/extensions/flat-list.ts b/packages/editor/src/core/extensions/flat-list/flat-list.ts similarity index 84% rename from packages/editor/src/core/extensions/flat-list.ts rename to packages/editor/src/core/extensions/flat-list/flat-list.ts index dadde55457a..617f38dbf93 100644 --- a/packages/editor/src/core/extensions/flat-list.ts +++ b/packages/editor/src/core/extensions/flat-list/flat-list.ts @@ -10,9 +10,11 @@ import { IndentListOptions, createIndentListCommand, createDedentListCommand, + // createSplitListCommand, } from "prosemirror-flat-list"; import { keymap } from "@tiptap/pm/keymap"; import { inputRules } from "@tiptap/pm/inputrules"; +import { createSplitListCommand } from "./commands/split-list"; declare module "@tiptap/core" { interface Commands { @@ -20,6 +22,7 @@ declare module "@tiptap/core" { createList: (attrs: ListAttributes) => ReturnType; indentList: (attrs: IndentListOptions) => ReturnType; dedentList: (attrs: DedentListOptions) => ReturnType; + splitList: () => ReturnType; // unwrapList: (attrs: UnwrapListOptions) => ReturnType; }; } @@ -64,6 +67,12 @@ export const FlatListExtension = Node.create({ const dedentList = createDedentListCommand(attrs); return dedentList(state, view.dispatch); }, + splitList: + () => + ({ state, view }) => { + const splitList = createSplitListCommand(); + return splitList(state, view.dispatch); + }, }; }, addKeyboardShortcuts(this) { @@ -86,6 +95,13 @@ export const FlatListExtension = Node.create({ } return false; }, + Enter: ({ editor }) => { + if (editor.isActive(this.name)) { + editor.chain().focus().splitList(); + return true; + } + return false; + }, }; }, addProseMirrorPlugins() { diff --git a/packages/editor/src/core/extensions/flat-list/utils/__snapshots__/list-serializer.spec.ts.snap b/packages/editor/src/core/extensions/flat-list/utils/__snapshots__/list-serializer.spec.ts.snap new file mode 100644 index 00000000000..c41210f853f --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/utils/__snapshots__/list-serializer.spec.ts.snap @@ -0,0 +1,175 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`ListDOMSerializer > can serialize list nodes into
    1`] = ` + +
      +
    1. +

      + A +

      +
    2. +
    3. +

      + B +

      +
    4. +
    +
    +`; + +exports[`ListDOMSerializer > can serialize list nodes into
      1`] = ` + +
        +
      • +

        + A +

        +
      • +
      • +

        + B +

        +
      • +
      +
      +`; + +exports[`ListDOMSerializer > can serialize list nodes with different types into a single
        1`] = ` + +
          +
        • +

          + A +

          +
        • +
        • +

          + B +

          +
        • +
        • +

          + C +

          +
        • +
        +
          +
        1. +

          + D +

          +
        2. +
        +
          +
        • +

          + D +

          +
        • +
        • +

          + E +

          +
        • +
        • +

          + D +

          +
        • +
        +
        +`; + +exports[`ListDOMSerializer > can serialize nested list node 1`] = ` + +
          +
        • +

          + A +

          +
            +
          1. +

            + B +

            +
          2. +
          3. +

            + C +

            +
          4. +
          +
        • +
        • +

          + D +

          +
            +
          1. +

            + E +

            +
          2. +
          3. +

            + F +

            +
          4. +
          +
        • +
        +
        +`; diff --git a/packages/editor/src/core/extensions/flat-list/utils/at-textblock-end.ts b/packages/editor/src/core/extensions/flat-list/utils/at-textblock-end.ts new file mode 100644 index 00000000000..5fbc1f1c76b --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/utils/at-textblock-end.ts @@ -0,0 +1,19 @@ +import { type ResolvedPos } from 'prosemirror-model' +import { type EditorState, type TextSelection } from 'prosemirror-state' +import { type EditorView } from 'prosemirror-view' + +// Copied from https://github.com/prosemirror/prosemirror-commands/blob/1.5.0/src/commands.ts#L157 +export function atTextblockEnd( + state: EditorState, + view?: EditorView, +): ResolvedPos | null { + const { $cursor } = state.selection as TextSelection + if ( + !$cursor || + (view + ? !view.endOfTextblock('forward', state) + : $cursor.parentOffset < $cursor.parent.content.size) + ) + return null + return $cursor +} diff --git a/packages/editor/src/core/extensions/flat-list/utils/at-textblock-start.ts b/packages/editor/src/core/extensions/flat-list/utils/at-textblock-start.ts new file mode 100644 index 00000000000..200b9387890 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/utils/at-textblock-start.ts @@ -0,0 +1,17 @@ +import { type ResolvedPos } from 'prosemirror-model' +import { type EditorState, type TextSelection } from 'prosemirror-state' +import { type EditorView } from 'prosemirror-view' + +// Copied from https://github.com/prosemirror/prosemirror-commands/blob/1.5.0/src/commands.ts#L15 +export function atTextblockStart( + state: EditorState, + view?: EditorView, +): ResolvedPos | null { + const { $cursor } = state.selection as TextSelection + if ( + !$cursor || + (view ? !view.endOfTextblock('backward', state) : $cursor.parentOffset > 0) + ) + return null + return $cursor +} diff --git a/packages/editor/src/core/extensions/flat-list/utils/auto-fix-list.spec.ts b/packages/editor/src/core/extensions/flat-list/utils/auto-fix-list.spec.ts new file mode 100644 index 00000000000..530f5da378d --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/utils/auto-fix-list.spec.ts @@ -0,0 +1,50 @@ +import { type Command } from 'prosemirror-state' +import { describe, it } from 'vitest' + +import { setupTestingEditor } from '../../test/setup-editor' + +import { withAutoFixList } from './auto-fix-list' + +describe('autoJoinList', () => { + const t = setupTestingEditor() + + it('should join two lists', () => { + const command: Command = withAutoFixList((state, dispatch) => { + const schema = state.schema + dispatch?.(state.tr.replaceWith(8, 9, schema.text('C'))) + return true + }) + + t.applyCommand( + command, + + t.doc( + /*0*/ + t.bulletList( + /*1*/ + t.p(/*2*/ 'A' /*3*/), + /*4*/ + ), + /*5*/ + t.bulletList( + /*6*/ + t.bulletList( + /*7*/ + t.p(/*8*/ 'B' /*9*/), + /*10*/ + ), + ), + ), + + t.doc( + t.bulletList( + t.p('A'), + t.bulletList( + // + t.p('C'), + ), + ), + ), + ) + }) +}) diff --git a/packages/editor/src/core/extensions/flat-list/utils/auto-fix-list.ts b/packages/editor/src/core/extensions/flat-list/utils/auto-fix-list.ts new file mode 100644 index 00000000000..671a0313f30 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/utils/auto-fix-list.ts @@ -0,0 +1,104 @@ +import { type Transaction } from "prosemirror-state"; +import { canJoin, canSplit } from "prosemirror-transform"; + +import { Node as ProsemirrorNode } from "prosemirror-model"; + +import { isListNode } from "./is-list-node"; +import { patchCommand } from "./patch-command"; + +/** @internal */ +export function* getTransactionRanges(tr: Transaction): Generator { + const ranges: number[] = []; + let i = 0; + + while (true) { + for (; i < tr.mapping.maps.length; i++) { + const map = tr.mapping.maps[i]; + for (let j = 0; j < ranges.length; j++) { + ranges[j] = map.map(ranges[j]); + } + + map.forEach((_oldStart, _oldEnd, newStart, newEnd) => ranges.push(newStart, newEnd)); + } + + yield ranges; + } +} + +/** @internal */ +export function findBoundaries( + positions: number[], + doc: ProsemirrorNode, + prediction: (before: ProsemirrorNode, after: ProsemirrorNode, parent: ProsemirrorNode, index: number) => boolean +): number[] { + const boundaries = new Set(); + const joinable: number[] = []; + + for (const pos of positions) { + const $pos = doc.resolve(pos); + for (let depth = $pos.depth; depth >= 0; depth--) { + const boundary = $pos.before(depth + 1); + if (boundaries.has(boundary)) { + break; + } + boundaries.add(boundary); + + const index = $pos.index(depth); + const parent = $pos.node(depth); + + const before = parent.maybeChild(index - 1); + if (!before) continue; + + const after = parent.maybeChild(index); + if (!after) continue; + + if (prediction(before, after, parent, index)) { + joinable.push(boundary); + } + } + } + + // Sort in the descending order + return joinable.sort((a, b) => b - a); +} + +function isListJoinable(before: ProsemirrorNode, after: ProsemirrorNode): boolean { + return isListNode(before) && isListNode(after) && isListNode(after.firstChild); +} + +function isListSplitable( + before: ProsemirrorNode, + after: ProsemirrorNode, + parent: ProsemirrorNode, + index: number +): boolean { + if (index === 1 && isListNode(parent) && isListNode(before) && !isListNode(after)) { + return true; + } + return false; +} + +function fixList(tr: Transaction): Transaction { + const ranges = getTransactionRanges(tr); + + const joinable = findBoundaries(ranges.next().value, tr.doc, isListJoinable); + + for (const pos of joinable) { + if (canJoin(tr.doc, pos)) { + tr.join(pos); + } + } + + const splitable = findBoundaries(ranges.next().value, tr.doc, isListSplitable); + + for (const pos of splitable) { + if (canSplit(tr.doc, pos)) { + tr.split(pos); + } + } + + return tr; +} + +/** @internal */ +export const withAutoFixList = patchCommand(fixList); diff --git a/packages/editor/src/core/extensions/flat-list/utils/block-boundary.spec.ts b/packages/editor/src/core/extensions/flat-list/utils/block-boundary.spec.ts new file mode 100644 index 00000000000..8e7de4e969a --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/utils/block-boundary.spec.ts @@ -0,0 +1,51 @@ +import { describe, expect, it } from 'vitest' + +import { setupTestingEditor } from '../../test/setup-editor' + +import { atEndBlockBoundary, atStartBlockBoundary } from './block-boundary' + +describe('boundary', () => { + const t = setupTestingEditor() + + const doc = t.doc( + /*0*/ + t.bulletList( + /*1*/ + t.p(/*2*/ 'A1' /*4*/), + /*5*/ + t.p(/*6*/ 'A2' /*8*/), + /*9*/ + ), + /*10*/ + t.bulletList( + /*11*/ + t.bulletList( + /*12*/ + t.p(/*13*/ 'B1' /*15*/), + /*16*/ + ), + ), + ) + + it('atStartBoundary', () => { + expect(atStartBlockBoundary(doc.resolve(14), 3)).toBe(true) + expect(atStartBlockBoundary(doc.resolve(14), 2)).toBe(true) + expect(atStartBlockBoundary(doc.resolve(14), 1)).toBe(true) + expect(atStartBlockBoundary(doc.resolve(14), 0)).toBe(false) + + expect(atStartBlockBoundary(doc.resolve(8), 2)).toBe(true) + expect(atStartBlockBoundary(doc.resolve(8), 1)).toBe(false) + expect(atStartBlockBoundary(doc.resolve(8), 0)).toBe(false) + }) + + it('atEndBoundary', () => { + expect(atEndBlockBoundary(doc.resolve(14), 3)).toBe(true) + expect(atEndBlockBoundary(doc.resolve(14), 2)).toBe(true) + expect(atEndBlockBoundary(doc.resolve(14), 1)).toBe(true) + expect(atEndBlockBoundary(doc.resolve(14), 0)).toBe(true) + + expect(atEndBlockBoundary(doc.resolve(6), 2)).toBe(true) + expect(atEndBlockBoundary(doc.resolve(6), 1)).toBe(true) + expect(atEndBlockBoundary(doc.resolve(6), 0)).toBe(false) + }) +}) diff --git a/packages/editor/src/core/extensions/flat-list/utils/block-boundary.ts b/packages/editor/src/core/extensions/flat-list/utils/block-boundary.ts new file mode 100644 index 00000000000..dcfecb3528f --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/utils/block-boundary.ts @@ -0,0 +1,32 @@ +import { type ResolvedPos } from 'prosemirror-model' + +export function atStartBlockBoundary( + $pos: ResolvedPos, + depth: number, +): boolean { + for (let d = depth; d <= $pos.depth; d++) { + if ($pos.node(d).isTextblock) { + continue + } + + const index = $pos.index(d) + if (index !== 0) { + return false + } + } + return true +} + +export function atEndBlockBoundary($pos: ResolvedPos, depth: number): boolean { + for (let d = depth; d <= $pos.depth; d++) { + if ($pos.node(d).isTextblock) { + continue + } + + const index = $pos.index(d) + if (index !== $pos.node(d).childCount - 1) { + return false + } + } + return true +} diff --git a/packages/editor/src/core/extensions/flat-list/utils/browser.ts b/packages/editor/src/core/extensions/flat-list/utils/browser.ts new file mode 100644 index 00000000000..cff97e2790e --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/utils/browser.ts @@ -0,0 +1,12 @@ +// Copied from https://github.com/prosemirror/prosemirror-view/blob/1.30.1/src/browser.ts + +const nav = typeof navigator != 'undefined' ? navigator : null +const agent = (nav && nav.userAgent) || '' + +const ie_edge = /Edge\/(\d+)/.exec(agent) +const ie_upto10 = /MSIE \d/.exec(agent) +const ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(agent) + +const ie = !!(ie_upto10 || ie_11up || ie_edge) + +export const safari = !ie && !!nav && /Apple Computer/.test(nav.vendor) diff --git a/packages/editor/src/core/extensions/flat-list/utils/create-and-fill.ts b/packages/editor/src/core/extensions/flat-list/utils/create-and-fill.ts new file mode 100644 index 00000000000..725eb166d96 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/utils/create-and-fill.ts @@ -0,0 +1,15 @@ +import { type Attrs, type Fragment, type Mark, type Node as ProsemirrorNode, type NodeType } from "prosemirror-model"; + +export function createAndFill( + type: NodeType, + attrs?: Attrs | null, + content?: Fragment | ProsemirrorNode | readonly ProsemirrorNode[] | null, + marks?: readonly Mark[] +) { + const node = type.createAndFill(attrs, content, marks); + if (!node) { + throw new RangeError(`Failed to create '${type.name}' node`); + } + node.check(); + return node; +} diff --git a/packages/editor/src/core/extensions/flat-list/utils/cut-by-index.ts b/packages/editor/src/core/extensions/flat-list/utils/cut-by-index.ts new file mode 100644 index 00000000000..437baf745e5 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/utils/cut-by-index.ts @@ -0,0 +1,10 @@ +import { type Fragment } from 'prosemirror-model' + +export function cutByIndex( + fragment: Fragment, + from: number, + to: number, +): Fragment { + // @ts-expect-error fragment.cutByIndex is internal API + return fragment.cutByIndex(from, to) +} diff --git a/packages/editor/src/core/extensions/flat-list/utils/get-list-type.ts b/packages/editor/src/core/extensions/flat-list/utils/get-list-type.ts new file mode 100644 index 00000000000..7b51c01b54c --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/utils/get-list-type.ts @@ -0,0 +1,24 @@ +import { flatListGroup } from "prosemirror-flat-list"; +import { type NodeType, type Schema } from "prosemirror-model"; + +/** @internal */ +export function getListType(schema: Schema): NodeType { + let name: string = schema.cached["PROSEMIRROR_FLAT_LIST_LIST_TYPE_NAME"]; + + if (!name) { + for (const type of Object.values(schema.nodes)) { + if ((type.spec.group || "").split(" ").includes(flatListGroup)) { + name = type.name; + break; + } + } + + if (!name) { + throw new TypeError("[prosemirror-flat-list] Unable to find a flat list type in the schema"); + } + + schema.cached["PROSEMIRROR_FLAT_LIST_LIST_TYPE_NAME"] = name; + } + + return schema.nodes[name]; +} diff --git a/packages/editor/src/core/extensions/flat-list/utils/in-collapsed-list.spec.ts b/packages/editor/src/core/extensions/flat-list/utils/in-collapsed-list.spec.ts new file mode 100644 index 00000000000..0e7d4ee3a74 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/utils/in-collapsed-list.spec.ts @@ -0,0 +1,38 @@ +import { describe, expect, it } from 'vitest' + +import { setupTestingEditor } from '../../test/setup-editor' + +import { inCollapsedList } from './in-collapsed-list' + +describe('inCollapsedList', () => { + const t = setupTestingEditor() + + it('returns false in a normal paragraph', () => { + t.add(t.doc(t.p('Hello world'))) + expect(inCollapsedList(t.view.state.selection.$from)).toBe(false) + }) + + it('returns true in a collapsed list node', () => { + t.add( + t.doc( + t.collapsedToggleList( + t.p('Visible content'), + t.p('Hidden content'), + ), + ), + ) + expect(inCollapsedList(t.view.state.selection.$from)).toBe(true) + }) + + it('returns false in a expanded list node', () => { + t.add( + t.doc( + t.expandedToggleList( + t.p('Visible content'), + t.p('Visible content'), + ), + ), + ) + expect(inCollapsedList(t.view.state.selection.$from)).toBe(false) + }) +}) diff --git a/packages/editor/src/core/extensions/flat-list/utils/in-collapsed-list.ts b/packages/editor/src/core/extensions/flat-list/utils/in-collapsed-list.ts new file mode 100644 index 00000000000..053d3525c05 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/utils/in-collapsed-list.ts @@ -0,0 +1,17 @@ +import { type ResolvedPos } from "prosemirror-model"; + +import { isListNode } from "./is-list-node"; +import { ListAttributes } from "prosemirror-flat-list"; + +export function inCollapsedList($pos: ResolvedPos): boolean { + for (let depth = $pos.depth; depth >= 0; depth--) { + const node = $pos.node(depth); + if (isListNode(node)) { + const attrs = node.attrs as ListAttributes; + if (attrs.collapsed) { + return true; + } + } + } + return false; +} diff --git a/packages/editor/src/core/extensions/flat-list/utils/is-block-node-selection.ts b/packages/editor/src/core/extensions/flat-list/utils/is-block-node-selection.ts new file mode 100644 index 00000000000..837e3580489 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/utils/is-block-node-selection.ts @@ -0,0 +1,9 @@ +import { type NodeSelection, type Selection } from 'prosemirror-state' + +import { isNodeSelection } from './is-node-selection' + +export function isBlockNodeSelection( + selection: Selection, +): selection is NodeSelection { + return isNodeSelection(selection) && selection.node.type.isBlock +} diff --git a/packages/editor/src/core/extensions/flat-list/utils/is-collapsed-list-node.ts b/packages/editor/src/core/extensions/flat-list/utils/is-collapsed-list-node.ts new file mode 100644 index 00000000000..93c6c1a71e2 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/utils/is-collapsed-list-node.ts @@ -0,0 +1,9 @@ +import { ProsemirrorNode, ListAttributes } from "prosemirror-flat-list"; +import { isListNode } from "./is-list-node"; + +/** + * @internal + */ +export function isCollapsedListNode(node: ProsemirrorNode): boolean { + return !!(isListNode(node) && (node.attrs as ListAttributes).collapsed); +} diff --git a/packages/editor/src/core/extensions/flat-list/utils/is-list-node.ts b/packages/editor/src/core/extensions/flat-list/utils/is-list-node.ts new file mode 100644 index 00000000000..de7649ce488 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/utils/is-list-node.ts @@ -0,0 +1,9 @@ +import { type Node as ProsemirrorNode } from 'prosemirror-model' + +import { isListType } from './is-list-type' + +/** @public */ +export function isListNode(node: ProsemirrorNode | null | undefined): boolean { + if (!node) return false + return isListType(node.type) +} diff --git a/packages/editor/src/core/extensions/flat-list/utils/is-list-type.ts b/packages/editor/src/core/extensions/flat-list/utils/is-list-type.ts new file mode 100644 index 00000000000..f6b0272d7b2 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/utils/is-list-type.ts @@ -0,0 +1,8 @@ +import { type NodeType } from 'prosemirror-model' + +import { getListType } from './get-list-type' + +/** @public */ +export function isListType(type: NodeType): boolean { + return getListType(type.schema) === type +} diff --git a/packages/editor/src/core/extensions/flat-list/utils/is-node-selection.ts b/packages/editor/src/core/extensions/flat-list/utils/is-node-selection.ts new file mode 100644 index 00000000000..d6c697d59ea --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/utils/is-node-selection.ts @@ -0,0 +1,7 @@ +import { type NodeSelection, type Selection } from 'prosemirror-state' + +export function isNodeSelection( + selection: Selection, +): selection is NodeSelection { + return Boolean((selection as NodeSelection).node) +} diff --git a/packages/editor/src/core/extensions/flat-list/utils/is-text-selection.ts b/packages/editor/src/core/extensions/flat-list/utils/is-text-selection.ts new file mode 100644 index 00000000000..f6b4232d6f8 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/utils/is-text-selection.ts @@ -0,0 +1,5 @@ +import { TextSelection } from 'prosemirror-state' + +export function isTextSelection(value?: unknown): value is TextSelection { + return Boolean(value && value instanceof TextSelection) +} diff --git a/packages/editor/src/core/extensions/flat-list/utils/list-range.ts b/packages/editor/src/core/extensions/flat-list/utils/list-range.ts new file mode 100644 index 00000000000..bec4418f2d1 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/utils/list-range.ts @@ -0,0 +1,47 @@ +import { NodeRange, type ResolvedPos } from 'prosemirror-model' + +import { isListNode } from './is-list-node' + +/** + * Returns a minimal block range that includes the given two positions and + * represents one or multiple sibling list nodes. + * + * @public + */ +export function findListsRange( + $from: ResolvedPos, + $to: ResolvedPos = $from, +): NodeRange | null { + if ($to.pos < $from.pos) { + return findListsRange($to, $from) + } + + let range = $from.blockRange($to) + + while (range) { + if (isListsRange(range)) { + return range + } + + if (range.depth <= 0) { + break + } + + range = new NodeRange($from, $to, range.depth - 1) + } + + return null +} + +/** @internal */ +export function isListsRange(range: NodeRange): boolean { + const { startIndex, endIndex, parent } = range + + for (let i = startIndex; i < endIndex; i++) { + if (!isListNode(parent.child(i))) { + return false + } + } + + return true +} diff --git a/packages/editor/src/core/extensions/flat-list/utils/list-serializer.spec.ts b/packages/editor/src/core/extensions/flat-list/utils/list-serializer.spec.ts new file mode 100644 index 00000000000..d2285a29b67 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/utils/list-serializer.spec.ts @@ -0,0 +1,90 @@ +import { describe, expect, it } from 'vitest' + +import { setupTestingEditor } from '../../test/setup-editor' + +import { ListDOMSerializer } from './list-serializer' + +describe('ListDOMSerializer', () => { + const { + add, + doc, + p, + bulletList, + orderedList, + uncheckedTaskList: taskList, + expandedToggleList: toggleList, + schema, + } = setupTestingEditor() + + let editor: ReturnType + + it('can serialize list nodes into
          ', () => { + editor = add(doc(bulletList(p('A')), bulletList(p('B')))) + + const serializer = ListDOMSerializer.fromSchema(schema) + + const serialized = serializer.serializeFragment(editor.state.doc.content) + expect(serialized.querySelectorAll('ul').length).toBe(1) + expect(serialized.querySelectorAll('ol').length).toBe(0) + expect(serialized.querySelectorAll('ul > li').length).toBe(2) + expect(serialized).toMatchSnapshot() + }) + + it('can serialize list nodes into
            ', () => { + editor = add(doc(orderedList(p('A')), orderedList(p('B')))) + + const serializer = ListDOMSerializer.fromSchema(schema) + + const serialized = serializer.serializeFragment(editor.state.doc.content) + + expect(serialized.querySelectorAll('ul').length).toBe(0) + expect(serialized.querySelectorAll('ol').length).toBe(1) + expect(serialized.querySelectorAll('ol > li').length).toBe(2) + expect(serialized).toMatchSnapshot() + }) + + it('can serialize list nodes with different types into a single
              ', () => { + editor = add( + doc( + bulletList(p('A')), + taskList(p('B')), + toggleList(p('C')), + + orderedList(p('D')), + + bulletList(p('D')), + taskList(p('E')), + toggleList(p('D')), + ), + ) + + const serializer = ListDOMSerializer.fromSchema(schema) + + const serialized = serializer.serializeFragment(editor.state.doc.content) + + expect(serialized.querySelectorAll('ul').length).toBe(2) + expect(serialized.querySelectorAll('ol').length).toBe(1) + expect(serialized.querySelectorAll('ul > li').length).toBe(6) + expect(serialized.querySelectorAll('ol > li').length).toBe(1) + expect(serialized).toMatchSnapshot() + }) + + it('can serialize nested list node ', () => { + editor = add( + doc( + bulletList(p('A'), orderedList(p('B')), orderedList(p('C'))), + bulletList(p('D'), orderedList(p('E')), orderedList(p('F'))), + ), + ) + + const serializer = ListDOMSerializer.fromSchema(schema) + + const serialized = serializer.serializeFragment(editor.state.doc.content) + + expect(serialized.querySelectorAll('ul').length).toBe(1) + expect(serialized.querySelectorAll('ol').length).toBe(2) + expect(serialized.querySelectorAll('ul > li').length).toBe(2) + expect(serialized.querySelectorAll('ol > li').length).toBe(4) + expect(serialized).toMatchSnapshot() + }) +}) diff --git a/packages/editor/src/core/extensions/flat-list/utils/list-serializer.ts b/packages/editor/src/core/extensions/flat-list/utils/list-serializer.ts new file mode 100644 index 00000000000..253023f49d7 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/utils/list-serializer.ts @@ -0,0 +1,68 @@ +import { listToDOM } from "prosemirror-flat-list"; +import { + type DOMOutputSpec, + DOMSerializer, + type Fragment, + type Node as ProsemirrorNode, + type Schema, +} from "prosemirror-model"; + +/** + * A custom DOM serializer class that can serialize flat list nodes into native + * HTML list elements (i.e. `
                ` and `
                  `). + * + * @public @group Plugins + */ +export class ListDOMSerializer extends DOMSerializer { + static nodesFromSchema(schema: Schema): { + [node: string]: (node: ProsemirrorNode) => DOMOutputSpec; + } { + const nodes = DOMSerializer.nodesFromSchema(schema); + return { + ...nodes, + list: (node) => listToDOM({ node, nativeList: true, getMarkers: () => null }), + }; + } + + static fromSchema(schema: Schema): ListDOMSerializer { + return ( + (schema.cached.listDomSerializer as ListDOMSerializer) || + (schema.cached.listDomSerializer = new ListDOMSerializer( + this.nodesFromSchema(schema), + this.marksFromSchema(schema) + )) + ); + } + + serializeFragment( + fragment: Fragment, + options?: { document?: Document }, + target?: HTMLElement | DocumentFragment + ): HTMLElement | DocumentFragment { + const dom = super.serializeFragment(fragment, options, target); + return joinListElements(dom); + } +} + +/** + * Merge adjacent
                    elements or adjacent
                      elements into a single list element. + * + * @public + */ +export function joinListElements(parent: T): T { + for (let i = 0; i < parent.childNodes.length; i++) { + const child = parent.children.item(i); + if (!child) continue; + + if (child.tagName === "UL" || child.tagName === "OL") { + let next: Element | null = null; + + while (((next = child.nextElementSibling), next?.tagName === child.tagName)) { + child.append(...Array.from(next.children)); + next.remove(); + } + } + joinListElements(child); + } + return parent; +} diff --git a/packages/editor/src/core/extensions/flat-list/utils/map-pos.ts b/packages/editor/src/core/extensions/flat-list/utils/map-pos.ts new file mode 100644 index 00000000000..93309f3a0a0 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/utils/map-pos.ts @@ -0,0 +1,16 @@ +import { type Transaction } from 'prosemirror-state' + +export function mapPos(tr: Transaction, pos: number) { + let nextStepIndex = tr.steps.length + + const getPos = (): number => { + if (nextStepIndex < tr.steps.length) { + const mapping = tr.mapping.slice(nextStepIndex) + nextStepIndex = tr.steps.length + pos = mapping.map(pos) + } + return pos + } + + return getPos +} diff --git a/packages/editor/src/core/extensions/flat-list/utils/max-open.ts b/packages/editor/src/core/extensions/flat-list/utils/max-open.ts new file mode 100644 index 00000000000..e5db021fe69 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/utils/max-open.ts @@ -0,0 +1,33 @@ +import { type Fragment, type Node as ProsemirrorNode } from 'prosemirror-model' + +// Copy from https://github.com/prosemirror/prosemirror-model/blob/1.19.0/src/replace.ts#L88-L95 +export function maxOpenStart( + fragment: Fragment | ProsemirrorNode, + openIsolating = true, +) { + let openStart = 0 + for ( + let n = fragment.firstChild; + n && !n.isLeaf && (openIsolating || !n.type.spec.isolating); + n = n.firstChild + ) { + openStart++ + } + return openStart +} + +// Copy from https://github.com/prosemirror/prosemirror-model/blob/1.19.0/src/replace.ts#L88-L95 +export function maxOpenEnd( + fragment: Fragment | ProsemirrorNode, + openIsolating = true, +) { + let openEnd = 0 + for ( + let n = fragment.lastChild; + n && !n.isLeaf && (openIsolating || !n.type.spec.isolating); + n = n.lastChild + ) { + openEnd++ + } + return openEnd +} diff --git a/packages/editor/src/core/extensions/flat-list/utils/parse-integer.spec.ts b/packages/editor/src/core/extensions/flat-list/utils/parse-integer.spec.ts new file mode 100644 index 00000000000..e1723ea2b95 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/utils/parse-integer.spec.ts @@ -0,0 +1,44 @@ +import { describe, it, expect } from 'vitest' + +import { parseInteger } from './parse-integer' + +describe('parseInteger', () => { + it('can parse integer string', () => { + expect(parseInteger('0')).toBe(0) + expect(parseInteger('1')).toBe(1) + expect(parseInteger('-10')).toBe(-10) + expect(parseInteger('999')).toBe(999) + }) + + it('can parse float string', () => { + expect(parseInteger('1.1')).toBe(1) + expect(parseInteger('1.9')).toBe(1) + expect(parseInteger('-1.1')).toBe(-1) + expect(parseInteger('-1.9')).toBe(-1) + expect(parseInteger('-999.9')).toBe(-999) + }) + + it('can parse non number', () => { + expect(parseInteger('Hello')).toBe(null) + // @ts-expect-error: wrong parameter type + expect(parseInteger(true)).toBe(null) + // @ts-expect-error: wrong parameter type + expect(parseInteger(false)).toBe(null) + // @ts-expect-error: wrong parameter type + expect(parseInteger({ object: 'object' })).toBe(null) + // @ts-expect-error: wrong parameter type + expect(parseInteger(Number.NaN)).toBe(null) + }) + + it('can parse number', () => { + // @ts-expect-error: wrong parameter type + expect(parseInteger(-1)).toBe(-1) + // @ts-expect-error: wrong parameter type + expect(parseInteger(100.1)).toBe(100) + }) + + it('can handle null and undefined', () => { + expect(parseInteger(null)).toBe(null) + expect(parseInteger(undefined)).toBe(null) + }) +}) diff --git a/packages/editor/src/core/extensions/flat-list/utils/parse-integer.ts b/packages/editor/src/core/extensions/flat-list/utils/parse-integer.ts new file mode 100644 index 00000000000..8a90f881cb8 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/utils/parse-integer.ts @@ -0,0 +1,7 @@ +/** @internal */ +export function parseInteger(attr: string | null | undefined): number | null { + if (attr == null) return null + const int = Number.parseInt(attr, 10) + if (Number.isInteger(int)) return int + return null +} diff --git a/packages/editor/src/core/extensions/flat-list/utils/patch-command.ts b/packages/editor/src/core/extensions/flat-list/utils/patch-command.ts new file mode 100644 index 00000000000..77dfb222a45 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/utils/patch-command.ts @@ -0,0 +1,15 @@ +import { type Command, type Transaction } from 'prosemirror-state' + +export function patchCommand(patch: (tr: Transaction) => Transaction) { + const withPatch = (command: Command): Command => { + const patchedCommand: Command = (state, dispatch, view) => { + return command( + state, + dispatch ? (tr: Transaction) => dispatch(patch(tr)) : undefined, + view, + ) + } + return patchedCommand + } + return withPatch +} diff --git a/packages/editor/src/core/extensions/flat-list/utils/range-to-string.ts b/packages/editor/src/core/extensions/flat-list/utils/range-to-string.ts new file mode 100644 index 00000000000..9d019c293e2 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/utils/range-to-string.ts @@ -0,0 +1,13 @@ +import { type NodeRange } from 'prosemirror-model' + +import { cutByIndex } from './cut-by-index' + +/** + * Return a debugging string that describes this range. + * + * @internal + */ +export function rangeToString(range: NodeRange): string { + const { parent, startIndex, endIndex } = range + return cutByIndex(parent.content, startIndex, endIndex).toString() +} diff --git a/packages/editor/src/core/extensions/flat-list/utils/safe-lift.ts b/packages/editor/src/core/extensions/flat-list/utils/safe-lift.ts new file mode 100644 index 00000000000..328076bd46f --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/utils/safe-lift.ts @@ -0,0 +1,24 @@ +import { type NodeRange } from 'prosemirror-model' +import { type Transaction } from 'prosemirror-state' +import { liftTarget } from 'prosemirror-transform' + +export function safeLift(tr: Transaction, range: NodeRange): boolean { + const target = liftTarget(range) + if (target == null) { + return false + } + tr.lift(range, target) + return true +} + +export function safeLiftFromTo( + tr: Transaction, + from: number, + to: number, +): boolean { + const $from = tr.doc.resolve(from) + const $to = tr.doc.resolve(to) + const range = $from.blockRange($to) + if (!range) return false + return safeLift(tr, range) +} diff --git a/packages/editor/src/core/extensions/flat-list/utils/set-list-attributes.ts b/packages/editor/src/core/extensions/flat-list/utils/set-list-attributes.ts new file mode 100644 index 00000000000..a2b03745fd2 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/utils/set-list-attributes.ts @@ -0,0 +1,21 @@ +import { type Transaction } from "prosemirror-state"; + +import { isListNode } from "./is-list-node"; +import { setNodeAttributes } from "./set-node-attributes"; +import { ListAttributes } from "prosemirror-flat-list"; + +export function setListAttributes( + tr: Transaction, + pos: number, + attrs: T +): boolean { + const $pos = tr.doc.resolve(pos); + const node = $pos.nodeAfter; + + if (node && isListNode(node)) { + const oldAttrs: T = node.attrs as T; + const newAttrs: T = { ...oldAttrs, ...attrs }; + return setNodeAttributes(tr, pos, oldAttrs, newAttrs); + } + return false; +} diff --git a/packages/editor/src/core/extensions/flat-list/utils/set-node-attributes.ts b/packages/editor/src/core/extensions/flat-list/utils/set-node-attributes.ts new file mode 100644 index 00000000000..561cc74cd09 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/utils/set-node-attributes.ts @@ -0,0 +1,18 @@ +import { type Attrs } from 'prosemirror-model' +import { type Transaction } from 'prosemirror-state' + +export function setNodeAttributes( + tr: Transaction, + pos: number, + oldAttrs: Attrs, + newAttrs: Attrs, +): boolean { + let needUpdate = false + for (const key of Object.keys(newAttrs)) { + if (newAttrs[key] !== oldAttrs[key]) { + tr.setNodeAttribute(pos, key, newAttrs[key]) + needUpdate = true + } + } + return needUpdate +} diff --git a/packages/editor/src/core/extensions/flat-list/utils/split-boundary.spec.ts b/packages/editor/src/core/extensions/flat-list/utils/split-boundary.spec.ts new file mode 100644 index 00000000000..a06ef80fb41 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/utils/split-boundary.spec.ts @@ -0,0 +1,370 @@ +import { type TaggedProsemirrorNode } from 'jest-remirror' +import { describe, expect, it } from 'vitest' + +import { setupTestingEditor } from '../../test/setup-editor' + +import { splitBoundary } from './split-boundary' + +describe('splitBoundary', () => { + const t = setupTestingEditor() + + const check = ({ + before, + after, + pos, + depth, + }: { + before: TaggedProsemirrorNode + after?: TaggedProsemirrorNode + pos?: number + depth: number + }) => { + const tr = t.add(before).tr + pos = pos ?? tr.selection.$from.pos + splitBoundary(tr, pos, depth) + expect(tr.doc).toEqualRemirrorDocument(after ?? before) + } + + it.each([ + { + pos: 4, + depth: 1, + before: t.doc( + /*0*/ + t.bulletList( + /*1*/ + t.bulletList( + /*2*/ + t.p(/*3*/ 'AB' /*5*/), + /*6*/ + t.p(/*7*/ 'CD' /*9*/), + /*10*/ + ), + /*11*/ + ), + /*12*/ + ), + after: t.doc( + t.bulletList( + t.bulletList( + // + t.p('A'), + t.p('B'), + t.p('CD'), + ), + ), + ), + }, + + { + pos: 4, + depth: 2, + before: t.doc( + /*0*/ + t.bulletList( + /*1*/ + t.bulletList( + /*2*/ + t.p(/*3*/ 'AB' /*5*/), + /*6*/ + t.p(/*7*/ 'CD' /*9*/), + /*10*/ + ), + /*11*/ + ), + /*12*/ + ), + after: t.doc( + t.bulletList( + t.bulletList( + // + t.p('A'), + ), + t.bulletList( + // + t.p('B'), + t.p('CD'), + ), + ), + ), + }, + + { + pos: 4, + depth: 3, + before: t.doc( + /*0*/ + t.bulletList( + /*1*/ + t.bulletList( + /*2*/ + t.p(/*3*/ 'AB' /*5*/), + /*6*/ + t.p(/*7*/ 'CD' /*9*/), + /*10*/ + ), + /*11*/ + ), + /*12*/ + ), + after: t.doc( + t.bulletList( + t.bulletList( + // + t.p('A'), + ), + ), + t.bulletList( + t.bulletList( + // + t.p('B'), + t.p('CD'), + ), + ), + ), + }, + + { + pos: 3, + depth: 1, + before: t.doc( + /*0*/ + t.bulletList( + /*1*/ + t.bulletList( + /*2*/ + t.p(/*3*/ 'AB' /*5*/), + /*6*/ + t.p(/*7*/ 'CD' /*9*/), + /*10*/ + ), + /*11*/ + ), + /*12*/ + ), + after: t.doc( + t.bulletList( + t.bulletList( + // + t.p('AB'), + t.p('CD'), + ), + ), + ), + }, + + { + pos: 3, + depth: 2, + before: t.doc( + /*0*/ + t.bulletList( + /*1*/ + t.bulletList( + /*2*/ + t.p(/*3*/ 'AB' /*5*/), + /*6*/ + t.p(/*7*/ 'CD' /*9*/), + /*10*/ + ), + /*11*/ + ), + /*12*/ + ), + after: t.doc( + t.bulletList( + t.bulletList( + // + t.p('AB'), + t.p('CD'), + ), + ), + ), + }, + + { + pos: 5, + depth: 1, + before: t.doc( + /*0*/ + t.bulletList( + /*1*/ + t.bulletList( + /*2*/ + t.p(/*3*/ 'AB' /*5*/), + /*6*/ + t.p(/*7*/ 'CD' /*9*/), + /*10*/ + ), + /*11*/ + ), + /*12*/ + ), + after: t.doc( + t.bulletList( + t.bulletList( + // + t.p('AB'), + t.p('CD'), + ), + ), + ), + }, + + { + pos: 5, + depth: 2, + before: t.doc( + /*0*/ + t.bulletList( + /*1*/ + t.bulletList( + /*2*/ + t.p(/*3*/ 'AB' /*5*/), + /*6*/ + t.p(/*7*/ 'CD' /*9*/), + /*10*/ + ), + /*11*/ + ), + /*12*/ + ), + after: t.doc( + t.bulletList( + t.bulletList( + // + t.p('AB'), + ), + t.bulletList( + // + t.p('CD'), + ), + ), + ), + }, + + { + pos: 6, + depth: 1, + before: t.doc( + /*0*/ + t.bulletList( + /*1*/ + t.bulletList( + /*2*/ + t.p(/*3*/ 'AB' /*5*/), + /*6*/ + t.p(/*7*/ 'CD' /*9*/), + /*10*/ + ), + /*11*/ + ), + /*12*/ + ), + after: t.doc( + t.bulletList( + t.bulletList( + // + t.p('AB'), + ), + t.bulletList( + // + t.p('CD'), + ), + ), + ), + }, + + { + pos: 6, + depth: 2, + before: t.doc( + /*0*/ + t.bulletList( + /*1*/ + t.bulletList( + /*2*/ + t.p(/*3*/ 'AB' /*5*/), + /*6*/ + t.p(/*7*/ 'CD' /*9*/), + /*10*/ + ), + /*11*/ + ), + /*12*/ + ), + after: t.doc( + t.bulletList( + t.bulletList( + // + t.p('AB'), + ), + ), + t.bulletList( + t.bulletList( + // + t.p('CD'), + ), + ), + ), + }, + + { + pos: 2, + depth: 1, + before: t.doc( + /*0*/ + t.bulletList( + /*1*/ + t.bulletList( + /*2*/ + t.p(/*3*/ 'AB' /*5*/), + /*6*/ + t.p(/*7*/ 'CD' /*9*/), + /*10*/ + ), + /*11*/ + ), + /*12*/ + ), + after: t.doc( + t.bulletList( + t.bulletList( + // + t.p('AB'), + t.p('CD'), + ), + ), + ), + }, + + { + pos: 2, + depth: 2, + before: t.doc( + /*0*/ + t.bulletList( + /*1*/ + t.bulletList( + /*2*/ + t.p(/*3*/ 'AB' /*5*/), + /*6*/ + t.p(/*7*/ 'CD' /*9*/), + /*10*/ + ), + /*11*/ + ), + /*12*/ + ), + after: t.doc( + t.bulletList( + t.bulletList( + // + t.p('AB'), + t.p('CD'), + ), + ), + ), + }, + ])('can split node %#', check) +}) diff --git a/packages/editor/src/core/extensions/flat-list/utils/split-boundary.ts b/packages/editor/src/core/extensions/flat-list/utils/split-boundary.ts new file mode 100644 index 00000000000..28619ed6e4a --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/utils/split-boundary.ts @@ -0,0 +1,34 @@ +import { type Transaction } from 'prosemirror-state' + +/** + * Split the node at the given position, and optionally, if `depth` is greater + * than one, any number of nodes above that. Unlike `tr.split`, this function + * will skip if the position is already at the boundary of a node. This will + * avoid creating empty nodes during the split. + */ +export function splitBoundary(tr: Transaction, pos: number, depth = 1): void { + if (depth <= 0) return + + const $pos = tr.doc.resolve(pos) + const parent = $pos.node() + + if (parent.isTextblock) { + const parentOffset = $pos.parentOffset + if (parentOffset == 0) { + return splitBoundary(tr, pos - 1, depth - 1) + } else if (parentOffset >= parent.content.size) { + return splitBoundary(tr, pos + 1, depth - 1) + } else { + tr.split(pos, depth) + } + } else { + const index = $pos.index($pos.depth) + if (index === 0) { + return splitBoundary(tr, pos - 1, depth - 1) + } else if (index === $pos.node().childCount) { + return splitBoundary(tr, pos + 1, depth - 1) + } else { + tr.split(pos, depth) + } + } +} diff --git a/packages/editor/src/core/extensions/flat-list/utils/unwrap-list-slice.ts b/packages/editor/src/core/extensions/flat-list/utils/unwrap-list-slice.ts new file mode 100644 index 00000000000..d4141b2eb7f --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/utils/unwrap-list-slice.ts @@ -0,0 +1,26 @@ +import { Slice } from 'prosemirror-model' + +import { isListNode } from './is-list-node' + +/** + * Reduce the open depth of a slice if it only contains a single list node. When + * copying some text from a deep nested list node, we don't want to paste the + * entire list structure into the document later. + * + * @internal + */ +export function unwrapListSlice(slice: Slice): Slice { + while ( + slice.openStart >= 2 && + slice.openEnd >= 2 && + slice.content.childCount === 1 && + isListNode(slice.content.child(0)) + ) { + slice = new Slice( + slice.content.child(0).content, + slice.openStart - 1, + slice.openEnd - 1, + ) + } + return slice +} diff --git a/packages/editor/src/core/extensions/flat-list/utils/zoom-in-range.ts b/packages/editor/src/core/extensions/flat-list/utils/zoom-in-range.ts new file mode 100644 index 00000000000..a50748e6e26 --- /dev/null +++ b/packages/editor/src/core/extensions/flat-list/utils/zoom-in-range.ts @@ -0,0 +1,19 @@ +import { type NodeRange } from 'prosemirror-model' + +/** + * Returns a deeper block range if possible + */ +export function zoomInRange(range: NodeRange): NodeRange | null { + const { $from, $to, depth, start, end } = range + const doc = $from.doc + + const deeper = ( + $from.pos > start ? $from : doc.resolve(start + 1) + ).blockRange($to.pos < end ? $to : doc.resolve(end - 1)) + + if (deeper && deeper.depth > depth) { + return deeper + } + + return null +} diff --git a/packages/editor/src/core/types/editor.ts b/packages/editor/src/core/types/editor.ts index 53aae1f265d..5aeb0e3ea0e 100644 --- a/packages/editor/src/core/types/editor.ts +++ b/packages/editor/src/core/types/editor.ts @@ -39,7 +39,11 @@ export type TEditorCommands = | "text-color" | "background-color" | "text-align" - | "callout"; + | "callout" + | "flat-toggle-list" + | "flat-bulleted-list" + | "flat-numbered-list" + | "flat-check-list"; export type TCommandExtraProps = { image: { diff --git a/packages/editor/src/index.ts b/packages/editor/src/index.ts index c74f7eb0e4b..8275c3ebdbc 100644 --- a/packages/editor/src/index.ts +++ b/packages/editor/src/index.ts @@ -1,7 +1,5 @@ // styles -// import "./styles/tailwind.css"; import "prosemirror-flat-list/style.css"; -; import "./styles/variables.css"; import "./styles/editor.css"; import "./styles/table.css"; diff --git a/packages/editor/src/styles/editor.css b/packages/editor/src/styles/editor.css index e77b4047ba7..4eec300c976 100644 --- a/packages/editor/src/styles/editor.css +++ b/packages/editor/src/styles/editor.css @@ -1,7 +1,3 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - .ProseMirror { position: relative; word-wrap: break-word; @@ -474,11 +470,129 @@ ul[data-type="taskList"] ul[data-type="taskList"] { top: 7px; } -.prosemirror-flat-list[data-list-kind="ordered"]::before { - position: absolute; - left: -17px; - top: 6px; - /* right: 100%; */ - font-variant-numeric: tabular-nums; - content: counter(prosemirror-flat-list-counter, decimal) ". "; +/* .prosemirror-flat-list[data-list-kind="ordered"]::before { */ +/* position: absolute; */ +/* top: 6px; */ +/* font-variant-numeric: tabular-nums; */ +/* content: counter(prosemirror-flat-list-counter, decimal) ". "; */ +/* } */ + +.prosemirror-flat-list[data-list-kind="ordered"] { + &::before { + position: absolute; + right: 100%; + top: 6px; + font-variant-numeric: tabular-nums; + content: counter(prosemirror-flat-list-counter, decimal) ". "; + } + + /* Second level - lower-alpha */ + & > .list-content > & { + &::before { + content: counter(prosemirror-flat-list-counter, lower-alpha) ". "; + } + + /* Third level - lower-roman */ + & > .list-content > & { + &::before { + content: counter(prosemirror-flat-list-counter, lower-roman) ". "; + } + + /* Fourth level - upper-alpha */ + & > .list-content > & { + &::before { + content: counter(prosemirror-flat-list-counter, upper-alpha) ". "; + } + + /* Start repeat pattern */ + & > .list-content > & { + &::before { + content: counter(prosemirror-flat-list-counter, decimal) ". "; + } + + /* Continue pattern */ + & > .list-content > & { + &::before { + content: counter(prosemirror-flat-list-counter, lower-alpha) ". "; + } + + & > .list-content > & { + &::before { + content: counter(prosemirror-flat-list-counter, lower-roman) ". "; + } + + & > .list-content > & { + &::before { + content: counter(prosemirror-flat-list-counter, upper-alpha) ". "; + } + + /* Pattern repeats again */ + & > .list-content > & { + &::before { + content: counter(prosemirror-flat-list-counter, decimal) ". "; + } + } + } + } + } + } + } + } + } +} + +.prosemirror-flat-list[data-list-kind="bullet"] { + list-style: none; + + &::before { + position: absolute; + right: 100%; + top: 6px; + } + + /* First level - disc */ + &::before { + content: "•"; + } + + /* Second level - circle */ + & > .list-content > & { + &::before { + content: "○"; + } + + /* Third level - square */ + & > .list-content > & { + &::before { + content: "▪"; + } + + /* Start repeat pattern */ + & > .list-content > & { + &::before { + content: "•"; + } + + /* Continue pattern */ + & > .list-content > & { + &::before { + content: "○"; + } + + & > .list-content > & { + &::before { + content: "▪"; + } + + /* Pattern repeats again */ + & > .list-content > & { + &::before { + content: "•"; + } + } + } + } + } + } + } } diff --git a/packages/editor/src/styles/tailwind.css b/packages/editor/src/styles/tailwind.css index b5c61c95671..b139bdba566 100644 --- a/packages/editor/src/styles/tailwind.css +++ b/packages/editor/src/styles/tailwind.css @@ -1,3 +1,3 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; +/* @tailwind base; */ +/* @tailwind components; */ +/* @tailwind utilities; */ diff --git a/yarn.lock b/yarn.lock index b82a0ea4a68..5f8437947c9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -831,6 +831,20 @@ dependencies: "@babel/helper-plugin-utils" "^7.25.9" +"@babel/plugin-transform-react-jsx-self@^7.24.7": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz#c0b6cae9c1b73967f7f9eb2fca9536ba2fad2858" + integrity sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-transform-react-jsx-source@^7.24.7": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.9.tgz#4c6b8daa520b5f155b5fb55547d7c9fa91417503" + integrity sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-transform-regenerator@^7.25.9": version "7.25.9" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz#03a8a4670d6cebae95305ac6defac81ece77740b" @@ -1301,6 +1315,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz#d1bc06aedb6936b3b6d313bf809a5a40387d2b7f" integrity sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA== +"@esbuild/aix-ppc64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f" + integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ== + "@esbuild/aix-ppc64@0.23.1": version "0.23.1" resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz#51299374de171dbd80bb7d838e1cfce9af36f353" @@ -1311,6 +1330,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz#7ad65a36cfdb7e0d429c353e00f680d737c2aed4" integrity sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA== +"@esbuild/android-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052" + integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A== + "@esbuild/android-arm64@0.23.1": version "0.23.1" resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz#58565291a1fe548638adb9c584237449e5e14018" @@ -1321,6 +1345,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.19.12.tgz#b0c26536f37776162ca8bde25e42040c203f2824" integrity sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w== +"@esbuild/android-arm@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28" + integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg== + "@esbuild/android-arm@0.23.1": version "0.23.1" resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.23.1.tgz#5eb8c652d4c82a2421e3395b808e6d9c42c862ee" @@ -1331,6 +1360,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.19.12.tgz#cb13e2211282012194d89bf3bfe7721273473b3d" integrity sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew== +"@esbuild/android-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e" + integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA== + "@esbuild/android-x64@0.23.1": version "0.23.1" resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.23.1.tgz#ae19d665d2f06f0f48a6ac9a224b3f672e65d517" @@ -1341,6 +1375,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz#cbee41e988020d4b516e9d9e44dd29200996275e" integrity sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g== +"@esbuild/darwin-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a" + integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ== + "@esbuild/darwin-arm64@0.23.1": version "0.23.1" resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz#05b17f91a87e557b468a9c75e9d85ab10c121b16" @@ -1351,6 +1390,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz#e37d9633246d52aecf491ee916ece709f9d5f4cd" integrity sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A== +"@esbuild/darwin-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz#c13838fa57372839abdddc91d71542ceea2e1e22" + integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw== + "@esbuild/darwin-x64@0.23.1": version "0.23.1" resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz#c58353b982f4e04f0d022284b8ba2733f5ff0931" @@ -1361,6 +1405,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz#1ee4d8b682ed363b08af74d1ea2b2b4dbba76487" integrity sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA== +"@esbuild/freebsd-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e" + integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g== + "@esbuild/freebsd-arm64@0.23.1": version "0.23.1" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz#f9220dc65f80f03635e1ef96cfad5da1f446f3bc" @@ -1371,6 +1420,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz#37a693553d42ff77cd7126764b535fb6cc28a11c" integrity sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg== +"@esbuild/freebsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261" + integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ== + "@esbuild/freebsd-x64@0.23.1": version "0.23.1" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz#69bd8511fa013b59f0226d1609ac43f7ce489730" @@ -1381,6 +1435,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz#be9b145985ec6c57470e0e051d887b09dddb2d4b" integrity sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA== +"@esbuild/linux-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b" + integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q== + "@esbuild/linux-arm64@0.23.1": version "0.23.1" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz#8050af6d51ddb388c75653ef9871f5ccd8f12383" @@ -1391,6 +1450,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz#207ecd982a8db95f7b5279207d0ff2331acf5eef" integrity sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w== +"@esbuild/linux-arm@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9" + integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA== + "@esbuild/linux-arm@0.23.1": version "0.23.1" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz#ecaabd1c23b701070484990db9a82f382f99e771" @@ -1401,6 +1465,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz#d0d86b5ca1562523dc284a6723293a52d5860601" integrity sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA== +"@esbuild/linux-ia32@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2" + integrity sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg== + "@esbuild/linux-ia32@0.23.1": version "0.23.1" resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz#3ed2273214178109741c09bd0687098a0243b333" @@ -1411,6 +1480,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz#9a37f87fec4b8408e682b528391fa22afd952299" integrity sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA== +"@esbuild/linux-loong64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df" + integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg== + "@esbuild/linux-loong64@0.23.1": version "0.23.1" resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz#a0fdf440b5485c81b0fbb316b08933d217f5d3ac" @@ -1421,6 +1495,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz#4ddebd4e6eeba20b509d8e74c8e30d8ace0b89ec" integrity sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w== +"@esbuild/linux-mips64el@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe" + integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg== + "@esbuild/linux-mips64el@0.23.1": version "0.23.1" resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz#e11a2806346db8375b18f5e104c5a9d4e81807f6" @@ -1431,6 +1510,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz#adb67dadb73656849f63cd522f5ecb351dd8dee8" integrity sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg== +"@esbuild/linux-ppc64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4" + integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w== + "@esbuild/linux-ppc64@0.23.1": version "0.23.1" resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz#06a2744c5eaf562b1a90937855b4d6cf7c75ec96" @@ -1441,6 +1525,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz#11bc0698bf0a2abf8727f1c7ace2112612c15adf" integrity sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg== +"@esbuild/linux-riscv64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc" + integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA== + "@esbuild/linux-riscv64@0.23.1": version "0.23.1" resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz#65b46a2892fc0d1af4ba342af3fe0fa4a8fe08e7" @@ -1451,6 +1540,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz#e86fb8ffba7c5c92ba91fc3b27ed5a70196c3cc8" integrity sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg== +"@esbuild/linux-s390x@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de" + integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A== + "@esbuild/linux-s390x@0.23.1": version "0.23.1" resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz#e71ea18c70c3f604e241d16e4e5ab193a9785d6f" @@ -1461,6 +1555,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz#5f37cfdc705aea687dfe5dfbec086a05acfe9c78" integrity sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg== +"@esbuild/linux-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0" + integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ== + "@esbuild/linux-x64@0.23.1": version "0.23.1" resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz#d47f97391e80690d4dfe811a2e7d6927ad9eed24" @@ -1471,6 +1570,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz#29da566a75324e0d0dd7e47519ba2f7ef168657b" integrity sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA== +"@esbuild/netbsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047" + integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg== + "@esbuild/netbsd-x64@0.23.1": version "0.23.1" resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz#44e743c9778d57a8ace4b72f3c6b839a3b74a653" @@ -1486,6 +1590,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz#306c0acbdb5a99c95be98bdd1d47c916e7dc3ff0" integrity sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw== +"@esbuild/openbsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70" + integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow== + "@esbuild/openbsd-x64@0.23.1": version "0.23.1" resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz#2e58ae511bacf67d19f9f2dcd9e8c5a93f00c273" @@ -1496,6 +1605,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz#0933eaab9af8b9b2c930236f62aae3fc593faf30" integrity sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA== +"@esbuild/sunos-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b" + integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg== + "@esbuild/sunos-x64@0.23.1": version "0.23.1" resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz#adb022b959d18d3389ac70769cef5a03d3abd403" @@ -1506,6 +1620,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz#773bdbaa1971b36db2f6560088639ccd1e6773ae" integrity sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A== +"@esbuild/win32-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d" + integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A== + "@esbuild/win32-arm64@0.23.1": version "0.23.1" resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz#84906f50c212b72ec360f48461d43202f4c8b9a2" @@ -1516,6 +1635,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz#000516cad06354cc84a73f0943a4aa690ef6fd67" integrity sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ== +"@esbuild/win32-ia32@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b" + integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA== + "@esbuild/win32-ia32@0.23.1": version "0.23.1" resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz#5e3eacc515820ff729e90d0cb463183128e82fac" @@ -1526,6 +1650,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz#c57c8afbb4054a3ab8317591a0b7320360b444ae" integrity sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA== +"@esbuild/win32-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c" + integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw== + "@esbuild/win32-x64@0.23.1": version "0.23.1" resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz#81fd50d11e2c32b2d6241470e3185b70c7b30699" @@ -1543,6 +1672,25 @@ resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.11.1.tgz#a547badfc719eb3e5f4b556325e542fbe9d7a18f" integrity sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q== +"@eslint-community/regexpp@^4.12.1": + version "4.12.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" + integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== + +"@eslint/config-array@^0.18.0": + version "0.18.0" + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.18.0.tgz#37d8fe656e0d5e3dbaea7758ea56540867fd074d" + integrity sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw== + dependencies: + "@eslint/object-schema" "^2.1.4" + debug "^4.3.1" + minimatch "^3.1.2" + +"@eslint/core@^0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.7.0.tgz#a1bb4b6a4e742a5ff1894b7ee76fbf884ec72bd3" + integrity sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw== + "@eslint/eslintrc@^2.1.4": version "2.1.4" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" @@ -1558,11 +1706,43 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" +"@eslint/eslintrc@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.1.0.tgz#dbd3482bfd91efa663cbe7aa1f506839868207b6" + integrity sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^10.0.1" + globals "^14.0.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + "@eslint/js@8.57.1": version "8.57.1" resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.1.tgz#de633db3ec2ef6a3c89e2f19038063e8a122e2c2" integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q== +"@eslint/js@9.14.0", "@eslint/js@^9.9.0": + version "9.14.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.14.0.tgz#2347a871042ebd11a00fd8c2d3d56a265ee6857e" + integrity sha512-pFoEtFWCPyDOl+C6Ift+wC7Ro89otjigCf5vcuWqWgqNSQbRrpjSvdeE6ofLz4dHmyxD5f7gIdGT4+p36L6Twg== + +"@eslint/object-schema@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.4.tgz#9e69f8bb4031e11df79e03db09f9dbbae1740843" + integrity sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ== + +"@eslint/plugin-kit@^0.2.0": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.2.2.tgz#5eff371953bc13e3f4d88150e2c53959f64f74f6" + integrity sha512-CXtq5nR4Su+2I47WPOlWud98Y5Lv8Kyxp2ukhgFx/eW6Blm18VXJO5WuQylPugRo8nbluoi6GvvxBLqHcvqUUw== + dependencies: + levn "^0.4.1" + "@floating-ui/core@^1.6.0": version "1.6.8" resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.6.8.tgz#aa43561be075815879305965020f492cdb43da12" @@ -1662,6 +1842,19 @@ uuid "^10.0.0" ws "^8.5.0" +"@humanfs/core@^0.19.1": + version "0.19.1" + resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.1.tgz#17c55ca7d426733fe3c561906b8173c336b40a77" + integrity sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA== + +"@humanfs/node@^0.16.6": + version "0.16.6" + resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.6.tgz#ee2a10eaabd1131987bf0488fd9b820174cd765e" + integrity sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw== + dependencies: + "@humanfs/core" "^0.19.1" + "@humanwhocodes/retry" "^0.3.0" + "@humanwhocodes/config-array@^0.13.0": version "0.13.0" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.13.0.tgz#fb907624df3256d04b9aa2df50d7aa97ec648748" @@ -1681,6 +1874,16 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== +"@humanwhocodes/retry@^0.3.0": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.1.tgz#c72a5c76a9fbaf3488e231b13dc52c0da7bab42a" + integrity sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA== + +"@humanwhocodes/retry@^0.4.0": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.1.tgz#9a96ce501bc62df46c4031fbd970e3cc6b10f07b" + integrity sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA== + "@hypnosphi/create-react-context@^0.3.1": version "0.3.1" resolved "https://registry.yarnpkg.com/@hypnosphi/create-react-context/-/create-react-context-0.3.1.tgz#f8bfebdc7665f5d426cba3753e0e9c7d3154d7c6" @@ -2749,6 +2952,15 @@ is-reference "1.2.1" magic-string "^0.30.3" +"@rollup/plugin-inject@^5.0.5": + version "5.0.5" + resolved "https://registry.yarnpkg.com/@rollup/plugin-inject/-/plugin-inject-5.0.5.tgz#616f3a73fe075765f91c5bec90176608bed277a3" + integrity sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg== + dependencies: + "@rollup/pluginutils" "^5.0.1" + estree-walker "^2.0.2" + magic-string "^0.30.3" + "@rollup/pluginutils@^5.0.1": version "5.1.2" resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.1.2.tgz#d3bc9f0fea4fd4086aaac6aa102f3fa587ce8bd9" @@ -2763,81 +2975,171 @@ resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz#1661ff5ea9beb362795304cb916049aba7ac9c54" integrity sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA== +"@rollup/rollup-android-arm-eabi@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.25.0.tgz#3e7eda4c0c1de6d2415343002d742ff95e38dca7" + integrity sha512-CC/ZqFZwlAIbU1wUPisHyV/XRc5RydFrNLtgl3dGYskdwPZdt4HERtKm50a/+DtTlKeCq9IXFEWR+P6blwjqBA== + "@rollup/rollup-android-arm64@4.24.0": version "4.24.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz#2ffaa91f1b55a0082b8a722525741aadcbd3971e" integrity sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA== +"@rollup/rollup-android-arm64@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.25.0.tgz#04f679231acf7284f1f8a1f7250d0e0944865ba8" + integrity sha512-/Y76tmLGUJqVBXXCfVS8Q8FJqYGhgH4wl4qTA24E9v/IJM0XvJCGQVSW1QZ4J+VURO9h8YCa28sTFacZXwK7Rg== + "@rollup/rollup-darwin-arm64@4.24.0": version "4.24.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz#627007221b24b8cc3063703eee0b9177edf49c1f" integrity sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA== +"@rollup/rollup-darwin-arm64@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.25.0.tgz#ecea723041621747d0772af93b54752edf26467a" + integrity sha512-YVT6L3UrKTlC0FpCZd0MGA7NVdp7YNaEqkENbWQ7AOVOqd/7VzyHpgIpc1mIaxRAo1ZsJRH45fq8j4N63I/vvg== + "@rollup/rollup-darwin-x64@4.24.0": version "4.24.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz#0605506142b9e796c370d59c5984ae95b9758724" integrity sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ== +"@rollup/rollup-darwin-x64@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.25.0.tgz#28e6e0687092f31e20982fc104779d48c643fc21" + integrity sha512-ZRL+gexs3+ZmmWmGKEU43Bdn67kWnMeWXLFhcVv5Un8FQcx38yulHBA7XR2+KQdYIOtD0yZDWBCudmfj6lQJoA== + +"@rollup/rollup-freebsd-arm64@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.25.0.tgz#99e9173b8aef3d1ef086983da70413988206e530" + integrity sha512-xpEIXhiP27EAylEpreCozozsxWQ2TJbOLSivGfXhU4G1TBVEYtUPi2pOZBnvGXHyOdLAUUhPnJzH3ah5cqF01g== + +"@rollup/rollup-freebsd-x64@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.25.0.tgz#f3a1ef941f8d3c6b2b036484c69a7b2d3d9ebbd7" + integrity sha512-sC5FsmZGlJv5dOcURrsnIK7ngc3Kirnx3as2XU9uER+zjfyqIjdcMVgzy4cOawhsssqzoAX19qmxgJ8a14Qrqw== + "@rollup/rollup-linux-arm-gnueabihf@4.24.0": version "4.24.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz#62dfd196d4b10c0c2db833897164d2d319ee0cbb" integrity sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA== +"@rollup/rollup-linux-arm-gnueabihf@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.25.0.tgz#9ba6adcc33f26f2a0c6ee658f0bbda4de8da2f75" + integrity sha512-uD/dbLSs1BEPzg564TpRAQ/YvTnCds2XxyOndAO8nJhaQcqQGFgv/DAVko/ZHap3boCvxnzYMa3mTkV/B/3SWA== + "@rollup/rollup-linux-arm-musleabihf@4.24.0": version "4.24.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz#53ce72aeb982f1f34b58b380baafaf6a240fddb3" integrity sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw== +"@rollup/rollup-linux-arm-musleabihf@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.25.0.tgz#62f2426fa9016ec884f4fa779d7b62d5ba02a41a" + integrity sha512-ZVt/XkrDlQWegDWrwyC3l0OfAF7yeJUF4fq5RMS07YM72BlSfn2fQQ6lPyBNjt+YbczMguPiJoCfaQC2dnflpQ== + "@rollup/rollup-linux-arm64-gnu@4.24.0": version "4.24.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz#1632990f62a75c74f43e4b14ab3597d7ed416496" integrity sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA== +"@rollup/rollup-linux-arm64-gnu@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.25.0.tgz#f98ec111a231d35e0c6d3404e3d80f67f9d5b9f8" + integrity sha512-qboZ+T0gHAW2kkSDPHxu7quaFaaBlynODXpBVnPxUgvWYaE84xgCKAPEYE+fSMd3Zv5PyFZR+L0tCdYCMAtG0A== + "@rollup/rollup-linux-arm64-musl@4.24.0": version "4.24.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz#8c03a996efb41e257b414b2e0560b7a21f2d9065" integrity sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw== +"@rollup/rollup-linux-arm64-musl@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.25.0.tgz#4b36ffb8359f959f2c29afd187603c53368b6723" + integrity sha512-ndWTSEmAaKr88dBuogGH2NZaxe7u2rDoArsejNslugHZ+r44NfWiwjzizVS1nUOHo+n1Z6qV3X60rqE/HlISgw== + "@rollup/rollup-linux-powerpc64le-gnu@4.24.0": version "4.24.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz#5b98729628d5bcc8f7f37b58b04d6845f85c7b5d" integrity sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw== +"@rollup/rollup-linux-powerpc64le-gnu@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.25.0.tgz#52f4b39e6783505d168a745b79d86474fde71680" + integrity sha512-BVSQvVa2v5hKwJSy6X7W1fjDex6yZnNKy3Kx1JGimccHft6HV0THTwNtC2zawtNXKUu+S5CjXslilYdKBAadzA== + "@rollup/rollup-linux-riscv64-gnu@4.24.0": version "4.24.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz#48e42e41f4cabf3573cfefcb448599c512e22983" integrity sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg== +"@rollup/rollup-linux-riscv64-gnu@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.25.0.tgz#49195be7e6a7d68d482b12461e2ea914e31ff977" + integrity sha512-G4hTREQrIdeV0PE2JruzI+vXdRnaK1pg64hemHq2v5fhv8C7WjVaeXc9P5i4Q5UC06d/L+zA0mszYIKl+wY8oA== + "@rollup/rollup-linux-s390x-gnu@4.24.0": version "4.24.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz#e0b4f9a966872cb7d3e21b9e412a4b7efd7f0b58" integrity sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g== +"@rollup/rollup-linux-s390x-gnu@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.25.0.tgz#4b8d50a205eac7b46cdcb9c50d4a6ae5994c02e0" + integrity sha512-9T/w0kQ+upxdkFL9zPVB6zy9vWW1deA3g8IauJxojN4bnz5FwSsUAD034KpXIVX5j5p/rn6XqumBMxfRkcHapQ== + "@rollup/rollup-linux-x64-gnu@4.24.0": version "4.24.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz#78144741993100f47bd3da72fce215e077ae036b" integrity sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A== +"@rollup/rollup-linux-x64-gnu@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.25.0.tgz#dfcceebc5ccac7fc2db19471996026258c81b55f" + integrity sha512-ThcnU0EcMDn+J4B9LD++OgBYxZusuA7iemIIiz5yzEcFg04VZFzdFjuwPdlURmYPZw+fgVrFzj4CA64jSTG4Ig== + "@rollup/rollup-linux-x64-musl@4.24.0": version "4.24.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz#d9fe32971883cd1bd858336bd33a1c3ca6146127" integrity sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ== +"@rollup/rollup-linux-x64-musl@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.25.0.tgz#192f78bad8429711d63a31dc0a7d3312e2df850e" + integrity sha512-zx71aY2oQxGxAT1JShfhNG79PnjYhMC6voAjzpu/xmMjDnKNf6Nl/xv7YaB/9SIa9jDYf8RBPWEnjcdlhlv1rQ== + "@rollup/rollup-win32-arm64-msvc@4.24.0": version "4.24.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz#71fa3ea369316db703a909c790743972e98afae5" integrity sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ== +"@rollup/rollup-win32-arm64-msvc@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.25.0.tgz#f4ec076579634f780b4e5896ae7f59f3e38e0c60" + integrity sha512-JT8tcjNocMs4CylWY/CxVLnv8e1lE7ff1fi6kbGocWwxDq9pj30IJ28Peb+Y8yiPNSF28oad42ApJB8oUkwGww== + "@rollup/rollup-win32-ia32-msvc@4.24.0": version "4.24.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz#653f5989a60658e17d7576a3996deb3902e342e2" integrity sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ== +"@rollup/rollup-win32-ia32-msvc@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.25.0.tgz#5458eab1929827e4f805cefb90bd09ecf7eeed2b" + integrity sha512-dRLjLsO3dNOfSN6tjyVlG+Msm4IiZnGkuZ7G5NmpzwF9oOc582FZG05+UdfTbz5Jd4buK/wMb6UeHFhG18+OEg== + "@rollup/rollup-win32-x64-msvc@4.24.0": version "4.24.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz#0574d7e87b44ee8511d08cc7f914bcb802b70818" integrity sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw== +"@rollup/rollup-win32-x64-msvc@4.25.0": + version "4.25.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.25.0.tgz#93415e7e707e4b156d77c5950b983b58f4bc33f3" + integrity sha512-/RqrIFtLB926frMhZD0a5oDa4eFIbyNEwLLloMTEjmqfwZWXywwVVOVmwTsuyhC9HKkVEZcOOi+KV4U9wmOdlg== + "@rtsao/scc@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" @@ -3132,10 +3434,10 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz#719df7fb41766bc143369eaa0dd56d8dc87c9958" integrity sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg== -"@storybook/addon-actions@8.3.6": - version "8.3.6" - resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-8.3.6.tgz#80c5dbfc2278d72dc461a954bb729165ee1dfecb" - integrity sha512-nOqgl0WoZK2KwjaABaXMoIgrIHOQl9inOzJvqQau0HOtsvnXGXYfJXYnpjZenoZDoZXKbUDl0U2haDFx2a2fJw== +"@storybook/addon-actions@8.3.5": + version "8.3.5" + resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-8.3.5.tgz#03fdb891114439ed47cb7df6ef21826530449db7" + integrity sha512-t8D5oo+4XfD+F8091wLa2y/CDd/W2lExCeol5Vm1tp5saO+u6f2/d7iykLhTowWV84Uohi3D073uFeyTAlGebg== dependencies: "@storybook/global" "^5.0.0" "@types/uuid" "^9.0.1" @@ -3788,16 +4090,12 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-task-list/-/extension-task-list-2.9.1.tgz#e0ca3ec1379dbc39a98070c650d3759df85db794" integrity sha512-vmUkclPi02iVf+uu74iyUp5xGNib0Gxs73DJ1z+a7CzjuLRqqCa/KEde95CR0Y//DaK/Csz4DOSUyTfLCMvpWg== -<<<<<<< HEAD -"@tiptap/extension-text-style@^2.9.1": -======= -"@tiptap/extension-text-align@^2.8.0": +"@tiptap/extension-text-align@^2.9.1": version "2.9.1" resolved "https://registry.yarnpkg.com/@tiptap/extension-text-align/-/extension-text-align-2.9.1.tgz#5f7920a16c95b283c961cf1e22357bdc355c1626" integrity sha512-oUp0XnwJpAImcOVV68vsY2CpkHpRZ3gzWfIRTuy+aYitQim3xDKis/qfWQUWZsANp9/TZ0VyjtkZxNMwOfcu1g== -"@tiptap/extension-text-style@^2.7.1", "@tiptap/extension-text-style@^2.9.1": ->>>>>>> preview +"@tiptap/extension-text-style@^2.9.1": version "2.9.1" resolved "https://registry.yarnpkg.com/@tiptap/extension-text-style/-/extension-text-style-2.9.1.tgz#b9fc9cd8e90747357fbd4cac541a33aaa8b76875" integrity sha512-LAxc0SeeiPiAVBwksczeA7BJSZb6WtVpYhy5Esvy9K0mK5kttB4KxtnXWeQzMIJZQbza65yftGKfQlexf/Y7yg== @@ -3911,7 +4209,7 @@ resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.4.tgz#1a31c3d378850d2778dabb6374d036dcba4ba708" integrity sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw== -"@types/babel__core@^7.18.0": +"@types/babel__core@^7.18.0", "@types/babel__core@^7.20.5": version "7.20.5" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== @@ -3973,6 +4271,11 @@ dependencies: "@types/node" "*" +"@types/cookie@^0.6.0": + version "0.6.0" + resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.6.0.tgz#eac397f28bf1d6ae0ae081363eca2f425bedf0d5" + integrity sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA== + "@types/cors@^2.8.17": version "2.8.17" resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.17.tgz#5d718a5e494a8166f569d986794e49c48b216b2b" @@ -4067,7 +4370,7 @@ resolved "https://registry.yarnpkg.com/@types/escodegen/-/escodegen-0.0.6.tgz#5230a9ce796e042cda6f086dbf19f22ea330659c" integrity sha512-AjwI4MvWx3HAOaZqYsjKWyEObT9lcVV0Y0V8nXo6cXzN8ZiMxVhf6F3d/UNvXVGKrEzL/Dluc5p+y9GkzlTWig== -"@types/estree@*", "@types/estree@1.0.6", "@types/estree@^1.0.0", "@types/estree@^1.0.5": +"@types/estree@*", "@types/estree@1.0.6", "@types/estree@^1.0.0", "@types/estree@^1.0.5", "@types/estree@^1.0.6": version "1.0.6" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== @@ -4140,6 +4443,14 @@ dependencies: "@types/unist" "*" +"@types/hoist-non-react-statics@^3.3.5": + version "3.3.5" + resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz#dab7867ef789d87e2b4b0003c9d65c49cc44a494" + integrity sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg== + dependencies: + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + "@types/html-minifier-terser@^6.0.0": version "6.1.0" resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35" @@ -4150,7 +4461,7 @@ resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f" integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA== -"@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": +"@types/json-schema@^7.0.15", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== @@ -4231,15 +4542,9 @@ "@types/node" "*" "@types/node@*", "@types/node@^22.0.0", "@types/node@^22.5.4": -<<<<<<< HEAD - version "22.7.5" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.7.5.tgz#cfde981727a7ab3611a481510b473ae54442b92b" - integrity sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ== -======= version "22.8.0" resolved "https://registry.yarnpkg.com/@types/node/-/node-22.8.0.tgz#193c6f82f9356ce0e6bba86b59f2ffe06e7e320b" integrity sha512-84rafSBHC/z1i1E3p0cJwKA+CfYDNSXX9WSZBRopjIzLET8oNt6ht2tei4C7izwDeEiLLfdeSVBv1egOH916hg== ->>>>>>> preview dependencies: undici-types "~6.19.8" @@ -4259,15 +4564,9 @@ integrity sha512-DZxSZWXxFfOlx7k7Rv4LAyiMroaxa3Ly/7OOzZO8cBNho0YzAi4qlbrx8W27JGqG57IgR/6J7r+nOJWw6kcvZA== "@types/node@^20.14.9", "@types/node@^20.5.2": -<<<<<<< HEAD - version "20.16.11" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.16.11.tgz#9b544c3e716b1577ac12e70f9145193f32750b33" - integrity sha512-y+cTCACu92FyA5fgQSAI8A1H429g7aSK2HsO7K4XYUWc4dY5IUz55JSDIYT6/VsOLfGy8vmvQYC2hfb0iF16Uw== -======= version "20.17.1" resolved "https://registry.yarnpkg.com/@types/node/-/node-20.17.1.tgz#2b968e060dfb04b7f9550fe3db5f552721c14566" integrity sha512-j2VlPv1NnwPJbaCNv69FO/1z4lId0QmGvpT41YxitRtWlg96g/j8qcv2RKsLKe2F6OJgyXhupN1Xo17b2m139Q== ->>>>>>> preview dependencies: undici-types "~6.19.2" @@ -4329,7 +4628,7 @@ "@types/react" "*" "@types/reactcss" "*" -"@types/react-dom@^18.2.18": +"@types/react-dom@^18.2.18", "@types/react-dom@^18.3.0": version "18.3.1" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.3.1.tgz#1e4654c08a9cdcfb6594c780ac59b55aad42fe07" integrity sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ== @@ -4352,7 +4651,7 @@ "@types/scheduler" "*" csstype "^3.0.2" -"@types/react@^18.3.11": +"@types/react@^18.3.11", "@types/react@^18.3.3": version "18.3.12" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.12.tgz#99419f182ccd69151813b7ee24b792fe08774f60" integrity sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw== @@ -4446,6 +4745,21 @@ resolved "https://registry.yarnpkg.com/@types/zxcvbn/-/zxcvbn-4.4.5.tgz#8ce8623ed7a36e3a76d1c0b539708dfb2e859bc0" integrity sha512-FZJgC5Bxuqg7Rhsm/bx6gAruHHhDQ55r+s0JhDh8CQ16fD7NsJJ+p8YMMQDhSQoIrSmjpqqYWA96oQVMNkjRyA== +"@typescript-eslint/eslint-plugin@8.13.0": + version "8.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.13.0.tgz#650c50b8c795b5d092189f139f6d00535b5b0f3d" + integrity sha512-nQtBLiZYMUPkclSeC3id+x4uVd1SGtHuElTxL++SfP47jR0zfkZBJHc+gL4qPsgTuypz0k8Y2GheaDYn6Gy3rg== + dependencies: + "@eslint-community/regexpp" "^4.10.0" + "@typescript-eslint/scope-manager" "8.13.0" + "@typescript-eslint/type-utils" "8.13.0" + "@typescript-eslint/utils" "8.13.0" + "@typescript-eslint/visitor-keys" "8.13.0" + graphemer "^1.4.0" + ignore "^5.3.1" + natural-compare "^1.4.0" + ts-api-utils "^1.3.0" + "@typescript-eslint/eslint-plugin@^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", "@typescript-eslint/eslint-plugin@^8.6.0": version "8.8.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.8.1.tgz#9364b756d4d78bcbdf6fd3e9345e6924c68ad371" @@ -4477,6 +4791,17 @@ semver "^7.3.7" tsutils "^3.21.0" +"@typescript-eslint/parser@8.13.0": + version "8.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.13.0.tgz#ef76203b7cac515aa3ccc4f7ce5320dd61c46b29" + integrity sha512-w0xp+xGg8u/nONcGw1UXAr6cjCPU1w0XVyBs6Zqaj5eLmxkKQAByTdV/uGgNN5tVvN/kKpoQlP2cL7R+ajZZIQ== + dependencies: + "@typescript-eslint/scope-manager" "8.13.0" + "@typescript-eslint/types" "8.13.0" + "@typescript-eslint/typescript-estree" "8.13.0" + "@typescript-eslint/visitor-keys" "8.13.0" + debug "^4.3.4" + "@typescript-eslint/parser@^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", "@typescript-eslint/parser@^8.6.0": version "8.8.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.8.1.tgz#5952ba2a83bd52024b872f3fdc8ed2d3636073b8" @@ -4496,6 +4821,14 @@ "@typescript-eslint/types" "5.62.0" "@typescript-eslint/visitor-keys" "5.62.0" +"@typescript-eslint/scope-manager@8.13.0": + version "8.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.13.0.tgz#2f4aed0b87d72360e64e4ea194b1fde14a59082e" + integrity sha512-XsGWww0odcUT0gJoBZ1DeulY1+jkaHUciUq4jKNv4cpInbvvrtDoyBH9rE/n2V29wQJPk8iCH1wipra9BhmiMA== + dependencies: + "@typescript-eslint/types" "8.13.0" + "@typescript-eslint/visitor-keys" "8.13.0" + "@typescript-eslint/scope-manager@8.8.1": version "8.8.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.8.1.tgz#b4bea1c0785aaebfe3c4ab059edaea1c4977e7ff" @@ -4514,6 +4847,16 @@ debug "^4.3.4" tsutils "^3.21.0" +"@typescript-eslint/type-utils@8.13.0": + version "8.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.13.0.tgz#8c8fa68490dcb9ae1687ffc7de8fbe23c26417bd" + integrity sha512-Rqnn6xXTR316fP4D2pohZenJnp+NwQ1mo7/JM+J1LWZENSLkJI8ID8QNtlvFeb0HnFSK94D6q0cnMX6SbE5/vA== + dependencies: + "@typescript-eslint/typescript-estree" "8.13.0" + "@typescript-eslint/utils" "8.13.0" + debug "^4.3.4" + ts-api-utils "^1.3.0" + "@typescript-eslint/type-utils@8.8.1": version "8.8.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.8.1.tgz#31f59ec46e93a02b409fb4d406a368a59fad306e" @@ -4529,6 +4872,11 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== +"@typescript-eslint/types@8.13.0": + version "8.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.13.0.tgz#3f35dead2b2491a04339370dcbcd17bbdfc204d8" + integrity sha512-4cyFErJetFLckcThRUFdReWJjVsPCqyBlJTi6IDEpc1GWCIIZRFxVppjWLIMcQhNGhdWJJRYFHpHoDWvMlDzng== + "@typescript-eslint/types@8.8.1": version "8.8.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.8.1.tgz#ebe85e0fa4a8e32a24a56adadf060103bef13bd1" @@ -4547,6 +4895,20 @@ semver "^7.3.7" tsutils "^3.21.0" +"@typescript-eslint/typescript-estree@8.13.0": + version "8.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.13.0.tgz#db8c93dd5437ca3ce417a255fb35ddc3c12c3e95" + integrity sha512-v7SCIGmVsRK2Cy/LTLGN22uea6SaUIlpBcO/gnMGT/7zPtxp90bphcGf4fyrCQl3ZtiBKqVTG32hb668oIYy1g== + dependencies: + "@typescript-eslint/types" "8.13.0" + "@typescript-eslint/visitor-keys" "8.13.0" + debug "^4.3.4" + fast-glob "^3.3.2" + is-glob "^4.0.3" + minimatch "^9.0.4" + semver "^7.6.0" + ts-api-utils "^1.3.0" + "@typescript-eslint/typescript-estree@8.8.1": version "8.8.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.8.1.tgz#34649f4e28d32ee49152193bc7dedc0e78e5d1ec" @@ -4575,6 +4937,16 @@ eslint-scope "^5.1.1" semver "^7.3.7" +"@typescript-eslint/utils@8.13.0": + version "8.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.13.0.tgz#f6d40e8b5053dcaeabbd2e26463857abf27d62c0" + integrity sha512-A1EeYOND6Uv250nybnLZapeXpYMl8tkzYUxqmoKAWnI4sei3ihf2XdZVd+vVOmHGcp3t+P7yRrNsyyiXTvShFQ== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + "@typescript-eslint/scope-manager" "8.13.0" + "@typescript-eslint/types" "8.13.0" + "@typescript-eslint/typescript-estree" "8.13.0" + "@typescript-eslint/utils@8.8.1": version "8.8.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.8.1.tgz#9e29480fbfa264c26946253daa72181f9f053c9d" @@ -4593,6 +4965,14 @@ "@typescript-eslint/types" "5.62.0" eslint-visitor-keys "^3.3.0" +"@typescript-eslint/visitor-keys@8.13.0": + version "8.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.13.0.tgz#e97b0d92b266ef38a1faf40a74da289b66683a5b" + integrity sha512-7N/+lztJqH4Mrf0lb10R/CbI1EaAMMGyF5y0oJvFoAhafwgiRA7TXyd8TFn8FC8k5y2dTsYogg238qavRGNnlw== + dependencies: + "@typescript-eslint/types" "8.13.0" + eslint-visitor-keys "^3.4.3" + "@typescript-eslint/visitor-keys@8.8.1": version "8.8.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.8.1.tgz#0fb1280f381149fc345dfde29f7542ff4e587fc5" @@ -4606,6 +4986,17 @@ resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== +"@vitejs/plugin-react@^4.3.1": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-4.3.3.tgz#28301ac6d7aaf20b73a418ee5c65b05519b4836c" + integrity sha512-NooDe9GpHGqNns1i8XDERg0Vsg5SSYRhRxxyTGogUdkdNt47jal+fbuYi+Yfq6pzRCKXyoPcWisfxE6RIM3GKA== + dependencies: + "@babel/core" "^7.25.2" + "@babel/plugin-transform-react-jsx-self" "^7.24.7" + "@babel/plugin-transform-react-jsx-source" "^7.24.7" + "@types/babel__core" "^7.20.5" + react-refresh "^0.14.2" + "@vitest/expect@2.0.5": version "2.0.5" resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-2.0.5.tgz#f3745a6a2c18acbea4d39f5935e913f40d26fa86" @@ -4839,6 +5230,11 @@ acorn@^8.11.0, acorn@^8.12.1, acorn@^8.4.1, acorn@^8.7.1, acorn@^8.8.1, acorn@^8 resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248" integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== +acorn@^8.14.0: + version "8.14.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" + integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== + agent-base@6: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" @@ -5084,6 +5480,26 @@ arraybuffer.prototype.slice@^1.0.3: is-array-buffer "^3.0.4" is-shared-array-buffer "^1.0.2" +asn1.js@^4.10.1: + version "4.10.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" + integrity sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw== + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +assert@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/assert/-/assert-2.1.0.tgz#6d92a238d05dc02e7427c881fb8be81c8448b2dd" + integrity sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw== + dependencies: + call-bind "^1.0.2" + is-nan "^1.3.2" + object-is "^1.1.5" + object.assign "^4.1.4" + util "^0.12.5" + assertion-error@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-2.0.1.tgz#f641a196b335690b1070bf00b6e7593fec190bf7" @@ -5133,7 +5549,7 @@ autoprefixer@10.4.14: picocolors "^1.0.0" postcss-value-parser "^4.2.0" -autoprefixer@^10.4.14, autoprefixer@^10.4.19: +autoprefixer@^10.4.14, autoprefixer@^10.4.19, autoprefixer@^10.4.20: version "10.4.20" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.20.tgz#5caec14d43976ef42e32dcb4bd62878e96be5b3b" integrity sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g== @@ -5314,6 +5730,16 @@ bluebird@^3.7.2: resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.9: + version "4.12.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + +bn.js@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" + integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== + body-parser@1.20.3: version "1.20.3" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6" @@ -5359,6 +5785,11 @@ braces@^3.0.3, braces@~3.0.2: dependencies: fill-range "^7.1.1" +brorand@^1.0.1, brorand@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== + brotli@^1.3.2: version "1.3.3" resolved "https://registry.yarnpkg.com/brotli/-/brotli-1.3.3.tgz#7365d8cc00f12cf765d2b2c898716bcf4b604d48" @@ -5371,6 +5802,69 @@ browser-assert@^1.2.1: resolved "https://registry.yarnpkg.com/browser-assert/-/browser-assert-1.2.1.tgz#9aaa5a2a8c74685c2ae05bfe46efd606f068c200" integrity sha512-nfulgvOR6S4gt9UKCeGJOuSGBPGiFT6oQ/2UBnvTY/5aQ1PnksW72fhZkM30DzoRRv2WpwZf1vHHEr3mtuXIWQ== +browser-resolve@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-2.0.0.tgz#99b7304cb392f8d73dba741bb2d7da28c6d7842b" + integrity sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ== + dependencies: + resolve "^1.17.0" + +browserify-aes@^1.0.4, browserify-aes@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +browserify-cipher@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +browserify-rsa@^4.0.0, browserify-rsa@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.1.tgz#06e530907fe2949dc21fc3c2e2302e10b1437238" + integrity sha512-YBjSAiTqM04ZVei6sXighu679a3SqWORA3qZTEqZImnlkDIFtKc6pNutpjyZ8RJTjQtuYfeetkxM11GwoYXMIQ== + dependencies: + bn.js "^5.2.1" + randombytes "^2.1.0" + safe-buffer "^5.2.1" + +browserify-sign@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.3.tgz#7afe4c01ec7ee59a89a558a4b75bd85ae62d4208" + integrity sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw== + dependencies: + bn.js "^5.2.1" + browserify-rsa "^4.1.0" + create-hash "^1.2.0" + create-hmac "^1.1.7" + elliptic "^6.5.5" + hash-base "~3.0" + inherits "^2.0.4" + parse-asn1 "^5.1.7" + readable-stream "^2.3.8" + safe-buffer "^5.2.1" + browserify-zlib@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" @@ -5393,7 +5887,12 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -buffer@^5.5.0: +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ== + +buffer@^5.5.0, buffer@^5.7.1: version "5.7.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== @@ -5409,6 +5908,11 @@ buffer@^6.0.3: base64-js "^1.3.1" ieee754 "^1.2.1" +builtin-status-codes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" + integrity sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ== + bundle-require@^4.0.0: version "4.2.1" resolved "https://registry.yarnpkg.com/bundle-require/-/bundle-require-4.2.1.tgz#4c450a5807381d20ade987bde8ac391544257919" @@ -5438,7 +5942,7 @@ cac@^6.7.12: resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== -call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: +call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== @@ -5580,6 +6084,14 @@ chrome-trace-event@^1.0.2: resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz#05bffd7ff928465093314708c93bdfa9bd1f0f5b" integrity sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ== +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + cjs-module-lexer@^1.2.2, cjs-module-lexer@^1.2.3: version "1.4.1" resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz#707413784dbb3a72aa11c2f2b042a0bef4004170" @@ -5781,6 +6293,11 @@ concurrently@^9.0.1: tree-kill "^1.2.2" yargs "^17.7.2" +console-browserify@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" + integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== + constant-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-3.0.4.tgz#3b84a9aeaf4cf31ec45e6bf5de91bdfb0589faf1" @@ -5827,6 +6344,11 @@ cookie@0.7.1: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.1.tgz#2f73c42142d5d5cf71310a74fc4ae61670e5dbc9" integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w== +cookie@^0.7.2: + version "0.7.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7" + integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== + core-js-compat@^3.38.0, core-js-compat@^3.38.1: version "3.38.1" resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.38.1.tgz#2bc7a298746ca5a7bcb9c164bcb120f2ebc09a09" @@ -5834,6 +6356,16 @@ core-js-compat@^3.38.0, core-js-compat@^3.38.1: dependencies: browserslist "^4.23.3" +core-js@^3.38.1: + version "3.39.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.39.0.tgz#57f7647f4d2d030c32a72ea23a0555b2eaa30f83" + integrity sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g== + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + cors@^2.8.5: version "2.8.5" resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" @@ -5853,7 +6385,38 @@ cosmiconfig@^7.0.0, cosmiconfig@^7.0.1: path-type "^4.0.0" yaml "^1.10.0" -create-require@^1.1.0: +create-ecdh@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" + integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A== + dependencies: + bn.js "^4.1.0" + elliptic "^6.5.3" + +create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.4, create-hmac@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +create-require@^1.1.0, create-require@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== @@ -5879,6 +6442,24 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" +crypto-browserify@^3.11.0: + version "3.12.1" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.1.tgz#bb8921bec9acc81633379aa8f52d69b0b69e0dac" + integrity sha512-r4ESw/IlusD17lgQi1O20Fa3qNnsckR126TdUuBgAu7GBYSIPvdNyONd3Zrxh0xCwA4+6w/TDArBPsMvhur+KQ== + dependencies: + browserify-cipher "^1.0.1" + browserify-sign "^4.2.3" + create-ecdh "^4.0.4" + create-hash "^1.2.0" + create-hmac "^1.1.7" + diffie-hellman "^5.0.3" + hash-base "~3.0.4" + inherits "^2.0.4" + pbkdf2 "^3.1.2" + public-encrypt "^4.0.3" + randombytes "^2.1.0" + randomfill "^1.0.4" + crypto-js@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.2.0.tgz#4d931639ecdfd12ff80e8186dba6af2c2e856631" @@ -6348,6 +6929,14 @@ dequal@^2.0.0, dequal@^2.0.2, dequal@^2.0.3: resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== +des.js@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.1.0.tgz#1d37f5766f3bbff4ee9638e871a8768c173b81da" + integrity sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg== + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + destroy@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" @@ -6390,6 +6979,15 @@ diff@^5.0.0: resolved "https://registry.yarnpkg.com/diff/-/diff-5.2.0.tgz#26ded047cd1179b78b9537d5ef725503ce1ae531" integrity sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A== +diffie-hellman@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" @@ -6464,6 +7062,11 @@ dom4@^2.1.5: resolved "https://registry.yarnpkg.com/dom4/-/dom4-2.1.6.tgz#c90df07134aa0dbd81ed4d6ba1237b36fc164770" integrity sha512-JkCVGnN4ofKGbjf5Uvc8mmxaATIErKQKSgACdBXpsQ3fY6DlIpAyWfiBSrGkttATssbDCp3psiAKWXk5gmjycA== +domain-browser@^4.22.0: + version "4.23.0" + resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-4.23.0.tgz#427ebb91efcb070f05cffdfb8a4e9a6c25f8c94b" + integrity sha512-ArzcM/II1wCCujdCNyQjXrAFwS4mrLh4C7DZWlaI8mdh7h3BfKdNd3bKXITfl2PT9FtfQqaGvhi1vPRQPimjGA== + domelementtype@^2.0.1, domelementtype@^2.2.0, domelementtype@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" @@ -6539,6 +7142,19 @@ electron-to-chromium@^1.5.28: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.36.tgz#ec41047f0e1446ec5dce78ed5970116533139b88" integrity sha512-HYTX8tKge/VNp6FGO+f/uVDmUkq+cEfcxYhKf15Akc4M5yxt5YmorwlAitKWjWhWQnKcDRBAQKXkhqqXMqcrjw== +elliptic@^6.5.3, elliptic@^6.5.5: + version "6.6.0" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.6.0.tgz#5919ec723286c1edf28685aa89261d4761afa210" + integrity sha512-dpwoQcLc/2WLQvJvLRHKZ+f9FgOdjnq11rurqwekGQygGPsYSK29OMMD2WalatiqQ+XGFDglTNixpPfI+lpaAA== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + emoji-picker-react@^4.5.16: version "4.12.0" resolved "https://registry.yarnpkg.com/emoji-picker-react/-/emoji-picker-react-4.12.0.tgz#4cc310ad4b8a39844a2d5edcc92967683d6b5138" @@ -6819,6 +7435,35 @@ esbuild@^0.19.2: "@esbuild/win32-ia32" "0.19.12" "@esbuild/win32-x64" "0.19.12" +esbuild@^0.21.3: + version "0.21.5" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d" + integrity sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw== + optionalDependencies: + "@esbuild/aix-ppc64" "0.21.5" + "@esbuild/android-arm" "0.21.5" + "@esbuild/android-arm64" "0.21.5" + "@esbuild/android-x64" "0.21.5" + "@esbuild/darwin-arm64" "0.21.5" + "@esbuild/darwin-x64" "0.21.5" + "@esbuild/freebsd-arm64" "0.21.5" + "@esbuild/freebsd-x64" "0.21.5" + "@esbuild/linux-arm" "0.21.5" + "@esbuild/linux-arm64" "0.21.5" + "@esbuild/linux-ia32" "0.21.5" + "@esbuild/linux-loong64" "0.21.5" + "@esbuild/linux-mips64el" "0.21.5" + "@esbuild/linux-ppc64" "0.21.5" + "@esbuild/linux-riscv64" "0.21.5" + "@esbuild/linux-s390x" "0.21.5" + "@esbuild/linux-x64" "0.21.5" + "@esbuild/netbsd-x64" "0.21.5" + "@esbuild/openbsd-x64" "0.21.5" + "@esbuild/sunos-x64" "0.21.5" + "@esbuild/win32-arm64" "0.21.5" + "@esbuild/win32-ia32" "0.21.5" + "@esbuild/win32-x64" "0.21.5" + escalade@^3.1.1, escalade@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" @@ -6960,6 +7605,16 @@ eslint-plugin-jsx-a11y@^6.7.1: resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.0.0-canary-7118f5dd7-20230705.tgz#4d55c50e186f1a2b0636433d2b0b2f592ddbccfd" integrity sha512-AZYbMo/NW9chdL7vk6HQzQhT+PvTAEVqWk9ziruUoW2kAOcN5qNyelv70e0F1VNQAbvutOC9oc+xfWycI9FxDw== +eslint-plugin-react-hooks@^5.1.0-rc.0: + version "5.1.0-rc-fb9a90fa48-20240614" + resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.1.0-rc-fb9a90fa48-20240614.tgz#206a7ec005f0b286aaf7091f4e566083d310b189" + integrity sha512-xsiRwaDNF5wWNC4ZHLut+x/YcAxksUd9Rizt7LaEn3bV8VyYRpXnRJQlLOfYaVy9esk4DFP4zPPnoNVjq5Gc0w== + +eslint-plugin-react-refresh@^0.4.9: + version "0.4.14" + resolved "https://registry.yarnpkg.com/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.14.tgz#e3c611ead69bbf7436d01295c853d4abb8c59f68" + integrity sha512-aXvzCTK7ZBv1e7fahFuR3Z/fyQQSIQ711yPgYRj+Oj64tyTgO4iQIDmYXDBqvSWQ/FA4OSCsXOStlF+noU0/NA== + eslint-plugin-react@^7.33.2: version "7.37.1" resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.37.1.tgz#56493d7d69174d0d828bc83afeffe96903fdadbd" @@ -7007,11 +7662,24 @@ eslint-scope@^7.2.2: esrecurse "^4.3.0" estraverse "^5.2.0" +eslint-scope@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.2.0.tgz#377aa6f1cb5dc7592cfd0b7f892fd0cf352ce442" + integrity sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: version "3.4.3" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== +eslint-visitor-keys@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45" + integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw== + eslint@8: version "8.57.1" resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.1.tgz#7df109654aba7e3bbe5c8eae533c5e461d3c6ca9" @@ -7056,6 +7724,56 @@ eslint@8: strip-ansi "^6.0.1" text-table "^0.2.0" +eslint@^9.9.0: + version "9.14.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.14.0.tgz#534180a97c00af08bcf2b60b0ebf0c4d6c1b2c95" + integrity sha512-c2FHsVBr87lnUtjP4Yhvk4yEhKrQavGafRA/Se1ouse8PfbfC/Qh9Mxa00yWsZRlqeUB9raXip0aiiUZkgnr9g== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.12.1" + "@eslint/config-array" "^0.18.0" + "@eslint/core" "^0.7.0" + "@eslint/eslintrc" "^3.1.0" + "@eslint/js" "9.14.0" + "@eslint/plugin-kit" "^0.2.0" + "@humanfs/node" "^0.16.6" + "@humanwhocodes/module-importer" "^1.0.1" + "@humanwhocodes/retry" "^0.4.0" + "@types/estree" "^1.0.6" + "@types/json-schema" "^7.0.15" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + escape-string-regexp "^4.0.0" + eslint-scope "^8.2.0" + eslint-visitor-keys "^4.2.0" + espree "^10.3.0" + esquery "^1.5.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^8.0.0" + find-up "^5.0.0" + glob-parent "^6.0.2" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + json-stable-stringify-without-jsonify "^1.0.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + text-table "^0.2.0" + +espree@^10.0.1, espree@^10.3.0: + version "10.3.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-10.3.0.tgz#29267cf5b0cb98735b65e64ba07e0ed49d1eed8a" + integrity sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg== + dependencies: + acorn "^8.14.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^4.2.0" + espree@^9.6.0, espree@^9.6.1: version "9.6.1" resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" @@ -7070,7 +7788,7 @@ esprima@^4.0.1, esprima@~4.0.0: resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esquery@^1.4.2: +esquery@^1.4.2, esquery@^1.5.0: version "1.6.0" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== @@ -7126,11 +7844,19 @@ eventemitter3@^4.0.1: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== -events@^3.2.0, events@^3.3.0: +events@^3.0.0, events@^3.2.0, events@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + execa@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" @@ -7287,6 +8013,13 @@ file-entry-cache@^6.0.1: dependencies: flat-cache "^3.0.4" +file-entry-cache@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f" + integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== + dependencies: + flat-cache "^4.0.0" + file-selector@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/file-selector/-/file-selector-0.6.0.tgz#fa0a8d9007b829504db4d07dd4de0310b65287dc" @@ -7377,6 +8110,14 @@ flat-cache@^3.0.4: keyv "^4.5.3" rimraf "^3.0.2" +flat-cache@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c" + integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.4" + flatted@^3.2.9: version "3.3.1" resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" @@ -7512,7 +8253,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@~2.3.2: +fsevents@~2.3.2, fsevents@~2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== @@ -7683,6 +8424,16 @@ globals@^13.19.0: dependencies: type-fest "^0.20.2" +globals@^14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" + integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== + +globals@^15.9.0: + version "15.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-15.12.0.tgz#1811872883ad8f41055b61457a130221297de5b5" + integrity sha512-1+gLErljJFhbOVyaetcwJiJ4+eLe45S2E7P5UiZ9xGfeq3ATQf5DOv9G7MH3gGbKQLkzmNh2DxfZwLdw+j6oTQ== + globalthis@^1.0.3, globalthis@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" @@ -7776,6 +8527,31 @@ has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: dependencies: has-symbols "^1.0.3" +hash-base@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== + dependencies: + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +hash-base@~3.0, hash-base@~3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" + integrity sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" @@ -7847,7 +8623,16 @@ highlight.js@~11.9.0: resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.9.0.tgz#04ab9ee43b52a41a047432c8103e2158a1b8b5b0" integrity sha512-fJ7cW7fQGCYAkgv4CPfwFHrfd/cLS4Hau96JuJ+ZTOWhjnhoeN1ub1tFmALm/+lW5z4WCAuAV9bm05AP0mS6Gw== -hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2: +hmac-drbg@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg== + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== @@ -7936,6 +8721,11 @@ http-proxy-agent@^7.0.2: agent-base "^7.1.0" debug "^4.3.4" +https-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" + integrity sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg== + https-proxy-agent@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" @@ -8032,7 +8822,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3, inherits@~2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -8249,6 +9039,14 @@ is-map@^2.0.2, is-map@^2.0.3: resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw== +is-nan@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d" + integrity sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + is-negative-zero@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747" @@ -8376,6 +9174,11 @@ isarray@^2.0.5: resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -8390,6 +9193,11 @@ isomorphic-dompurify@^2.12.0, isomorphic-dompurify@^2.16.0: dompurify "^3.1.7" jsdom "^25.0.1" +isomorphic-timers-promises@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/isomorphic-timers-promises/-/isomorphic-timers-promises-1.0.1.tgz#e4137c24dbc54892de8abae3a4b5c1ffff381598" + integrity sha512-u4sej9B1LPSxTGKB/HiuzvEQnXH0ECYkSVQU39koSwmFAxhlEAFl9RdTvLv4TOTQUgBS5O3O5fwUxk6byBZ+IQ== + isomorphic.js@^0.2.4: version "0.2.5" resolved "https://registry.yarnpkg.com/isomorphic.js/-/isomorphic.js-0.2.5.tgz#13eecf36f2dba53e85d355e11bf9d4208c6f7f88" @@ -8577,7 +9385,7 @@ keycon@^1.2.0: "@scena/event-emitter" "^1.0.2" keycode "^2.2.0" -keyv@^4.5.3: +keyv@^4.5.3, keyv@^4.5.4: version "4.5.4" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== @@ -8860,6 +9668,15 @@ material-colors@^1.2.1: resolved "https://registry.yarnpkg.com/material-colors/-/material-colors-1.2.6.tgz#6d1958871126992ceecc72f4bcc4d8f010865f46" integrity sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg== +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + mdast-util-definitions@^5.0.0: version "5.1.2" resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz#9910abb60ac5d7115d6819b57ae0bcef07a3f7a7" @@ -9164,6 +9981,14 @@ micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5: braces "^3.0.3" picomatch "^2.3.1" +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + mime-db@1.52.0: version "1.52.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" @@ -9201,6 +10026,16 @@ min-indent@^1.0.0, min-indent@^1.0.1: resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== + minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" @@ -9409,6 +10244,39 @@ node-releases@^2.0.18: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f" integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== +node-stdlib-browser@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/node-stdlib-browser/-/node-stdlib-browser-1.2.1.tgz#888fa104914af94143ca4d8a8980fe0ed242d2d1" + integrity sha512-dZezG3D88Lg22DwyjsDuUs7cCT/XGr8WwJgg/S3ZnkcWuPet2Tt/W1d2Eytb1Z73JpZv+XVCDI5TWv6UMRq0Gg== + dependencies: + assert "^2.0.0" + browser-resolve "^2.0.0" + browserify-zlib "^0.2.0" + buffer "^5.7.1" + console-browserify "^1.1.0" + constants-browserify "^1.0.0" + create-require "^1.1.1" + crypto-browserify "^3.11.0" + domain-browser "^4.22.0" + events "^3.0.0" + https-browserify "^1.0.0" + isomorphic-timers-promises "^1.0.1" + os-browserify "^0.3.0" + path-browserify "^1.0.1" + pkg-dir "^5.0.0" + process "^0.11.10" + punycode "^1.4.1" + querystring-es3 "^0.2.1" + readable-stream "^3.6.0" + stream-browserify "^3.0.0" + stream-http "^3.2.0" + string_decoder "^1.0.0" + timers-browserify "^2.0.4" + tty-browserify "0.0.1" + url "^0.11.4" + util "^0.12.4" + vm-browserify "^1.0.1" + nodemon@^3.1.7: version "3.1.7" resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-3.1.7.tgz#07cb1f455f8bece6a499e0d72b5e029485521a54" @@ -9620,6 +10488,11 @@ orderedmap@^2.0.0: resolved "https://registry.yarnpkg.com/orderedmap/-/orderedmap-2.1.1.tgz#61481269c44031c449915497bf5a4ad273c512d2" integrity sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g== +os-browserify@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" + integrity sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A== + overlap-area@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/overlap-area/-/overlap-area-1.1.0.tgz#1fcaa21bdb9cb1ace973d9aa299ae6b56557a4c2" @@ -9702,6 +10575,18 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" +parse-asn1@^5.0.0, parse-asn1@^5.1.7: + version "5.1.7" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.7.tgz#73cdaaa822125f9647165625eb45f8a051d2df06" + integrity sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg== + dependencies: + asn1.js "^4.10.1" + browserify-aes "^1.2.0" + evp_bytestokey "^1.0.3" + hash-base "~3.0" + pbkdf2 "^3.1.2" + safe-buffer "^5.2.1" + parse-json@^5.0.0: version "5.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" @@ -9803,6 +10688,17 @@ pathval@^2.0.0: resolved "https://registry.yarnpkg.com/pathval/-/pathval-2.0.0.tgz#7e2550b422601d4f6b8e26f1301bc8f15a741a25" integrity sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA== +pbkdf2@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" + integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + pg-int8@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c" @@ -9934,6 +10830,13 @@ pkg-dir@^4.1.0: dependencies: find-up "^4.0.0" +pkg-dir@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-5.0.0.tgz#a02d6aebe6ba133a928f74aec20bafdfe6b8e760" + integrity sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA== + dependencies: + find-up "^5.0.0" + pkg-up@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" @@ -10081,7 +10984,7 @@ postcss@8.4.31: picocolors "^1.0.0" source-map-js "^1.0.2" -postcss@^8.4.23, postcss@^8.4.33, postcss@^8.4.38: +postcss@^8.4.23, postcss@^8.4.33, postcss@^8.4.38, postcss@^8.4.43, postcss@^8.4.45: version "8.4.47" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.47.tgz#5bf6c9a010f3e724c503bf03ef7947dcb0fea365" integrity sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ== @@ -10140,19 +11043,14 @@ postgres-range@^1.1.1: integrity sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w== posthog-js@^1.131.3: -<<<<<<< HEAD - version "1.167.0" - resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.167.0.tgz#9a6f7dc26b292f846938655b513ecda484210d59" - integrity sha512-/zXQ6tuJgiF1d4mgg3UsAi/uoyg7UnfFNQtikuALmaE53xFExpcAKbMfHPG/f54QgTvLxSHyGL1kFl/1uspkGg== -======= version "1.176.0" resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.176.0.tgz#39841ab213aa9c5500982659dc6d537a407f7205" integrity sha512-T5XKNtRzp7q6CGb7Vc7wAI76rWap9fiuDUPxPsyPBPDkreKya91x9RIsSapAVFafwD1AEin1QMczCmt9Le9BWw== ->>>>>>> preview dependencies: + core-js "^3.38.1" fflate "^0.4.8" preact "^10.19.3" - web-vitals "^4.0.1" + web-vitals "^4.2.0" preact@^10.19.3: version "10.24.3" @@ -10224,6 +11122,11 @@ pretty-hrtime@^1.0.3: resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" integrity sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A== +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + process-warning@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-4.0.0.tgz#581e3a7a1fb456c5f4fd239f76bce75897682d5a" @@ -10399,15 +11302,9 @@ prosemirror-state@^1.0.0, prosemirror-state@^1.2.2, prosemirror-state@^1.3.1, pr prosemirror-view "^1.27.0" prosemirror-tables@^1.4.0: -<<<<<<< HEAD - version "1.5.0" - resolved "https://registry.yarnpkg.com/prosemirror-tables/-/prosemirror-tables-1.5.0.tgz#3ba1ea3d53852505cc0d2037ce386973bb639a7d" - integrity sha512-VMx4zlYWm7aBlZ5xtfJHpqa3Xgu3b7srV54fXYnXgsAcIGRqKSrhiK3f89omzzgaAgAtDOV4ImXnLKhVfheVNQ== -======= version "1.6.0" resolved "https://registry.yarnpkg.com/prosemirror-tables/-/prosemirror-tables-1.6.0.tgz#b05fbb1172d55dd22ad2662af8e243c969bbbfdd" integrity sha512-eirSS2fwVYzKhvM2qeXSn9ix/SBn7QOLDftPQ4ImEQIevFDiSKAB6Lbrmm/WEgrbTDbCm+xhSq4gOD9w7wT59Q== ->>>>>>> preview dependencies: prosemirror-keymap "^1.1.2" prosemirror-model "^1.8.1" @@ -10462,6 +11359,18 @@ pstree.remy@^1.1.8: resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a" integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== +public-encrypt@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" + integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + safe-buffer "^5.1.2" + pump@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.2.tgz#836f3edd6bc2ee599256c924ffe0d88573ddcbf8" @@ -10492,6 +11401,11 @@ qs@6.13.0, qs@^6.12.3: dependencies: side-channel "^1.0.6" +querystring-es3@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" + integrity sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA== + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -10519,13 +11433,21 @@ raf-schd@^4.0.3: resolved "https://registry.yarnpkg.com/raf-schd/-/raf-schd-4.0.3.tgz#5d6c34ef46f8b2a0e880a8fcdb743efc5bfdbc1a" integrity sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ== -randombytes@^2.1.0: +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== dependencies: safe-buffer "^5.1.0" +randomfill@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + range-parser@^1.2.1, range-parser@~1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" @@ -10576,6 +11498,15 @@ react-confetti@^6.1.0: dependencies: tween-functions "^1.2.0" +react-cookie@^7.2.1: + version "7.2.2" + resolved "https://registry.yarnpkg.com/react-cookie/-/react-cookie-7.2.2.tgz#a7559e552ea9cca39a4b3686723a5acf504b8f84" + integrity sha512-e+hi6axHcw9VODoeVu8WyMWyoosa1pzpyjfvrLdF7CexfU+WSGZdDuRfHa4RJgTpfv3ZjdIpHE14HpYBieHFhg== + dependencies: + "@types/hoist-non-react-statics" "^3.3.5" + hoist-non-react-statics "^3.3.2" + universal-cookie "^7.0.0" + react-css-styled@^1.1.9: version "1.1.9" resolved "https://registry.yarnpkg.com/react-css-styled/-/react-css-styled-1.1.9.tgz#a7cc948e49f72b2f7fb1393bd85416a8293afab3" @@ -10740,6 +11671,11 @@ react-popper@^2.3.0: react-fast-compare "^3.0.1" warning "^4.0.2" +react-refresh@^0.14.2: + version "0.14.2" + resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.2.tgz#3833da01ce32da470f1f936b9d477da5c7028bf9" + integrity sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA== + react-remove-scroll-bar@^2.3.3: version "2.3.6" resolved "https://registry.yarnpkg.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz#3e585e9d163be84a010180b18721e851ac81a29c" @@ -10815,7 +11751,20 @@ read-cache@^1.0.0: dependencies: pify "^2.3.0" -readable-stream@^3.1.1, readable-stream@^3.4.0: +readable-stream@^2.3.8: + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.5.0, readable-stream@^3.6.0: version "3.6.2" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== @@ -11079,7 +12028,7 @@ resolve-pkg-maps@^1.0.0: resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== -resolve@1.22.8, resolve@^1.1.7, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.22.1, resolve@^1.22.2, resolve@^1.22.4, resolve@^1.22.8: +resolve@1.22.8, resolve@^1.1.7, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.22.1, resolve@^1.22.2, resolve@^1.22.4, resolve@^1.22.8: version "1.22.8" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== @@ -11114,6 +12063,14 @@ rimraf@^3.0.2: dependencies: glob "^7.1.3" +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + rollup@3.29.5: version "3.29.5" resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.29.5.tgz#8a2e477a758b520fb78daf04bca4c522c1da8a54" @@ -11146,6 +12103,33 @@ rollup@^4.0.2: "@rollup/rollup-win32-x64-msvc" "4.24.0" fsevents "~2.3.2" +rollup@^4.20.0: + version "4.25.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.25.0.tgz#74dff4b5c2777dfc490f9711393925da50171787" + integrity sha512-uVbClXmR6wvx5R1M3Od4utyLUxrmOcEm3pAtMphn73Apq19PDtHpgZoEvqH2YnnaNUuvKmg2DgRd2Sqv+odyqg== + dependencies: + "@types/estree" "1.0.6" + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.25.0" + "@rollup/rollup-android-arm64" "4.25.0" + "@rollup/rollup-darwin-arm64" "4.25.0" + "@rollup/rollup-darwin-x64" "4.25.0" + "@rollup/rollup-freebsd-arm64" "4.25.0" + "@rollup/rollup-freebsd-x64" "4.25.0" + "@rollup/rollup-linux-arm-gnueabihf" "4.25.0" + "@rollup/rollup-linux-arm-musleabihf" "4.25.0" + "@rollup/rollup-linux-arm64-gnu" "4.25.0" + "@rollup/rollup-linux-arm64-musl" "4.25.0" + "@rollup/rollup-linux-powerpc64le-gnu" "4.25.0" + "@rollup/rollup-linux-riscv64-gnu" "4.25.0" + "@rollup/rollup-linux-s390x-gnu" "4.25.0" + "@rollup/rollup-linux-x64-gnu" "4.25.0" + "@rollup/rollup-linux-x64-musl" "4.25.0" + "@rollup/rollup-win32-arm64-msvc" "4.25.0" + "@rollup/rollup-win32-ia32-msvc" "4.25.0" + "@rollup/rollup-win32-x64-msvc" "4.25.0" + fsevents "~2.3.2" + rope-sequence@^1.3.0: version "1.3.4" resolved "https://registry.yarnpkg.com/rope-sequence/-/rope-sequence-1.3.4.tgz#df85711aaecd32f1e756f76e43a415171235d425" @@ -11187,12 +12171,12 @@ safe-array-concat@^1.1.2: has-symbols "^1.0.3" isarray "^2.0.5" -safe-buffer@5.1.2: +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0: +safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@^5.2.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -11367,11 +12351,24 @@ set-function-name@^2.0.1, set-function-name@^2.0.2: functions-have-names "^1.2.3" has-property-descriptors "^1.0.2" +setimmediate@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== + setprototypeof@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + sharp@^0.32.1: version "0.32.6" resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.32.6.tgz#6ad30c0b7cd910df65d5f355f774aa4fce45732a" @@ -11569,6 +12566,24 @@ storybook@^8.1.1: dependencies: "@storybook/core" "8.3.5" +stream-browserify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-3.0.0.tgz#22b0a2850cdf6503e73085da1fc7b7d0c2122f2f" + integrity sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA== + dependencies: + inherits "~2.0.4" + readable-stream "^3.5.0" + +stream-http@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-3.2.0.tgz#1872dfcf24cb15752677e40e5c3f9cc1926028b5" + integrity sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A== + dependencies: + builtin-status-codes "^3.0.0" + inherits "^2.0.4" + readable-stream "^3.6.0" + xtend "^4.0.2" + streamsearch@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" @@ -11674,13 +12689,20 @@ string.prototype.trimstart@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" -string_decoder@^1.1.1, string_decoder@^1.3.0: +string_decoder@^1.0.0, string_decoder@^1.1.1, string_decoder@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== dependencies: safe-buffer "~5.2.0" +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + "strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -11839,7 +12861,7 @@ tailwind-merge@^2.0.0: resolved "https://registry.yarnpkg.com/tailwind-merge/-/tailwind-merge-2.5.3.tgz#579546e14ddda24462e0303acd8798c50f5511bb" integrity sha512-d9ZolCAIzom1nf/5p4LdD5zvjmgSxY0BGgdSvmXIoMYAiPdAW/dSpP7joCDYFY7r/HkEa2qmPtkgsu0xjQeQtw== -tailwindcss-animate@^1.0.6: +tailwindcss-animate@^1.0.6, tailwindcss-animate@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz#318b692c4c42676cc9e67b19b78775742388bef4" integrity sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA== @@ -11901,6 +12923,34 @@ tailwindcss@^3.2.7, tailwindcss@^3.4.3: resolve "^1.22.2" sucrase "^3.32.0" +tailwindcss@^3.4.10: + version "3.4.14" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.14.tgz#6dd23a7f54ec197b19159e91e3bb1e55e7aa73ac" + integrity sha512-IcSvOcTRcUtQQ7ILQL5quRDg7Xs93PdJEk1ZLbhhvJc7uj/OAhYOnruEiwnGgBvUtaUAJ8/mhSw1o8L2jCiENA== + dependencies: + "@alloc/quick-lru" "^5.2.0" + arg "^5.0.2" + chokidar "^3.5.3" + didyoumean "^1.2.2" + dlv "^1.1.3" + fast-glob "^3.3.0" + glob-parent "^6.0.2" + is-glob "^4.0.3" + jiti "^1.21.0" + lilconfig "^2.1.0" + micromatch "^4.0.5" + normalize-path "^3.0.0" + object-hash "^3.0.0" + picocolors "^1.0.0" + postcss "^8.4.23" + postcss-import "^15.1.0" + postcss-js "^4.0.1" + postcss-load-config "^4.0.1" + postcss-nested "^6.0.1" + postcss-selector-parser "^6.0.11" + resolve "^1.22.2" + sucrase "^3.32.0" + tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0, tapable@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" @@ -12013,6 +13063,13 @@ thread-stream@^3.0.0: dependencies: real-require "^0.2.0" +timers-browserify@^2.0.4: + version "2.0.12" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.12.tgz#44a45c11fbf407f34f97bccd1577c652361b00ee" + integrity sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ== + dependencies: + setimmediate "^1.0.4" + tiny-inflate@^1.0.0, tiny-inflate@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/tiny-inflate/-/tiny-inflate-1.0.3.tgz#122715494913a1805166aaf7c93467933eea26c4" @@ -12055,24 +13112,6 @@ tiptap-markdown@^0.8.10: markdown-it-task-lists "^2.1.1" prosemirror-markdown "^1.11.1" -<<<<<<< HEAD -tldts-core@^6.1.51: - version "6.1.51" - resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-6.1.51.tgz#abbb005cccc1c469ed7ddf1ec472acd91efda4d0" - integrity sha512-bu9oCYYWC1iRjx+3UnAjqCsfrWNZV1ghNQf49b3w5xE8J/tNShHTzp5syWJfwGH+pxUgTTLUnzHnfuydW7wmbg== - -tldts@^6.1.32: - version "6.1.51" - resolved "https://registry.yarnpkg.com/tldts/-/tldts-6.1.51.tgz#ee5b35a939e733515f8cbfc882791ec87962e12c" - integrity sha512-33lfQoL0JsDogIbZ8fgRyvv77GnRtwkNE/MOKocwUgPO1WrSfsq7+vQRKxRQZai5zd+zg97Iv9fpFQSzHyWdLA== - dependencies: - tldts-core "^6.1.51" - -to-fast-properties@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" - integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== -======= tldts-core@^6.1.55: version "6.1.55" resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-6.1.55.tgz#cab0d412672fca9c77d3c51312c69bb5b5ee95c2" @@ -12084,7 +13123,11 @@ tldts@^6.1.32: integrity sha512-HxQR/9roQ07Pwc8RyyrJMAxRz5/ssoF3qIPPUiIo3zUt6yMdmYZjM2OZIFMiZ3jHyz9jrGHEHuQZrUhoc1LkDw== dependencies: tldts-core "^6.1.55" ->>>>>>> preview + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== to-regex-range@^5.0.1: version "5.0.1" @@ -12239,6 +13282,11 @@ tsutils@^3.21.0: dependencies: tslib "^1.8.1" +tty-browserify@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.1.tgz#3f05251ee17904dfd0677546670db9651682b811" + integrity sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw== + tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" @@ -12372,12 +13420,21 @@ typed-styles@^0.0.7: resolved "https://registry.yarnpkg.com/typed-styles/-/typed-styles-0.0.7.tgz#93392a008794c4595119ff62dde6809dbc40a3d9" integrity sha512-pzP0PWoZUhsECYjABgCGQlRGL1n7tOHsgwYv3oIiEpJwGhFTuty/YNeduxQYzXXa3Ge5BdT6sHYIQYpl4uJ+5Q== +typescript-eslint@^8.0.1: + version "8.13.0" + resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.13.0.tgz#c7d92cc06188176c7d0e3825e10305b9c22fb102" + integrity sha512-vIMpDRJrQd70au2G8w34mPps0ezFSPMEX4pXkTzUkrNbRX+36ais2ksGWN0esZL+ZMaFJEneOBHzCgSqle7DHw== + dependencies: + "@typescript-eslint/eslint-plugin" "8.13.0" + "@typescript-eslint/parser" "8.13.0" + "@typescript-eslint/utils" "8.13.0" + typescript@5.3.3: version "5.3.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37" integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw== -typescript@^5.6.2: +typescript@^5.5.3, typescript@^5.6.2: version "5.6.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.3.tgz#5f3449e31c9d94febb17de03cc081dd56d81db5b" integrity sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw== @@ -12531,6 +13588,14 @@ unist-util-visit@^5.0.0: unist-util-is "^6.0.0" unist-util-visit-parents "^6.0.0" +universal-cookie@^7.0.0: + version "7.2.2" + resolved "https://registry.yarnpkg.com/universal-cookie/-/universal-cookie-7.2.2.tgz#93ae9ec55baab89b24300473543170bb8112773c" + integrity sha512-fMiOcS3TmzP2x5QV26pIH3mvhexLIT0HmPa3V7Q7knRfT9HG6kTwq02HZGLPw0sAOXrAmotElGRvTLCMbJsvxQ== + dependencies: + "@types/cookie" "^0.6.0" + cookie "^0.7.2" + universalify@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" @@ -12588,7 +13653,7 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -url@^0.11.0: +url@^0.11.0, url@^0.11.4: version "0.11.4" resolved "https://registry.yarnpkg.com/url/-/url-0.11.4.tgz#adca77b3562d56b72746e76b330b7f27b6721f3c" integrity sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg== @@ -12628,7 +13693,7 @@ use-sync-external-store@^1.2.0, use-sync-external-store@^1.2.2: resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz#c3b6390f3a30eba13200d2302dcdf1e7b57b2ef9" integrity sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw== -util-deprecate@^1.0.1, util-deprecate@^1.0.2: +util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== @@ -12731,6 +13796,30 @@ vite-compatible-readable-stream@^3.6.1: string_decoder "^1.1.1" util-deprecate "^1.0.1" +vite-plugin-node-polyfills@^0.22.0: + version "0.22.0" + resolved "https://registry.yarnpkg.com/vite-plugin-node-polyfills/-/vite-plugin-node-polyfills-0.22.0.tgz#d0afcf82eb985fc02244620d7cec1ddd1c6e0864" + integrity sha512-F+G3LjiGbG8QpbH9bZ//GSBr9i1InSTkaulfUHFa9jkLqVGORFBoqc2A/Yu5Mmh1kNAbiAeKeK+6aaQUf3x0JA== + dependencies: + "@rollup/plugin-inject" "^5.0.5" + node-stdlib-browser "^1.2.0" + +vite@^5.4.1: + version "5.4.10" + resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.10.tgz#d358a7bd8beda6cf0f3b7a450a8c7693a4f80c18" + integrity sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ== + dependencies: + esbuild "^0.21.3" + postcss "^8.4.43" + rollup "^4.20.0" + optionalDependencies: + fsevents "~2.3.3" + +vm-browserify@^1.0.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" + integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== + w3c-keyname@^2.2.0: version "2.2.8" resolved "https://registry.yarnpkg.com/w3c-keyname/-/w3c-keyname-2.2.8.tgz#7b17c8c6883d4e8b86ac8aba79d39e880f8869c5" @@ -12758,10 +13847,10 @@ watchpack@^2.4.1: glob-to-regexp "^0.4.1" graceful-fs "^4.1.2" -web-vitals@^4.0.1: - version "4.2.3" - resolved "https://registry.yarnpkg.com/web-vitals/-/web-vitals-4.2.3.tgz#270c4baecfbc6ec6fc15da1989e465e5f9b94fb7" - integrity sha512-/CFAm1mNxSmOj6i0Co+iGFJ58OS4NRGVP+AWS/l509uIK5a1bSoIVaHz/ZumpHTfHSZBpgrJ+wjfpAOrTHok5Q== +web-vitals@^4.2.0: + version "4.2.4" + resolved "https://registry.yarnpkg.com/web-vitals/-/web-vitals-4.2.4.tgz#1d20bc8590a37769bd0902b289550936069184b7" + integrity sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw== webidl-conversions@^3.0.0: version "3.0.1" @@ -12993,7 +14082,7 @@ xmlchars@^2.2.0: resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== -xtend@^4.0.0: +xtend@^4.0.0, xtend@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==