Merge branch 'tags/outline' into feeds-playground

* tags/outline:
  Revise autocomplete usage
  Basic presentation
  Create post with outline tags
  Basic outline tags on web
  Improve autocomplete
  Hook up autocomplete model
  WIP model
  Fix styles
  Ok working
  For sure matches Mention
  WIP, can't add spaces after tag
  Dump in files
  Upgrade all tiptap deps to latest
This commit is contained in:
Eric Bailey
2024-10-22 16:14:38 -05:00
17 changed files with 1356 additions and 260 deletions
+14 -12
View File
@@ -88,18 +88,18 @@
"@tanstack/query-async-storage-persister": "^5.25.0",
"@tanstack/react-query": "^5.8.1",
"@tanstack/react-query-persist-client": "^5.25.0",
"@tiptap/core": "^2.0.0-beta.220",
"@tiptap/extension-document": "^2.0.0-beta.220",
"@tiptap/extension-hard-break": "^2.0.3",
"@tiptap/extension-history": "^2.0.3",
"@tiptap/extension-mention": "^2.0.0-beta.220",
"@tiptap/extension-paragraph": "^2.0.0-beta.220",
"@tiptap/extension-placeholder": "^2.0.0-beta.220",
"@tiptap/extension-text": "^2.0.0-beta.220",
"@tiptap/html": "^2.1.11",
"@tiptap/pm": "^2.0.0-beta.220",
"@tiptap/react": "^2.0.0-beta.220",
"@tiptap/suggestion": "^2.0.0-beta.220",
"@tiptap/core": "^2.6.6",
"@tiptap/extension-document": "^2.6.6",
"@tiptap/extension-hard-break": "^2.6.6",
"@tiptap/extension-history": "^2.6.6",
"@tiptap/extension-mention": "^2.6.6",
"@tiptap/extension-paragraph": "^2.6.6",
"@tiptap/extension-placeholder": "^2.6.6",
"@tiptap/extension-text": "^2.6.6",
"@tiptap/html": "^2.6.6",
"@tiptap/pm": "^2.6.6",
"@tiptap/react": "^2.6.6",
"@tiptap/suggestion": "^2.6.6",
"@types/invariant": "^2.2.37",
"@types/lodash.throttle": "^4.1.9",
"@types/node": "^18.16.2",
@@ -143,6 +143,7 @@
"expo-updates": "~0.25.14",
"expo-web-browser": "~13.0.3",
"fast-text-encoding": "^1.0.6",
"fuse.js": "^7.0.0",
"history": "^5.3.0",
"hls.js": "^1.5.11",
"js-sha256": "^0.9.0",
@@ -161,6 +162,7 @@
"nanoid": "^5.0.5",
"normalize-url": "^8.0.0",
"patch-package": "^6.5.1",
"pind": "^0.5.0",
"postinstall-postinstall": "^2.1.0",
"psl": "^1.9.0",
"react": "18.2.0",
@@ -0,0 +1,7 @@
export function OutlineTags(_props: {
max?: number
initialTags?: string[]
onChangeTags: (tags: string[]) => void
}) {
return null
}
@@ -0,0 +1,350 @@
import React from 'react'
import {
NativeSyntheticEvent,
Platform,
Pressable,
StyleSheet,
TextInput,
TextInputKeyPressEventData,
View,
} from 'react-native'
import {TextInputFocusEventData} from 'react-native'
import {Pin} from 'pind'
import {isWeb} from '#/platform/detection'
import {useTagAutocomplete} from '#/view/com/composer/text-input/tagsAutocompleteState'
import {atoms as a, useTheme} from '#/alf'
import {Button, ButtonIcon, ButtonProps, ButtonText} from '#/components/Button'
import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times'
import {Text} from '#/components/Typography'
/**
* Basically `sanitizeHashtag` from `@atproto/api`, but ignores trailing
* punctuation in case the user intends to use `_` or `-`.
*/
export function sanitizeHashtagOnChange(hashtag: string) {
return hashtag.replace(/^\d+/g, '').slice(0, 64)
}
function TagButton({
children,
onPress,
}: {
children: string
onPress: ButtonProps['onPress']
}) {
return (
<Button
label="Remove tag"
size="tiny"
variant="solid"
color="secondary"
onPress={onPress}>
<ButtonText style={[a.text_sm]}>#{children}</ButtonText>
<ButtonIcon icon={X} position="right" />
</Button>
)
}
export function OutlineTags({
max = 8,
initialTags = [],
onChangeTags,
}: {
max?: number
initialTags?: string[]
onChangeTags: (tags: string[]) => void
}) {
const t = useTheme()
const dropdown = React.useRef<HTMLDivElement>(null)
const input = React.useRef<HTMLInputElement>(null)
const inputWidth = input.current
? input.current.getBoundingClientRect().width
: 200
const {query, suggestions, setQuery, saveRecentTag} = useTagAutocomplete()
const containerRef = React.useRef<HTMLDivElement>(null)
const [tags, setTags] = React.useState<string[]>(initialTags)
const [selectedItemIndex, setSelectedItemIndex] = React.useState(0)
const dropdownIsActive = Boolean(query.length)
const closeDropdownAndReset = React.useCallback(() => {
setQuery('')
setSelectedItemIndex(0)
}, [setQuery, setSelectedItemIndex])
const addTags = React.useCallback(
(_tags: string[]) => {
const _t = _tags.slice(0, max)
setTags(_t)
onChangeTags(_t)
},
[onChangeTags, setTags, max],
)
const removeTag = React.useCallback(
(tag: string) => {
addTags(tags.filter(t => t !== tag))
},
[tags, addTags],
)
const addTagAndReset = React.useCallback(
(value: string) => {
const tag = sanitizeHashtagOnChange(value).replace(/^#{1}/, '')
// enforce max hashtag length
if (tag.length > 0 && tag.length <= 64) {
addTags(Array.from(new Set([...tags, tag])).slice(0, max))
}
saveRecentTag(tag)
setQuery('')
input.current?.focus()
closeDropdownAndReset()
},
[max, tags, closeDropdownAndReset, setQuery, addTags, saveRecentTag],
)
const onSubmitEditing = React.useCallback(() => {
const item = suggestions[selectedItemIndex]
addTagAndReset(item?.value || query)
}, [query, suggestions, selectedItemIndex, addTagAndReset])
const onKeyPress = React.useCallback(
(e: NativeSyntheticEvent<TextInputKeyPressEventData>) => {
const {key} = e.nativeEvent
if (key === 'Backspace' && query === '') {
addTags(tags.slice(0, -1))
closeDropdownAndReset()
} else if (key === ' ') {
e.preventDefault() // prevents an additional space on web
addTagAndReset(query)
}
if (dropdownIsActive) {
if (key === 'Escape') {
closeDropdownAndReset()
} else if (key === 'ArrowUp') {
e.preventDefault()
setSelectedItemIndex(
(selectedItemIndex + suggestions.length - 1) % suggestions.length,
)
} else if (key === 'ArrowDown') {
e.preventDefault()
setSelectedItemIndex((selectedItemIndex + 1) % suggestions.length)
} else if (
isWeb &&
key === 'Tab' &&
// @ts-ignore web only
!e.nativeEvent.shiftKey
) {
e.preventDefault()
onSubmitEditing()
}
}
},
[
query,
tags,
dropdownIsActive,
selectedItemIndex,
suggestions.length,
closeDropdownAndReset,
setSelectedItemIndex,
addTags,
addTagAndReset,
onSubmitEditing,
],
)
const onChangeText = React.useCallback(
async (value: string) => {
const tag = sanitizeHashtagOnChange(value)
setQuery(tag)
if (tag.length) {
setQuery(tag)
} else {
setSelectedItemIndex(0)
}
},
[setSelectedItemIndex, setQuery],
)
const onBlur = React.useCallback(
(e: NativeSyntheticEvent<TextInputFocusEventData>) => {
// @ts-ignore
const target = e.nativeEvent.relatedTarget as HTMLElement | undefined
if (
!tags.length &&
(!target || !target.id.includes('tag_autocomplete_option'))
) {
setQuery('')
}
},
[tags, setQuery],
)
React.useEffect(() => {
// outside click
function onClick(e: MouseEvent) {
const drop = dropdown.current
const control = input.current
if (
!drop ||
!control ||
e.target === drop ||
e.target === control ||
drop.contains(e.target as Node) ||
control.contains(e.target as Node)
)
return
closeDropdownAndReset()
}
document.addEventListener('click', onClick)
return () => {
document.removeEventListener('click', onClick)
}
}, [closeDropdownAndReset])
return (
<View ref={containerRef as any} style={[a.px_sm]}>
<View style={[a.flex_row, a.align_start, a.flex_wrap, a.gap_sm]}>
{tags.map((tag, i) => (
<TagButton key={tag + i} onPress={() => removeTag(tag)}>
{tag}
</TagButton>
))}
{tags.length >= max ? null : (
<TextInput
ref={input as any}
id="tags-autocomplete-input"
role={'listbox' as any}
aria-controls="tags-autocomplete-dropdown"
aria-haspopup="listbox"
aria-expanded={dropdownIsActive}
value={query}
onBlur={onBlur}
onKeyPress={onKeyPress}
onSubmitEditing={onSubmitEditing}
onChangeText={onChangeText}
blurOnSubmit={false}
style={[
a.text_sm,
a.leading_tight,
t.atoms.text_contrast_medium,
{
paddingVertical: 4,
},
]}
placeholder="Add outline tags"
autoCapitalize="none"
autoCorrect={false}
autoComplete="off"
accessible={true}
accessibilityLabel="Add tags to your post"
accessibilityHint={`Type a tag and press enter to add it. You can add up to ${max} tag.`}
/>
)}
</View>
<Pin
pinned={Boolean(query.length)}
to={input}
at="bottomLeft"
from="topLeft"
style={{width: inputWidth}}>
<View
ref={dropdown as any}
style={[t.atoms.bg, t.atoms.border_contrast_low, styles.dropdown]}
role={'listbox' as any}
id="tags-autocomplete-dropdown">
{suggestions.map((item, index) => {
const isFirst = index === 0
const isLast = index === suggestions.length - 1
return (
<Pressable
id={`tag_autocomplete_option_${item.value}`}
accessibilityRole="button"
key={item.value}
onPress={() => addTagAndReset(item.value)}
style={state => [
t.atoms.border_contrast_low,
styles.dropdownItem,
{
backgroundColor: state.hovered
? t.atoms.bg_contrast_25.backgroundColor
: undefined,
},
selectedItemIndex === index
? t.atoms.bg_contrast_25
: undefined,
isFirst
? styles.firstResult
: isLast
? styles.lastResult
: undefined,
]}>
<Text numberOfLines={1}>{item.value}</Text>
</Pressable>
)
})}
</View>
</Pin>
</View>
)
}
const styles = StyleSheet.create({
outer: {
flexDirection: 'row',
flexWrap: 'wrap',
alignItems: 'center',
gap: 8,
},
input: {
flexGrow: 1,
minWidth: 100,
fontSize: 15,
lineHeight: Platform.select({
web: 20,
native: 18,
}),
paddingTop: 5,
paddingBottom: 5,
},
dropdown: {
width: '100%',
borderRadius: 6,
borderWidth: 1,
borderStyle: 'solid',
padding: 4,
},
dropdownItem: {
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
flexDirection: 'row',
paddingHorizontal: 12,
paddingVertical: 8,
gap: 4,
},
firstResult: {
borderTopLeftRadius: 2,
borderTopRightRadius: 2,
},
lastResult: {
borderBottomLeftRadius: 2,
borderBottomRightRadius: 2,
},
})
+1
View File
@@ -111,6 +111,7 @@ export async function post(
embed,
langs,
labels,
tags: draft.tags,
})
} catch (e: any) {
logger.error(`Failed to create post`, {
+9 -1
View File
@@ -1,7 +1,7 @@
import {MMKV} from 'react-native-mmkv'
import {IS_DEV} from '#/env'
import {Device} from '#/storage/schema'
import {Account, Device} from '#/storage/schema'
export * from '#/storage/schema'
@@ -74,9 +74,17 @@ export class Storage<Scopes extends unknown[], Schema> {
*/
export const device = new Storage<[], Device>({id: 'bsky_device'})
/**
* Account data that's specific to the account
*
* `account.set([did, key], true)`
*/
export const account = new Storage<[string], Account>({id: 'bsky_account'})
if (IS_DEV && typeof window !== 'undefined') {
// @ts-ignore
window.bsky_storage = {
device,
account,
}
}
+9
View File
@@ -1,3 +1,5 @@
import {Result} from '#/view/com/composer/text-input/tagsAutocompleteState'
/**
* Device data that's specific to the device and does not vary based account
*/
@@ -9,3 +11,10 @@ export type Device = {
countryCode: string | undefined
}
}
/**
* Account data that's specific to the current account
*/
export type Account = {
recentTags: Result[] | undefined
}
+43
View File
@@ -15,6 +15,11 @@
--text: black;
--background: white;
--backgroundLight: hsl(211, 20%, 95%);
--tagFg: black;
--tagBg: #c1ccd7;
--mentionFg: white;
--mentionBg: #14a571;
}
@media (prefers-color-scheme: dark) {
:root {
@@ -22,6 +27,11 @@
--text: white;
--background: black;
--backgroundLight: hsl(211, 20%, 20%);
--tagFg: black;
--tagBg: #c1ccd7;
--mentionFg: white;
--mentionBg: #14a571;
}
}
@@ -30,6 +40,11 @@ html.theme--light {
--background: white;
--backgroundLight: hsl(211, 20%, 95%);
background-color: white;
--tagFg: black;
--tagBg: #c1ccd7;
--mentionFg: white;
--mentionBg: #14a571;
}
html.theme--dark {
color-scheme: dark;
@@ -37,6 +52,11 @@ html.theme--dark {
--text: white;
--background: black;
--backgroundLight: hsl(211, 20%, 20%);
--tagFg: white;
--tagBg: #637f9c;
--mentionFg: white;
--mentionBg: #14a571;
}
html.theme--dim {
color-scheme: dark;
@@ -44,6 +64,9 @@ html.theme--dim {
--text: white;
--background: hsl(211, 20%, 4%);
--backgroundLight: hsl(211, 20%, 10%);
--tagFg: white;
--tagBg: #637f9c;
}
/* Buttons and inputs have a font set by UA, so we'll have to reset that */
@@ -120,6 +143,26 @@ a[role='link'][data-no-underline='1']:hover {
.ProseMirror-focused {
outline: 0;
}
/* Hashtags in ProseMirror */
.ProseMirror .inline-tag,
.ProseMirror .mention {
position: relative;
}
/* Neato™ */
.ProseMirror .inline-tag {
color: var(--tagFg);
background-color: var(--tagBg);
}
.ProseMirror .mention {
color: var(--mentionFg);
background-color: var(--mentionBg);
}
.ProseMirror .inline-tag,
.ProseMirror .mention {
border-radius: 4px;
padding: 0 3px 2px;
}
textarea:focus,
input:focus {
outline: 0;
+17 -1
View File
@@ -105,6 +105,7 @@ import * as Toast from '#/view/com/util/Toast'
import {UserAvatar} from '#/view/com/util/UserAvatar'
import {atoms as a, native, useTheme} from '#/alf'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import {OutlineTags} from '#/components/Composer/OutlineTags'
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
import {EmojiArc_Stroke2_Corner0_Rounded as EmojiSmile} from '#/components/icons/Emoji'
import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times'
@@ -164,7 +165,13 @@ export const ComposePost = ({
const [draft, dispatch] = useReducer(
composerReducer,
{initImageUris, initQuoteUri: initQuote?.uri, initText, initMention},
{
initImageUris,
initQuoteUri: initQuote?.uri,
initText,
initMention,
initOutlineTags: [],
},
createComposerState,
)
const richtext = draft.richtext
@@ -508,6 +515,13 @@ export const ComposePost = ({
dispatch({type: 'embed_update_gif', alt: altText})
}, [])
const onChangeOutlineTags = useCallback(
(tags: string[]) => {
dispatch({type: 'tags_update', tags})
},
[dispatch],
)
const {
scrollHandler,
onScrollViewContentSizeChange,
@@ -744,6 +758,8 @@ export const ComposePost = ({
</Animated.ScrollView>
<SuggestedLanguage text={richtext.text} />
<OutlineTags onChangeTags={onChangeOutlineTags} />
<Animated.View
style={[a.flex_row, a.p_sm, t.atoms.bg, bottomBarAnimatedStyle]}>
<ScrollView
+11
View File
@@ -53,6 +53,7 @@ export type ComposerDraft = {
postgate: AppBskyFeedPostgate.Record
threadgate: ThreadgateAllowUISetting[]
embed: EmbedDraft
tags: string[]
}
export type ComposerAction =
@@ -76,6 +77,7 @@ export type ComposerAction =
| {type: 'embed_add_gif'; gif: Gif}
| {type: 'embed_update_gif'; alt: string}
| {type: 'embed_remove_gif'}
| {type: 'tags_update'; tags: string[]}
export const MAX_IMAGES = 4
@@ -324,6 +326,12 @@ export function composerReducer(
},
}
}
case 'tags_update': {
return {
...state,
tags: action.tags,
}
}
default:
return state
}
@@ -334,11 +342,13 @@ export function createComposerState({
initMention,
initImageUris,
initQuoteUri,
initOutlineTags,
}: {
initText: string | undefined
initMention: string | undefined
initImageUris: ComposerOpts['imageUris']
initQuoteUri: string | undefined
initOutlineTags: string[]
}): ComposerDraft {
let media: ImagesMedia | undefined
if (initImageUris?.length) {
@@ -379,5 +389,6 @@ export function createComposerState({
media,
link: undefined,
},
tags: initOutlineTags,
}
}
@@ -13,15 +13,15 @@ import {Text as TiptapText} from '@tiptap/extension-text'
import {generateJSON} from '@tiptap/html'
import {EditorContent, JSONContent, useEditor} from '@tiptap/react'
import {useColorSchemeStyle} from '#/lib/hooks/useColorSchemeStyle'
import {usePalette} from '#/lib/hooks/usePalette'
import {blobToDataUri, isUriImage} from '#/lib/media/util'
import {useActorAutocompleteFn} from '#/state/queries/actor-autocomplete'
import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle'
import {blobToDataUri, isUriImage} from 'lib/media/util'
import {textInputWebEmitter} from '#/view/com/composer/text-input/textInputWebEmitter'
import {
LinkFacetMatch,
suggestLinkCardUri,
} from 'view/com/composer/text-input/text-input-util'
} from '#/view/com/composer/text-input/text-input-util'
import {textInputWebEmitter} from '#/view/com/composer/text-input/textInputWebEmitter'
import {atoms as a, useAlf} from '#/alf'
import {Portal} from '#/components/Portal'
import {normalizeTextStyles} from '#/components/Typography'
@@ -29,7 +29,7 @@ import {Text} from '../../util/text/Text'
import {createSuggestion} from './web/Autocomplete'
import {Emoji} from './web/EmojiPicker.web'
import {LinkDecorator} from './web/LinkDecorator'
import {TagDecorator} from './web/TagDecorator'
import {createTagsAutocomplete, Tags} from './web/Tags'
export interface TextInputRef {
focus: () => void
@@ -71,7 +71,13 @@ export const TextInput = React.forwardRef(function TextInputImpl(
() => [
Document,
LinkDecorator,
TagDecorator,
// TagDecorator,
Tags.configure({
HTMLAttributes: {
class: 'inline-tag',
},
suggestion: createTagsAutocomplete(),
}),
Mention.configure({
HTMLAttributes: {
class: 'mention',
@@ -327,6 +333,8 @@ function editorJsonToText(
text += json.text || ''
} else if (json.type === 'mention') {
text += `@${json.attrs?.id || ''}`
} else if (json.type === 'tag') {
text += `#${json.attrs?.id || ''}`
}
return text
}
@@ -0,0 +1,74 @@
import React from 'react'
import Fuse from 'fuse.js'
import {useSession} from '#/state/session'
import {account} from '#/storage'
export type Result = {
value: string
}
export type Model = {
readonly suggestions: Result[]
setQuery(query: string): void
save(tag: string): void
}
export function useTagAutocomplete() {
const {currentAccount} = useSession()
const [query, setQuery] = React.useState('')
const [searchSuggestions, setSearchSuggestions] = React.useState<Result[]>([])
const search = React.useCallback(
async (_query: string) => {
// TODO actually search
// TODO debounce/abort controller
setSearchSuggestions([])
},
[setSearchSuggestions],
)
const onSetQuery = React.useCallback(
(query: string) => {
setQuery(query)
search(query)
},
[setQuery, search],
)
const saveRecentTag = React.useCallback(
(tag: string) => {
if (!currentAccount) {
throw new Error('No current account')
}
const recentTags = account.get([currentAccount.did, 'recentTags']) || []
account.set(
[currentAccount.did, 'recentTags'],
[{value: tag}, ...recentTags.filter(t => t.value !== tag)].slice(0, 40),
)
},
[currentAccount],
)
const suggestions: Result[] = React.useMemo(() => {
if (!currentAccount) {
throw new Error('No current account')
}
const recentTags = account.get([currentAccount.did, 'recentTags']) || []
const items = [
...recentTags.map(t => t.value),
...searchSuggestions.map(s => s.value),
]
const fuse = new Fuse(items)
// search amongst mixed set of tags
const results = fuse.search(query).map(r => r.item)
return results.map(value => ({value}))
}, [currentAccount, query, searchSuggestions])
return {
query,
suggestions,
setQuery: onSetQuery,
saveRecentTag,
}
}
@@ -0,0 +1,2 @@
export {Tags} from './plugin'
export {createTagsAutocomplete} from './view'
@@ -0,0 +1,308 @@
/**
* This is basically a fork of the Mention plugin from Tiptap.
*
* @see https://github.com/ueberdosis/tiptap/blob/ec6121da1c3f808987d32de7a8c56b52520bfea8/packages/extension-mention/src/mention.ts
*/
import {mergeAttributes, Node} from '@tiptap/core'
import {DOMOutputSpec, Node as ProseMirrorNode} from '@tiptap/pm/model'
import {PluginKey} from '@tiptap/pm/state'
import Suggestion, {SuggestionOptions} from '@tiptap/suggestion'
import {findSuggestionMatch} from './utils'
// See `addAttributes` below
export interface MentionNodeAttrs {
/**
* The identifier for the selected item that was mentioned, stored as a `data-id`
* attribute.
*/
id: string | null
/**
* The label to be rendered by the editor as the displayed text for this mentioned
* item, if provided. Stored as a `data-label` attribute. See `renderLabel`.
*/
label?: string | null
}
export type MentionOptions<
SuggestionItem = any,
Attrs extends Record<string, any> = MentionNodeAttrs,
> = {
/**
* The HTML attributes for a mention node.
* @default {}
* @example { class: 'foo' }
*/
HTMLAttributes: Record<string, any>
/**
* A function to render the label of a mention.
* @deprecated use renderText and renderHTML instead
* @param props The render props
* @returns The label
* @example ({ options, node }) => `${options.suggestion.char}${node.attrs.label ?? node.attrs.id}`
*/
renderLabel?: (props: {
options: MentionOptions<SuggestionItem, Attrs>
node: ProseMirrorNode
}) => string
/**
* A function to render the text of a mention.
* @param props The render props
* @returns The text
* @example ({ options, node }) => `${options.suggestion.char}${node.attrs.label ?? node.attrs.id}`
*/
renderText: (props: {
options: MentionOptions<SuggestionItem, Attrs>
node: ProseMirrorNode
}) => string
/**
* A function to render the HTML of a mention.
* @param props The render props
* @returns The HTML as a ProseMirror DOM Output Spec
* @example ({ options, node }) => ['span', { 'data-type': 'mention' }, `${options.suggestion.char}${node.attrs.label ?? node.attrs.id}`]
*/
renderHTML: (props: {
options: MentionOptions<SuggestionItem, Attrs>
node: ProseMirrorNode
}) => DOMOutputSpec
/**
* Whether to delete the trigger character with backspace.
* @default false
*/
deleteTriggerWithBackspace: boolean
/**
* The suggestion options.
* @default {}
* @example { char: '@', pluginKey: MentionPluginKey, command: ({ editor, range, props }) => { ... } }
*/
suggestion: Omit<SuggestionOptions<SuggestionItem, Attrs>, 'editor'>
}
/**
* The plugin key for the mention plugin.
* @default 'mention'
*/
export const TagsPluginKey = new PluginKey('tag')
/**
* This extension allows you to insert mentions into the editor.
* @see https://www.tiptap.dev/api/extensions/mention
*/
export const Tags = Node.create<
MentionOptions<any, {tag: string; punctuation?: string}>
>({
name: 'tag',
addOptions() {
return {
HTMLAttributes: {},
renderText({options, node}) {
return `${options.suggestion.char}${node.attrs.label ?? node.attrs.id}`
},
deleteTriggerWithBackspace: false,
renderHTML({options, node}) {
return [
'span',
mergeAttributes(this.HTMLAttributes, options.HTMLAttributes),
`${options.suggestion.char}${node.attrs.label ?? node.attrs.id}`,
]
},
suggestion: {
char: '#',
pluginKey: TagsPluginKey,
command: ({editor, range, props}) => {
const {tag, punctuation} = props
// increase range.to by one when the next node is of type "text"
// and starts with a space character
const nodeAfter = editor.view.state.selection.$to.nodeAfter
const overrideSpace = nodeAfter?.text?.startsWith(' ')
if (overrideSpace) {
range.to += 1
}
editor
.chain()
.focus()
.insertContentAt(range, [
{
type: this.name,
attrs: {id: tag},
},
{
type: 'text',
text: `${punctuation || ''} `,
},
])
.run()
window.getSelection()?.collapseToEnd()
},
allow: ({state, range}) => {
const $from = state.doc.resolve(range.from)
const type = state.schema.nodes[this.name]
const allow = !!$from.parent.type.contentMatch.matchType(type)
return allow
},
findSuggestionMatch: findSuggestionMatch,
},
}
},
group: 'inline',
inline: true,
selectable: false,
atom: true,
addAttributes() {
return {
id: {
default: null,
parseHTML: element => element.getAttribute('data-id'),
renderHTML: attributes => {
if (!attributes.id) {
return {}
}
return {
'data-id': attributes.id,
}
},
},
label: {
default: null,
parseHTML: element => element.getAttribute('data-label'),
renderHTML: attributes => {
if (!attributes.label) {
return {}
}
return {
'data-label': attributes.label,
}
},
},
}
},
parseHTML() {
return [
{
tag: `span[data-type="${this.name}"]`,
},
]
},
renderHTML({node, HTMLAttributes}) {
if (this.options.renderLabel !== undefined) {
console.warn(
'renderLabel is deprecated use renderText and renderHTML instead',
)
return [
'span',
mergeAttributes(
{'data-type': this.name},
this.options.HTMLAttributes,
HTMLAttributes,
),
this.options.renderLabel({
options: this.options,
node,
}),
]
}
const mergedOptions = {...this.options}
mergedOptions.HTMLAttributes = mergeAttributes(
{'data-type': this.name},
this.options.HTMLAttributes,
HTMLAttributes,
)
const html = this.options.renderHTML({
options: mergedOptions,
node,
})
if (typeof html === 'string') {
return [
'span',
mergeAttributes(
{'data-type': this.name},
this.options.HTMLAttributes,
HTMLAttributes,
),
html,
]
}
return html
},
renderText({node}) {
if (this.options.renderLabel !== undefined) {
console.warn(
'renderLabel is deprecated use renderText and renderHTML instead',
)
return this.options.renderLabel({
options: this.options,
node,
})
}
return this.options.renderText({
options: this.options,
node,
})
},
addKeyboardShortcuts() {
return {
Backspace: () =>
this.editor.commands.command(({tr, state}) => {
let isMention = false
const {selection} = state
const {empty, anchor} = selection
if (!empty) {
return false
}
state.doc.nodesBetween(anchor - 1, anchor, (node, pos) => {
if (node.type.name === this.name) {
isMention = true
tr.insertText(
this.options.deleteTriggerWithBackspace
? ''
: this.options.suggestion.char || '',
pos,
pos + node.nodeSize,
)
return false
}
})
return isMention
}),
}
},
addProseMirrorPlugins() {
return [
Suggestion({
editor: this.editor,
...this.options.suggestion,
}),
]
},
})
@@ -0,0 +1,69 @@
import {TAG_REGEX, TRAILING_PUNCTUATION_REGEX} from '@atproto/api'
import {findSuggestionMatch as defaultFindSuggestionMatch} from '@tiptap/suggestion'
/**
* This method eventually receives the `query` property from the result of
* `findSuggestionMatch` below.
*/
export function parsePunctuationFromTag(value: string) {
const reg = TRAILING_PUNCTUATION_REGEX
const tag = value.replace(reg, '')
const punctuation = value.match(reg)?.[0] || ''
return {tag, punctuation}
}
/**
* A result must be returned from this method in order for the suggestion
* plugin to remain active and allow for the user to select a suggestion.
*
* That's why we use the loose regex form that includes trialing punctuation.
* We strip that our later.
*
* @see https://github.com/ueberdosis/tiptap/blob/cf2067906f506486c6613f872be8b1fd318526c9/packages/suggestion/src/findSuggestionMatch.ts
*/
export function findSuggestionMatch({
$position,
}: Parameters<typeof defaultFindSuggestionMatch>[0]) {
const text = $position.nodeBefore?.isText && $position.nodeBefore.text
if (!text) {
return null
}
const textFrom = $position.pos - text.length
const match = Array.from(text.matchAll(TAG_REGEX)).pop()
if (!match || match.input === undefined || match.index === undefined) {
return null
}
const [fullMatch, , tag] = match
if (!tag || tag.length === 0 || tag.length > 64) return null
const leadingSpaceOffset = fullMatch.startsWith(' ') ? 1 : 0
const hashtagOffset = 1
// The absolute position of the match in the document
const from = textFrom + match.index + leadingSpaceOffset
const to = from + tag.length + hashtagOffset
// If the $position is located within the matched substring, return that range
if (from < $position.pos && to >= $position.pos) {
return {
range: {
from,
to,
},
/**
* TODO
* We parse out the punctuation later.
*/
query: tag,
text: fullMatch.replace(/^\s{1}/, ''),
}
}
return null
}
@@ -0,0 +1,213 @@
import React, {forwardRef, useImperativeHandle, useState} from 'react'
import {Pressable, StyleSheet, View} from 'react-native'
import {ReactRenderer} from '@tiptap/react'
import {
SuggestionKeyDownProps,
SuggestionOptions,
SuggestionProps,
} from '@tiptap/suggestion'
import tippy, {Instance as TippyInstance} from 'tippy.js'
import {usePalette} from '#/lib/hooks/usePalette'
import {useTagAutocomplete} from '#/view/com/composer/text-input/tagsAutocompleteState'
import {Text} from '#/view/com/util/text/Text'
import {parsePunctuationFromTag} from './utils'
type AutocompleteRef = {
onKeyDown: (props: SuggestionKeyDownProps) => boolean
}
export function createTagsAutocomplete(): Omit<SuggestionOptions, 'editor'> {
return {
render() {
let component: ReactRenderer<AutocompleteRef> | undefined
let popup: TippyInstance[] | undefined
return {
onStart: props => {
component = new ReactRenderer(Autocomplete, {
props,
editor: props.editor,
})
if (!props.clientRect) return
// @ts-ignore getReferenceClientRect doesnt like that clientRect can return null -prf
popup = tippy('body', {
getReferenceClientRect: props.clientRect,
appendTo: () => document.body,
content: component.element,
showOnCreate: true,
interactive: true,
trigger: 'manual',
placement: 'bottom-start',
})
},
onUpdate(props) {
component?.updateProps(props)
if (!props.clientRect) return
popup?.[0]?.setProps({
// @ts-ignore getReferenceClientRect doesnt like that clientRect can return null -prf
getReferenceClientRect: props.clientRect,
})
},
onKeyDown(props) {
if (props.event.key === 'Escape') {
popup?.[0]?.hide()
return true
}
return component?.ref?.onKeyDown(props) || false
},
onExit() {
popup?.[0]?.destroy()
component?.destroy()
},
}
},
}
}
const Autocomplete = forwardRef<AutocompleteRef, SuggestionProps>(
function AutocompleteImpl(props, ref) {
const {command, query} = props
const {suggestions, setQuery, saveRecentTag} = useTagAutocomplete()
const pal = usePalette('default')
const [selectedIndex, setSelectedIndex] = useState(0)
React.useEffect(() => {
setQuery(query)
}, [query, setQuery])
const commit = React.useCallback(
(query: string) => {
const {tag, punctuation} = parsePunctuationFromTag(query)
/*
* This values here are passed directly to the `command` method
* configured in the `Tags` plugin.
*
* The type error is ignored because we parse the tag and punctuation
* separately above. We could do this in `command` definition, but we
* only want to `commitRecentTag` with the sanitized tag.
*/
command({tag, punctuation})
saveRecentTag(tag)
},
[command, saveRecentTag],
)
const selectItem = React.useCallback(
(index: number) => {
const item = suggestions[index]
if (item) commit(item.value)
},
[suggestions, commit],
)
useImperativeHandle(ref, () => ({
onKeyDown: ({event}) => {
if (event.key === 'ArrowUp') {
setSelectedIndex(
(selectedIndex + suggestions.length - 1) % suggestions.length,
)
return true
}
if (event.key === 'ArrowDown') {
setSelectedIndex((selectedIndex + 1) % suggestions.length)
return true
}
if (event.key === 'Enter') {
if (!suggestions.length) {
// no suggestions, use whatever the user typed
commit(props.query)
} else {
selectItem(selectedIndex)
}
return true
}
if (event.key === ' ') {
commit(props.query)
return true
}
return false
},
}))
// hide entirely if no suggestions
if (!suggestions.length) return null
return (
<div className="items">
<View style={[pal.borderDark, pal.view, styles.container]}>
{suggestions.map(({value}, index) => {
const {tag} = parsePunctuationFromTag(value)
const isSelected = selectedIndex === index
const isFirst = index === 0
const isLast = index === suggestions.length - 1
return (
<Pressable
key={tag}
style={state => [
styles.resultContainer,
{
backgroundColor: state.hovered
? pal.viewLight.backgroundColor
: undefined,
},
isSelected ? pal.viewLight : undefined,
isFirst
? styles.firstResult
: isLast
? styles.lastResult
: undefined,
]}
onPress={() => selectItem(index)}
accessibilityRole="button">
<Text type="md" style={pal.textLight} numberOfLines={1}>
#{tag}
</Text>
</Pressable>
)
})}
</View>
</div>
)
},
)
const styles = StyleSheet.create({
container: {
width: 500,
borderRadius: 6,
borderWidth: 1,
borderStyle: 'solid',
padding: 4,
},
resultContainer: {
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
flexDirection: 'row',
paddingHorizontal: 12,
paddingVertical: 8,
gap: 4,
},
firstResult: {
borderTopLeftRadius: 2,
borderTopRightRadius: 2,
},
lastResult: {
borderBottomLeftRadius: 2,
borderBottomRightRadius: 2,
},
})
@@ -29,6 +29,7 @@ import {useComposerControls} from '#/state/shell/composer'
import {useMergedThreadgateHiddenReplies} from '#/state/threadgate-hidden-replies'
import {PostThreadFollowBtn} from '#/view/com/post-thread/PostThreadFollowBtn'
import {atoms as a, useTheme} from '#/alf'
import {Button, ButtonText} from '#/components/Button'
import {AppModerationCause} from '#/components/Pills'
import {RichText} from '#/components/RichText'
import {Text as NewText} from '#/components/Typography'
@@ -367,6 +368,28 @@ let PostThreadItemLoaded = ({
/>
</View>
)}
{AppBskyFeedPost.isRecord(post.record) && post.record.tags ? (
<View
style={[
a.flex_row,
a.flex_wrap,
a.align_start,
a.gap_sm,
a.pt_sm,
]}>
{post.record.tags.map((tag, i) => (
<Button
key={tag + i}
label={tag}
size="tiny"
variant="solid"
color="secondary">
<ButtonText>#{tag}</ButtonText>
</Button>
))}
</View>
) : null}
</ContentHider>
<ExpandedPostDetails
post={post}
+192 -240
View File
@@ -5942,32 +5942,6 @@
resolved "https://registry.yarnpkg.com/@remirror/core-constants/-/core-constants-2.0.2.tgz#f05eccdc69e3a65e7d524b52548f567904a11a1a"
integrity sha512-dyHY+sMF0ihPus3O27ODd4+agdHMEmuRdyiZJ2CCWjPV5UFmn17ZbElvk6WOGVE4rdCJKZQCrPV2BcikOMLUGQ==
"@remirror/core-helpers@^3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@remirror/core-helpers/-/core-helpers-3.0.0.tgz#3a35c2346bc23ebc3cee585b7840b5567755c5f1"
integrity sha512-tusEgQJIqg4qKj6HSBUFcyRnWnziw3neh4T9wOmsPGHFC3w9kl5KSrDb9UAgE8uX6y32FnS7vJ955mWOl3n50A==
dependencies:
"@remirror/core-constants" "^2.0.2"
"@remirror/types" "^1.0.1"
"@types/object.omit" "^3.0.0"
"@types/object.pick" "^1.3.2"
"@types/throttle-debounce" "^2.1.0"
case-anything "^2.1.13"
dash-get "^1.0.2"
deepmerge "^4.3.1"
fast-deep-equal "^3.1.3"
make-error "^1.3.6"
object.omit "^3.0.0"
object.pick "^1.3.0"
throttle-debounce "^3.0.1"
"@remirror/types@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@remirror/types/-/types-1.0.1.tgz#768502497a0fbbc23338a1586b893f729310cf70"
integrity sha512-VlZQxwGnt1jtQ18D6JqdIF+uFZo525WEqrfp9BOc3COPpK4+AWCgdnAWL+ho6imWcoINlGjR/+3b6y5C1vBVEA==
dependencies:
type-fest "^2.19.0"
"@rnx-kit/chromium-edge-launcher@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@rnx-kit/chromium-edge-launcher/-/chromium-edge-launcher-1.0.0.tgz#c0df8ea00a902c7a417cd9655aab06de398b939c"
@@ -6871,103 +6845,105 @@
dependencies:
pretty-format "^29.0.0"
"@tiptap/core@^2.0.0-beta.220":
version "2.1.6"
resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.1.6.tgz#681350ebbdc429850059f43d4aa31181b78293f0"
integrity sha512-gm8n1oiBhSP6CDhalmmWwLD7yzIUqJJ246/t8rY3o+HJ/I+p0rqCx0mPvMiwcIBmYX8tUCVz7mb9aSFUu/umOQ==
"@tiptap/core@^2.6.6":
version "2.6.6"
resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.6.6.tgz#34edc749dece70e99231b669582b92afae1c3ec8"
integrity sha512-VO5qTsjt6rwworkuo0s5AqYMfDA0ZwiTiH6FHKFSu2G/6sS7HKcc/LjPq+5Legzps4QYdBDl3W28wGsGuS1GdQ==
"@tiptap/extension-bubble-menu@^2.1.6":
version "2.1.6"
resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.1.6.tgz#23b2664963a7347f864a7255be83592927268399"
integrity sha512-13YDJB19xbDL/SZaPs8NvUAA+w5MIWugP8ByKQeIlL8vlcbiJjqoT77YP6v300DtFyVrnLo/iMJh9RMB4NOnwg==
"@tiptap/extension-bubble-menu@^2.6.6":
version "2.6.6"
resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.6.6.tgz#6e2b745fd10262258ce51562963694bfc8178ef1"
integrity sha512-IkfmlZq67aaegym5sBddBc/xXWCArxn5WJEl1oxKEayjQhybKSaqI7tk0lOx/x7fa5Ml1WlGpCFh+KKXbQTG0g==
dependencies:
tippy.js "^6.3.7"
"@tiptap/extension-document@^2.0.0-beta.220":
version "2.1.6"
resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.1.6.tgz#91b1239d4eaa3c12cb382fc9805eedd3d32b1b7c"
integrity sha512-econFqLeQR8pe0xv7kjw6ZPRhcNXGrNi9854celX0lhqTqtBxvU6nWHzUDzoq/lmnXYgpFTPv42AwUEspvpwdw==
"@tiptap/extension-document@^2.6.6":
version "2.6.6"
resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.6.6.tgz#d766c645409b3799d0f9fa6ac8a318e34052db15"
integrity sha512-6qlH5VWzLHHRVeeciRC6C4ZHpMsAGPNG16EF53z0GeMSaaFD/zU3B239QlmqXmLsAl8bpf8Bn93N0t2ABUvScw==
"@tiptap/extension-floating-menu@^2.1.6":
version "2.1.6"
resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.1.6.tgz#df5ddcafc70981028d8869bc0269f3bb850fdde4"
integrity sha512-Xy4esdjsZlgNxMbBC6+wLoiTfqaqFjuFquqcYEPqzgBizYa15Ww6wIx5+h2K+hzyJkSPI7ZX/rPjKXML8lNteQ==
"@tiptap/extension-floating-menu@^2.6.6":
version "2.6.6"
resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.6.6.tgz#37a16b966518b24fce021e2591669b470467c419"
integrity sha512-lPkESOfAUxgmXRiNqUU23WSyja5FUfSWjsW4hqe+BKNjsUt1OuFMEtYJtNc+MCGhhtPfFvM3Jg6g9jd6g5XsLQ==
dependencies:
tippy.js "^6.3.7"
"@tiptap/extension-hard-break@^2.0.3":
version "2.1.6"
resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.1.6.tgz#7ddd962b3d936267b6ea84c34f6b9369447be933"
integrity sha512-znFYceEFbrgxhHZF+/wNQlAn3MWG9/VRqQAFxPGne0csewibKZRwZbeSYZQ16x1vSAlAQsKhIaAst/na/2H8LA==
"@tiptap/extension-hard-break@^2.6.6":
version "2.6.6"
resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.6.6.tgz#cc31c641e1dbd37ff8fab3c6f66ac2f700d863f1"
integrity sha512-bsUuyYBrMDEiudx1dOQSr9MzKv13m0xHWrOK+DYxuIDYJb5g+c9un5cK7Js+et/HEYYSPOoH/iTW6h+4I5YeUg==
"@tiptap/extension-history@^2.0.3":
version "2.1.6"
resolved "https://registry.yarnpkg.com/@tiptap/extension-history/-/extension-history-2.1.6.tgz#799d7412c270e29b1fcaa08cd8343724b88eb6a6"
integrity sha512-ltHz9cW3bWi7Z3m960F5eLPAqZDBNOpUP31t9YdKqhyxA16eygryj1USVeus9DX5OBoW79I8EecFAuRo3Rymlw==
"@tiptap/extension-history@^2.6.6":
version "2.6.6"
resolved "https://registry.yarnpkg.com/@tiptap/extension-history/-/extension-history-2.6.6.tgz#8067d7f50ef529c84179eeb946e6f81af8853b90"
integrity sha512-tPTzAmPGqMX5Bd5H8lzRpmsaMvB9DvI5Dy2za/VQuFtxgXmDiFVgHRkRXIuluSkPTuANu84XBOQ0cBijqY8x4w==
"@tiptap/extension-mention@^2.0.0-beta.220":
version "2.1.6"
resolved "https://registry.yarnpkg.com/@tiptap/extension-mention/-/extension-mention-2.1.6.tgz#70ee72f917c11c6f2542e9cffc8afb38c3719749"
integrity sha512-GgoiCRhcpAv6wH7vHPFxa3f+vhiicGMqwJo+ZKT0VdyegHfHfMVRIN57sTV8R9/ZXCAjL1smqwLhF+PlWheN2A==
"@tiptap/extension-mention@^2.6.6":
version "2.6.6"
resolved "https://registry.yarnpkg.com/@tiptap/extension-mention/-/extension-mention-2.6.6.tgz#c46878bed263eb0ddb8f57b33530dd4ae7000920"
integrity sha512-fghNe4ZQRiZ7i3+sSrZx87zPZjaCwVtxn56/5UinoBUP/ZpCGwGtI+ErKhCBVyLW1fKyd0MmlihK/IGIeCBw1A==
"@tiptap/extension-paragraph@^2.0.0-beta.220":
version "2.1.6"
resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.1.6.tgz#c0b76f9242e823ccbeb1ba6af9df6a09ee971044"
integrity sha512-k0QSIaJPVgTn9+X2580JFCjV2RCH1Fo+gPodABDnjunfoUVSjuq0rlILEtTuha3evlS6kDKiz7lk7pIoCo36Cw==
"@tiptap/extension-paragraph@^2.6.6":
version "2.6.6"
resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.6.6.tgz#fb975b58e5055bb52e3df0fb9f996ed2407322e5"
integrity sha512-fD/onCr16UQWx+/xEmuFC2MccZZ7J5u4YaENh8LMnAnBXf78iwU7CAcmuc9rfAEO3qiLoYGXgLKiHlh2ZfD4wA==
"@tiptap/extension-placeholder@^2.0.0-beta.220":
version "2.1.6"
resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.1.6.tgz#86524a01c5b18de8932f4f5a34102603f1dacdfd"
integrity sha512-M6C80FnbDPiZWVGFIVVOUMbqNUMhXRzlJr7uwUWP98OJfj3Du4pk8mF5Lo5MsWH3C/XW3YRbqlGPpdas3onSkQ==
"@tiptap/extension-placeholder@^2.6.6":
version "2.6.6"
resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.6.6.tgz#a4e7a0150f3ab1c685cca75b2ee8f6fb73fbda1a"
integrity sha512-J0ZMvF93NsRrt+R7IQ3GhxNq32vq+88g25oV/YFJiwvC48HMu1tQB6kG1I3LJpu5b8lN+LnfANNqDOEhiBfjaA==
"@tiptap/extension-text@^2.0.0-beta.220":
version "2.1.6"
resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.1.6.tgz#23f36114ee164e3da2fd326145ac7b7f8bd34c56"
integrity sha512-CqV0N6ngoXZFeJGlQ86FSZJ/0k7+BN3S6aSUcb5DRAKsSEv/Ga1LvSG24sHy+dwjTuj3EtRPJSVZTFcSB17ZSA==
"@tiptap/extension-text@^2.6.6":
version "2.6.6"
resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.6.6.tgz#8851aa57e4fcd2194968dc64f5f5599ad858dbfa"
integrity sha512-e84uILnRzNzcwK1DVQNpXVmBG1Cq3BJipTOIDl1LHifOok7MBjhI/X+/NR0bd3N2t6gmDTWi63+4GuJ5EeDmsg==
"@tiptap/html@^2.1.11":
version "2.1.11"
resolved "https://registry.yarnpkg.com/@tiptap/html/-/html-2.1.11.tgz#998421b526f200d01c549f37eb8fae2a0d1f0ed6"
integrity sha512-VKmBb1c3YN9hZfBzkV+QERf3ZWBUHHxjv2/BOr/Dw6mbb6+0iA1nxO9vQYPUb+xAmlm0n8vWwc7YQ8rxBwTKWQ==
"@tiptap/html@^2.6.6":
version "2.6.6"
resolved "https://registry.yarnpkg.com/@tiptap/html/-/html-2.6.6.tgz#ac0c57d9ca7ac5429c3c60f19cf6828a1cd3aa29"
integrity sha512-OjS+rmu3jNTGbt0BR9pKVaK2w2y8dhnWOqqu4Fn7CKMJGD0HkDM+pYV/ks5ZU2TgTkPT6edosOantnrkvJJcmQ==
dependencies:
zeed-dom "^0.9.19"
zeed-dom "^0.10.9"
"@tiptap/pm@^2.0.0-beta.220":
version "2.1.6"
resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-2.1.6.tgz#4c196a7147fedd71316ef3413bb0e98d5c97726d"
integrity sha512-JkFlZp2z6Se2Ttnabi4lkP2yLNMH/eebO7ScYL1kXvwNLgELC/I3fwQVmnYA0E8pqJ5KQXOSl14NaB1mVPJqlg==
"@tiptap/pm@^2.6.6":
version "2.6.6"
resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-2.6.6.tgz#8ce1956f7fb0e222cc9399bffc81349c6b23bbc0"
integrity sha512-56FGLPn3fwwUlIbLs+BO21bYfyqP9fKyZQbQyY0zWwA/AG2kOwoXaRn7FOVbjP6CylyWpFJnpRRmgn694QKHEg==
dependencies:
prosemirror-changeset "^2.2.0"
prosemirror-collab "^1.3.0"
prosemirror-commands "^1.3.1"
prosemirror-dropcursor "^1.5.0"
prosemirror-gapcursor "^1.3.1"
prosemirror-history "^1.3.0"
prosemirror-inputrules "^1.2.0"
prosemirror-keymap "^1.2.0"
prosemirror-markdown "^1.10.1"
prosemirror-menu "^1.2.1"
prosemirror-model "^1.18.1"
prosemirror-schema-basic "^1.2.0"
prosemirror-schema-list "^1.2.2"
prosemirror-state "^1.4.1"
prosemirror-tables "^1.3.0"
prosemirror-trailing-node "^2.0.2"
prosemirror-transform "^1.7.0"
prosemirror-view "^1.28.2"
prosemirror-changeset "^2.2.1"
prosemirror-collab "^1.3.1"
prosemirror-commands "^1.5.2"
prosemirror-dropcursor "^1.8.1"
prosemirror-gapcursor "^1.3.2"
prosemirror-history "^1.4.1"
prosemirror-inputrules "^1.4.0"
prosemirror-keymap "^1.2.2"
prosemirror-markdown "^1.13.0"
prosemirror-menu "^1.2.4"
prosemirror-model "^1.22.2"
prosemirror-schema-basic "^1.2.3"
prosemirror-schema-list "^1.4.1"
prosemirror-state "^1.4.3"
prosemirror-tables "^1.4.0"
prosemirror-trailing-node "^2.0.9"
prosemirror-transform "^1.9.0"
prosemirror-view "^1.33.9"
"@tiptap/react@^2.0.0-beta.220":
version "2.1.6"
resolved "https://registry.yarnpkg.com/@tiptap/react/-/react-2.1.6.tgz#b4004fd564a48afa1cbe75f27d5e43fabc90276c"
integrity sha512-HEsoFlcE61gQz9TllEtBa+5d909MA/ersbxGYOUWIY2HhH5lvNIUvyJ3pdzMkK/4cSniMsDDqobFexsGyTAsrw==
"@tiptap/react@^2.6.6":
version "2.6.6"
resolved "https://registry.yarnpkg.com/@tiptap/react/-/react-2.6.6.tgz#661be83100c4a4206d7c933e6e0b66c91e7e9819"
integrity sha512-AUmdb/J1O/vCO2b8LL68ctcZr9a3931BwX4fUUZ1kCrCA5lTj2xz0rjeAtpxEdzLnR+Z7q96vB7vf7bPYOUAew==
dependencies:
"@tiptap/extension-bubble-menu" "^2.1.6"
"@tiptap/extension-floating-menu" "^2.1.6"
"@tiptap/extension-bubble-menu" "^2.6.6"
"@tiptap/extension-floating-menu" "^2.6.6"
"@types/use-sync-external-store" "^0.0.6"
use-sync-external-store "^1.2.2"
"@tiptap/suggestion@^2.0.0-beta.220":
version "2.1.6"
resolved "https://registry.yarnpkg.com/@tiptap/suggestion/-/suggestion-2.1.6.tgz#ae3c64f869454b802a8e94b358c33cd829eab8e0"
integrity sha512-8nMVARHbJ4Q9eeB7gmvqNommx6/RuFkrJEmmqxSrgyiqYEqb/if5ZTa1LGRWRNZYuzmeVN/r3eUu33jn+o5kJg==
"@tiptap/suggestion@^2.6.6":
version "2.6.6"
resolved "https://registry.yarnpkg.com/@tiptap/suggestion/-/suggestion-2.6.6.tgz#acb07c410db862b58d9118199ef5ff53d3608fd6"
integrity sha512-jogG0QgGit9UtTznVnhQfNImZfQM89NR0is20yRQzC0HmD8B8f3jmGrotG63Why2oKbeoe3CpM5/5eDE/paqCA==
"@tokenizer/token@^0.3.0":
version "0.3.0"
@@ -7362,16 +7338,6 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.17.6.tgz#0296e9a30b22d2a8fcaa48d3c45afe51474ca55b"
integrity sha512-fGmT/P7z7ecA6bv/ia5DlaWCH4YeZvAQMNpUhrJjtAhOhZfoxS1VLUgU2pdk63efSjQaOJWdXMuAJsws+8I6dg==
"@types/object.omit@^3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@types/object.omit/-/object.omit-3.0.0.tgz#0d31e1208eac8fe2ad5c9499a1016a8273bbfafc"
integrity sha512-I27IoPpH250TUzc9FzXd0P1BV/BMJuzqD3jOz98ehf9dQqGkxlq+hO1bIqZGWqCg5bVOy0g4AUVJtnxe0klDmw==
"@types/object.pick@^1.3.2":
version "1.3.2"
resolved "https://registry.yarnpkg.com/@types/object.pick/-/object.pick-1.3.2.tgz#9eb28118240ad8f658b9c9c6caf35359fdb37150"
integrity sha512-sn7L+qQ6RLPdXRoiaE7bZ/Ek+o4uICma/lBFPyJEKDTPTBP1W8u0c4baj3EiS4DiqLs+Hk+KUGvMVJtAw3ePJg==
"@types/parse-json@^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
@@ -7495,11 +7461,6 @@
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c"
integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==
"@types/throttle-debounce@^2.1.0":
version "2.1.0"
resolved "https://registry.yarnpkg.com/@types/throttle-debounce/-/throttle-debounce-2.1.0.tgz#1c3df624bfc4b62f992d3012b84c56d41eab3776"
integrity sha512-5eQEtSCoESnh2FsiLTxE121IiE60hnMqcb435fShf4bpLRjEu1Eoekht23y6zXS9Ts3l+Szu3TARnTsA0GkOkQ==
"@types/tough-cookie@*":
version "4.0.2"
resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.2.tgz#6286b4c7228d58ab7866d19716f3696e03a09397"
@@ -7510,6 +7471,11 @@
resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.3.tgz#a136f83b0758698df454e328759dbd3d44555311"
integrity sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==
"@types/use-sync-external-store@^0.0.6":
version "0.0.6"
resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz#60be8d21baab8c305132eb9cb912ed497852aadc"
integrity sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==
"@types/ws@^8.5.5":
version "8.5.5"
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.5.tgz#af587964aa06682702ee6dcbc7be41a80e4b28eb"
@@ -9068,11 +9034,6 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001517, caniuse-lite@^1.0.30001520, can
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001655.tgz"
integrity sha512-jRGVy3iSGO5Uutn2owlb5gR6qsGngTw9ZTb4ali9f3glshcNmJ2noam4Mo9zia5P9Dk3jNNydy7vQjuE5dQmfg==
case-anything@^2.1.13:
version "2.1.13"
resolved "https://registry.yarnpkg.com/case-anything/-/case-anything-2.1.13.tgz#0cdc16278cb29a7fcdeb072400da3f342ba329e9"
integrity sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng==
case-sensitive-paths-webpack-plugin@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz#db64066c6422eed2e08cc14b986ca43796dbc6d4"
@@ -9985,11 +9946,6 @@ damerau-levenshtein@^1.0.8:
resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7"
integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==
dash-get@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/dash-get/-/dash-get-1.0.2.tgz#4c9e9ad5ef04c4bf9d3c9a451f6f7997298dcc7c"
integrity sha512-4FbVrHDwfOASx7uQVxeiCTo7ggSdYZbqs8lH+WU6ViypPlDbe9y6IP5VVUDQBv9DcnyaiPT5XT0UWHgJ64zLeQ==
data-urls@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b"
@@ -10088,7 +10044,7 @@ deep-is@^0.1.3:
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
deepmerge@^4.2.2, deepmerge@^4.3.0, deepmerge@^4.3.1:
deepmerge@^4.2.2, deepmerge@^4.3.0:
version "4.3.1"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a"
integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==
@@ -10571,11 +10527,6 @@ entities@^4.2.0, entities@^4.4.0:
resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
entities@~3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4"
integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==
env-editor@^0.4.1:
version "0.4.2"
resolved "https://registry.yarnpkg.com/env-editor/-/env-editor-0.4.2.tgz#4e76568d0bd8f5c2b6d314a9412c8fe9aa3ae861"
@@ -12202,6 +12153,11 @@ functions-have-names@^1.2.2, functions-have-names@^1.2.3:
resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834"
integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==
fuse.js@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-7.0.0.tgz#6573c9fcd4c8268e403b4fc7d7131ffcf99a9eb2"
integrity sha512-14F4hBIxqKvD4Zz/XjDc3y94mNZN6pRv3U13Udo0lNLCWRBUsrMv2xwcF/y/Z5sV6+FQW+/ow68cHpm4sunt8Q==
gensync@^1.0.0-beta.2:
version "1.0.0-beta.2"
resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
@@ -13182,13 +13138,6 @@ is-docker@^2.0.0, is-docker@^2.1.1:
resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa"
integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==
is-extendable@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4"
integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==
dependencies:
is-plain-object "^2.0.4"
is-extglob@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0"
@@ -15047,12 +14996,12 @@ lines-and-columns@^1.1.6:
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
linkify-it@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-4.0.1.tgz#01f1d5e508190d06669982ba31a7d9f56a5751ec"
integrity sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==
linkify-it@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-5.0.0.tgz#9ef238bfa6dc70bd8e7f9572b52d369af569b421"
integrity sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==
dependencies:
uc.micro "^1.0.1"
uc.micro "^2.0.0"
lint-staged@^13.2.3:
version "13.3.0"
@@ -15334,7 +15283,7 @@ make-dir@^4.0.0:
dependencies:
semver "^7.5.3"
make-error@^1.1.1, make-error@^1.3.6:
make-error@^1.1.1:
version "1.3.6"
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
@@ -15346,16 +15295,17 @@ makeerror@1.0.12:
dependencies:
tmpl "1.0.5"
markdown-it@^13.0.1:
version "13.0.1"
resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-13.0.1.tgz#c6ecc431cacf1a5da531423fc6a42807814af430"
integrity sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==
markdown-it@^14.0.0:
version "14.1.0"
resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-14.1.0.tgz#3c3c5992883c633db4714ccb4d7b5935d98b7d45"
integrity sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==
dependencies:
argparse "^2.0.1"
entities "~3.0.1"
linkify-it "^4.0.1"
mdurl "^1.0.1"
uc.micro "^1.0.5"
entities "^4.4.0"
linkify-it "^5.0.0"
mdurl "^2.0.0"
punycode.js "^2.3.1"
uc.micro "^2.1.0"
marky@^1.2.2:
version "1.2.5"
@@ -15419,10 +15369,10 @@ mdn-data@2.0.4:
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b"
integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==
mdurl@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
integrity sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==
mdurl@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-2.0.0.tgz#80676ec0433025dd3e17ee983d0fe8de5a2237e0"
integrity sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==
media-typer@0.3.0:
version "0.3.0"
@@ -16267,20 +16217,6 @@ object.hasown@^1.1.2:
define-properties "^1.1.4"
es-abstract "^1.20.4"
object.omit@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-3.0.0.tgz#0e3edc2fce2ba54df5577ff529f6d97bd8a522af"
integrity sha512-EO+BCv6LJfu+gBIF3ggLicFebFLN5zqzz/WWJlMFfkMyGth+oBkhxzDl0wx2W4GkLzuQs/FsSkXZb2IMWQqmBQ==
dependencies:
is-extendable "^1.0.0"
object.pick@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747"
integrity sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==
dependencies:
isobject "^3.0.1"
object.values@^1.1.0, object.values@^1.1.6:
version "1.1.6"
resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d"
@@ -16826,6 +16762,13 @@ pify@^4.0.1:
resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231"
integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==
pind@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/pind/-/pind-0.5.0.tgz#9f04be810b0c91aac0bb0976a57ba1d76126d0c8"
integrity sha512-xK7GlJl2AeQQaYbtxoGOsGVa0Ms8M4B3CPIIohC2gmeDBRrxtBSWyzPc6cgdG4AEEvQETt//bIBETPy8VFO+BA==
dependencies:
tackjs "^3.0.1"
pinkie-promise@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
@@ -17709,30 +17652,30 @@ prop-types@^15.6.1, prop-types@^15.7.2, prop-types@^15.8.1:
object-assign "^4.1.1"
react-is "^16.13.1"
prosemirror-changeset@^2.2.0:
prosemirror-changeset@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/prosemirror-changeset/-/prosemirror-changeset-2.2.1.tgz#dae94b63aec618fac7bb9061648e6e2a79988383"
integrity sha512-J7msc6wbxB4ekDFj+n9gTW/jav/p53kdlivvuppHsrZXCaQdVgRghoZbSS3kwrRyAstRVQ4/+u5k7YfLgkkQvQ==
dependencies:
prosemirror-transform "^1.0.0"
prosemirror-collab@^1.3.0:
prosemirror-collab@^1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/prosemirror-collab/-/prosemirror-collab-1.3.1.tgz#0e8c91e76e009b53457eb3b3051fb68dad029a33"
integrity sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==
dependencies:
prosemirror-state "^1.0.0"
prosemirror-commands@^1.0.0, prosemirror-commands@^1.3.1:
version "1.5.2"
resolved "https://registry.yarnpkg.com/prosemirror-commands/-/prosemirror-commands-1.5.2.tgz#e94aeea52286f658cd984270de9b4c3fff580852"
integrity sha512-hgLcPaakxH8tu6YvVAaILV2tXYsW3rAdDR8WNkeKGcgeMVQg3/TMhPdVoh7iAmfgVjZGtcOSjKiQaoeKjzd2mQ==
prosemirror-commands@^1.0.0, prosemirror-commands@^1.5.2:
version "1.6.0"
resolved "https://registry.yarnpkg.com/prosemirror-commands/-/prosemirror-commands-1.6.0.tgz#b79f034ed371576e7bf83ddd4ede689c8ccbd9ab"
integrity sha512-xn1U/g36OqXn2tn5nGmvnnimAj/g1pUx2ypJJIe8WkVX83WyJVC5LTARaxZa2AtQRwntu9Jc5zXs9gL9svp/mg==
dependencies:
prosemirror-model "^1.0.0"
prosemirror-state "^1.0.0"
prosemirror-transform "^1.0.0"
prosemirror-dropcursor@^1.5.0:
prosemirror-dropcursor@^1.8.1:
version "1.8.1"
resolved "https://registry.yarnpkg.com/prosemirror-dropcursor/-/prosemirror-dropcursor-1.8.1.tgz#49b9fb2f583e0d0f4021ff87db825faa2be2832d"
integrity sha512-M30WJdJZLyXHi3N8vxN6Zh5O8ZBbQCz0gURTfPmTIBNQ5pxrdU7A58QkNqfa98YEjSAL1HUyyU34f6Pm5xBSGw==
@@ -17741,7 +17684,7 @@ prosemirror-dropcursor@^1.5.0:
prosemirror-transform "^1.1.0"
prosemirror-view "^1.1.0"
prosemirror-gapcursor@^1.3.1:
prosemirror-gapcursor@^1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/prosemirror-gapcursor/-/prosemirror-gapcursor-1.3.2.tgz#5fa336b83789c6199a7341c9493587e249215cb4"
integrity sha512-wtjswVBd2vaQRrnYZaBCbyDqr232Ed4p2QPtRIUK5FuqHYKGWkEwl08oQM4Tw7DOR0FsasARV5uJFvMZWxdNxQ==
@@ -17751,25 +17694,25 @@ prosemirror-gapcursor@^1.3.1:
prosemirror-state "^1.0.0"
prosemirror-view "^1.0.0"
prosemirror-history@^1.0.0, prosemirror-history@^1.3.0:
version "1.3.2"
resolved "https://registry.yarnpkg.com/prosemirror-history/-/prosemirror-history-1.3.2.tgz#ce6ad7ab9db83e761aee716f3040d74738311b15"
integrity sha512-/zm0XoU/N/+u7i5zepjmZAEnpvjDtzoPWW6VmKptcAnPadN/SStsBjMImdCEbb3seiNTpveziPTIrXQbHLtU1g==
prosemirror-history@^1.0.0, prosemirror-history@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/prosemirror-history/-/prosemirror-history-1.4.1.tgz#cc370a46fb629e83a33946a0e12612e934ab8b98"
integrity sha512-2JZD8z2JviJrboD9cPuX/Sv/1ChFng+xh2tChQ2X4bB2HeK+rra/bmJ3xGntCcjhOqIzSDG6Id7e8RJ9QPXLEQ==
dependencies:
prosemirror-state "^1.2.2"
prosemirror-transform "^1.0.0"
prosemirror-view "^1.31.0"
rope-sequence "^1.3.0"
prosemirror-inputrules@^1.2.0:
version "1.2.1"
resolved "https://registry.yarnpkg.com/prosemirror-inputrules/-/prosemirror-inputrules-1.2.1.tgz#8faf3d78c16150aedac71d326a3e3947417ce557"
integrity sha512-3LrWJX1+ULRh5SZvbIQlwZafOXqp1XuV21MGBu/i5xsztd+9VD15x6OtN6mdqSFI7/8Y77gYUbQ6vwwJ4mr6QQ==
prosemirror-inputrules@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/prosemirror-inputrules/-/prosemirror-inputrules-1.4.0.tgz#ef1519bb2cb0d1e0cec74bad1a97f1c1555068bb"
integrity sha512-6ygpPRuTJ2lcOXs9JkefieMst63wVJBgHZGl5QOytN7oSZs3Co/BYbc3Yx9zm9H37Bxw8kVzCnDsihsVsL4yEg==
dependencies:
prosemirror-state "^1.0.0"
prosemirror-transform "^1.0.0"
prosemirror-keymap@^1.0.0, prosemirror-keymap@^1.1.2, prosemirror-keymap@^1.2.0:
prosemirror-keymap@^1.0.0, prosemirror-keymap@^1.1.2, prosemirror-keymap@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/prosemirror-keymap/-/prosemirror-keymap-1.2.2.tgz#14a54763a29c7b2704f561088ccf3384d14eb77e"
integrity sha512-EAlXoksqC6Vbocqc0GtzCruZEzYgrn+iiGnNjsJsH4mrnIGex4qbLdWWNza3AW5W36ZRrlBID0eM6bdKH4OStQ==
@@ -17777,15 +17720,15 @@ prosemirror-keymap@^1.0.0, prosemirror-keymap@^1.1.2, prosemirror-keymap@^1.2.0:
prosemirror-state "^1.0.0"
w3c-keyname "^2.2.0"
prosemirror-markdown@^1.10.1:
version "1.11.2"
resolved "https://registry.yarnpkg.com/prosemirror-markdown/-/prosemirror-markdown-1.11.2.tgz#f6e529e669d11fa3eec859e93c0d2c91788d6c80"
integrity sha512-Eu5g4WPiCdqDTGhdSsG9N6ZjACQRYrsAkrF9KYfdMaCmjIApH75aVncsWYOJvEk2i1B3i8jZppv3J/tnuHGiUQ==
prosemirror-markdown@^1.13.0:
version "1.13.0"
resolved "https://registry.yarnpkg.com/prosemirror-markdown/-/prosemirror-markdown-1.13.0.tgz#67ebfa40af48a22d1e4ed6cad2e29851eb61e649"
integrity sha512-UziddX3ZYSYibgx8042hfGKmukq5Aljp2qoBiJRejD/8MH70siQNz5RB1TrdTPheqLMy4aCe4GYNF10/3lQS5g==
dependencies:
markdown-it "^13.0.1"
prosemirror-model "^1.0.0"
markdown-it "^14.0.0"
prosemirror-model "^1.20.0"
prosemirror-menu@^1.2.1:
prosemirror-menu@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/prosemirror-menu/-/prosemirror-menu-1.2.4.tgz#3cfdc7c06d10f9fbd1bce29082c498bd11a0a79a"
integrity sha512-S/bXlc0ODQup6aiBbWVsX/eM+xJgCTAfMq/nLqaO5ID/am4wS0tTCIkzwytmao7ypEtjj39i7YbJjAgO20mIqA==
@@ -17795,30 +17738,30 @@ prosemirror-menu@^1.2.1:
prosemirror-history "^1.0.0"
prosemirror-state "^1.0.0"
prosemirror-model@^1.0.0, prosemirror-model@^1.16.0, prosemirror-model@^1.18.1, prosemirror-model@^1.19.0, prosemirror-model@^1.8.1:
version "1.19.3"
resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.19.3.tgz#f0d55285487fefd962d0ac695f716f4ec6705006"
integrity sha512-tgSnwN7BS7/UM0sSARcW+IQryx2vODKX4MI7xpqY2X+iaepJdKBPc7I4aACIsDV/LTaTjt12Z56MhDr9LsyuZQ==
prosemirror-model@^1.0.0, prosemirror-model@^1.19.0, prosemirror-model@^1.20.0, prosemirror-model@^1.21.0, prosemirror-model@^1.22.2, prosemirror-model@^1.8.1:
version "1.22.3"
resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.22.3.tgz#52fdf5897f348b0f07f64bea89156d90afdf645a"
integrity sha512-V4XCysitErI+i0rKFILGt/xClnFJaohe/wrrlT2NSZ+zk8ggQfDH4x2wNK7Gm0Hp4CIoWizvXFP7L9KMaCuI0Q==
dependencies:
orderedmap "^2.0.0"
prosemirror-schema-basic@^1.2.0:
version "1.2.2"
resolved "https://registry.yarnpkg.com/prosemirror-schema-basic/-/prosemirror-schema-basic-1.2.2.tgz#6695f5175e4628aab179bf62e5568628b9cfe6c7"
integrity sha512-/dT4JFEGyO7QnNTe9UaKUhjDXbTNkiWTq/N4VpKaF79bBjSExVV2NXmJpcM7z/gD7mbqNjxbmWW5nf1iNSSGnw==
prosemirror-schema-basic@^1.2.3:
version "1.2.3"
resolved "https://registry.yarnpkg.com/prosemirror-schema-basic/-/prosemirror-schema-basic-1.2.3.tgz#649c349bb21c61a56febf9deb71ac68fca4cedf2"
integrity sha512-h+H0OQwZVqMon1PNn0AG9cTfx513zgIG2DY00eJ00Yvgb3UD+GQ/VlWW5rcaxacpCGT1Yx8nuhwXk4+QbXUfJA==
dependencies:
prosemirror-model "^1.19.0"
prosemirror-schema-list@^1.2.2:
version "1.3.0"
resolved "https://registry.yarnpkg.com/prosemirror-schema-list/-/prosemirror-schema-list-1.3.0.tgz#05374702cf35a3ba5e7ec31079e355a488d52519"
integrity sha512-Hz/7gM4skaaYfRPNgr421CU4GSwotmEwBVvJh5ltGiffUJwm7C8GfN/Bc6DR1EKEp5pDKhODmdXXyi9uIsZl5A==
prosemirror-schema-list@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/prosemirror-schema-list/-/prosemirror-schema-list-1.4.1.tgz#78b8d25531db48ca9688836dbde50e13ac19a4a1"
integrity sha512-jbDyaP/6AFfDfu70VzySsD75Om2t3sXTOdl5+31Wlxlg62td1haUpty/ybajSfJ1pkGadlOfwQq9kgW5IMo1Rg==
dependencies:
prosemirror-model "^1.0.0"
prosemirror-state "^1.0.0"
prosemirror-transform "^1.7.3"
prosemirror-state@^1.0.0, prosemirror-state@^1.2.2, prosemirror-state@^1.3.1, prosemirror-state@^1.4.1:
prosemirror-state@^1.0.0, prosemirror-state@^1.2.2, prosemirror-state@^1.3.1, prosemirror-state@^1.4.3:
version "1.4.3"
resolved "https://registry.yarnpkg.com/prosemirror-state/-/prosemirror-state-1.4.3.tgz#94aecf3ffd54ec37e87aa7179d13508da181a080"
integrity sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q==
@@ -17827,10 +17770,10 @@ prosemirror-state@^1.0.0, prosemirror-state@^1.2.2, prosemirror-state@^1.3.1, pr
prosemirror-transform "^1.0.0"
prosemirror-view "^1.27.0"
prosemirror-tables@^1.3.0:
version "1.3.4"
resolved "https://registry.yarnpkg.com/prosemirror-tables/-/prosemirror-tables-1.3.4.tgz#0b7cc16d49f90c5b834c9f29291c545478ce9ab0"
integrity sha512-z6uLSQ1BLC3rgbGwZmpfb+xkdvD7W/UOsURDfognZFYaTtc0gsk7u/t71Yijp2eLflVpffMk6X0u0+u+MMDvIw==
prosemirror-tables@^1.4.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/prosemirror-tables/-/prosemirror-tables-1.5.0.tgz#3ba1ea3d53852505cc0d2037ce386973bb639a7d"
integrity sha512-VMx4zlYWm7aBlZ5xtfJHpqa3Xgu3b7srV54fXYnXgsAcIGRqKSrhiK3f89omzzgaAgAtDOV4ImXnLKhVfheVNQ==
dependencies:
prosemirror-keymap "^1.1.2"
prosemirror-model "^1.8.1"
@@ -17838,28 +17781,27 @@ prosemirror-tables@^1.3.0:
prosemirror-transform "^1.2.1"
prosemirror-view "^1.13.3"
prosemirror-trailing-node@^2.0.2:
version "2.0.7"
resolved "https://registry.yarnpkg.com/prosemirror-trailing-node/-/prosemirror-trailing-node-2.0.7.tgz#ba782a7929f18bcae650b1c7082a2d10443eab19"
integrity sha512-8zcZORYj/8WEwsGo6yVCRXFMOfBo0Ub3hCUvmoWIZYfMP26WqENU0mpEP27w7mt8buZWuGrydBewr0tOArPb1Q==
prosemirror-trailing-node@^2.0.9:
version "2.0.9"
resolved "https://registry.yarnpkg.com/prosemirror-trailing-node/-/prosemirror-trailing-node-2.0.9.tgz#a087e6d1372e888cd3e57c977507b6b85dc658e4"
integrity sha512-YvyIn3/UaLFlFKrlJB6cObvUhmwFNZVhy1Q8OpW/avoTbD/Y7H5EcjK4AZFKhmuS6/N6WkGgt7gWtBWDnmFvHg==
dependencies:
"@remirror/core-constants" "^2.0.2"
"@remirror/core-helpers" "^3.0.0"
escape-string-regexp "^4.0.0"
prosemirror-transform@^1.0.0, prosemirror-transform@^1.1.0, prosemirror-transform@^1.2.1, prosemirror-transform@^1.7.0, prosemirror-transform@^1.7.3:
version "1.7.4"
resolved "https://registry.yarnpkg.com/prosemirror-transform/-/prosemirror-transform-1.7.4.tgz#ea878c90563f3586064dd5ccf6cabb50b2753fd9"
integrity sha512-GO38mvqJ2yeI0BbL5E1CdHcly032Dlfn9nHqlnCHqlNf9e9jZwJixxp6VRtOeDZ1uTDpDIziezMKbA41LpAx3A==
prosemirror-transform@^1.0.0, prosemirror-transform@^1.1.0, prosemirror-transform@^1.2.1, prosemirror-transform@^1.7.3, prosemirror-transform@^1.9.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/prosemirror-transform/-/prosemirror-transform-1.10.0.tgz#2211ddcb13f12e4e530de97c077f82ab46177b0c"
integrity sha512-9UOgFSgN6Gj2ekQH5CTDJ8Rp/fnKR2IkYfGdzzp5zQMFsS4zDllLVx/+jGcX86YlACpG7UR5fwAXiWzxqWtBTg==
dependencies:
prosemirror-model "^1.0.0"
prosemirror-model "^1.21.0"
prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.13.3, prosemirror-view@^1.27.0, prosemirror-view@^1.28.2, prosemirror-view@^1.31.0:
version "1.31.7"
resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.31.7.tgz#dccb2879314e1e1a24d48044c15374754e50ef00"
integrity sha512-Pr7w93yOYmxQwzGIRSaNLZ/1uM6YjnenASzN2H6fO6kGekuzRbgZ/4bHbBTd1u4sIQmL33/TcGmzxxidyPwCjg==
prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.13.3, prosemirror-view@^1.27.0, prosemirror-view@^1.31.0, prosemirror-view@^1.33.9:
version "1.34.2"
resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.34.2.tgz#239a1766e46e364666e2b850855361929c4236df"
integrity sha512-tPX/V2Xd70vrAGQ/V9CppJtPKnQyQMypJGlLylvdI94k6JaG+4P6fVmXPR1zc1eVTW0gq3c6zsfqwJKCRLaG9Q==
dependencies:
prosemirror-model "^1.16.0"
prosemirror-model "^1.20.0"
prosemirror-state "^1.0.0"
prosemirror-transform "^1.1.0"
@@ -17896,6 +17838,11 @@ pump@^3.0.0:
end-of-stream "^1.1.0"
once "^1.3.1"
punycode.js@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/punycode.js/-/punycode.js-2.3.1.tgz#6b53e56ad75588234e79f4affa90972c7dd8cdb7"
integrity sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==
punycode@^2.1.0, punycode@^2.1.1:
version "2.3.0"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f"
@@ -20094,6 +20041,11 @@ symbol-tree@^3.2.4:
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==
tackjs@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/tackjs/-/tackjs-3.0.1.tgz#d69af48df5921320953d28131a8ae110ae11ea06"
integrity sha512-a/0vc4RWqG/9mYeldQ9uhGGTicc4LCr0EPpjV0IIKB21QkmFfkN559wu/Z4rprOzWDnEjUzdwsqIIrQC1H+rpg==
tailwindcss@^3.0.2:
version "3.3.3"
resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.3.3.tgz#90da807393a2859189e48e9e7000e6880a736daf"
@@ -20330,11 +20282,6 @@ throat@^6.0.1:
resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.2.tgz#51a3fbb5e11ae72e2cf74861ed5c8020f89f29fe"
integrity sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==
throttle-debounce@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-3.0.1.tgz#32f94d84dfa894f786c9a1f290e7a645b6a19abb"
integrity sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==
through2@^2.0.1:
version "2.0.5"
resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd"
@@ -20580,7 +20527,7 @@ type-fest@^1.0.2:
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-1.4.0.tgz#e9fb813fe3bf1744ec359d55d1affefa76f14be1"
integrity sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==
type-fest@^2.19.0, type-fest@^2.3.3:
type-fest@^2.3.3:
version "2.19.0"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b"
integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==
@@ -20671,10 +20618,10 @@ ua-parser-js@^1.0.35:
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.35.tgz#c4ef44343bc3db0a3cbefdf21822f1b1fc1ab011"
integrity sha512-fKnGuqmTBnIE+/KXSzCn4db8RTigUzw1AN0DmdU6hJovUTbYJKyqj+8Mt1c4VfRDnOVJnENmfYkIPZ946UrSAA==
uc.micro@^1.0.1, uc.micro@^1.0.5:
version "1.0.6"
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"
integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==
uc.micro@^2.0.0, uc.micro@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-2.1.0.tgz#f8d3f7d0ec4c3dea35a7e3c8efa4cb8b45c9e7ee"
integrity sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==
uglify-js@^3.1.4:
version "3.17.4"
@@ -20906,6 +20853,11 @@ use-sidecar@^1.1.2:
detect-node-es "^1.1.0"
tslib "^2.0.0"
use-sync-external-store@^1.2.2:
version "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:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
@@ -21845,7 +21797,7 @@ yocto-queue@^1.0.0:
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251"
integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==
zeed-dom@0.10.9, zeed-dom@^0.9.19:
zeed-dom@0.10.9, zeed-dom@^0.10.9:
version "0.10.9"
resolved "https://registry.yarnpkg.com/zeed-dom/-/zeed-dom-0.10.9.tgz#b3eb5d9b7cf1be17e1fb3a708379df5edce195be"
integrity sha512-qQQ7Wu7IJ3Vo/LjeKWj97A2Hi17di4ZdmgNZj6AWbDbpt3hvO4EMfjYVA2/2unLYT+XpmMq5fqaLqCeU7Im83A==