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:
+14
-12
@@ -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,
|
||||
},
|
||||
})
|
||||
@@ -111,6 +111,7 @@ export async function post(
|
||||
embed,
|
||||
langs,
|
||||
labels,
|
||||
tags: draft.tags,
|
||||
})
|
||||
} catch (e: any) {
|
||||
logger.error(`Failed to create post`, {
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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==
|
||||
|
||||
Reference in New Issue
Block a user