diff --git a/frontend/__snapshots__/lemon-ui-emoji-picker-popover--default--dark.png b/frontend/__snapshots__/lemon-ui-emoji-picker-popover--default--dark.png new file mode 100644 index 000000000000..4175a85d09e7 Binary files /dev/null and b/frontend/__snapshots__/lemon-ui-emoji-picker-popover--default--dark.png differ diff --git a/frontend/__snapshots__/lemon-ui-emoji-picker-popover--default--light.png b/frontend/__snapshots__/lemon-ui-emoji-picker-popover--default--light.png new file mode 100644 index 000000000000..1fe0368f09e6 Binary files /dev/null and b/frontend/__snapshots__/lemon-ui-emoji-picker-popover--default--light.png differ diff --git a/frontend/__snapshots__/lemon-ui-emoji-picker-popover--open--dark.png b/frontend/__snapshots__/lemon-ui-emoji-picker-popover--open--dark.png new file mode 100644 index 000000000000..87d8938c0ede Binary files /dev/null and b/frontend/__snapshots__/lemon-ui-emoji-picker-popover--open--dark.png differ diff --git a/frontend/__snapshots__/lemon-ui-emoji-picker-popover--open--light.png b/frontend/__snapshots__/lemon-ui-emoji-picker-popover--open--light.png new file mode 100644 index 000000000000..c0b458162a75 Binary files /dev/null and b/frontend/__snapshots__/lemon-ui-emoji-picker-popover--open--light.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-text-area-markdown--empty-lemon-text-markdown--dark.png b/frontend/__snapshots__/lemon-ui-lemon-text-area-markdown--empty-lemon-text-markdown--dark.png index 7f6ca6c71e26..e75098a177c4 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-text-area-markdown--empty-lemon-text-markdown--dark.png and b/frontend/__snapshots__/lemon-ui-lemon-text-area-markdown--empty-lemon-text-markdown--dark.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-text-area-markdown--empty-lemon-text-markdown--light.png b/frontend/__snapshots__/lemon-ui-lemon-text-area-markdown--empty-lemon-text-markdown--light.png index 73e88188eda7..75349e8c1a4d 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-text-area-markdown--empty-lemon-text-markdown--light.png and b/frontend/__snapshots__/lemon-ui-lemon-text-area-markdown--empty-lemon-text-markdown--light.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-text-area-markdown--lemon-text-markdown-with-max-length--dark.png b/frontend/__snapshots__/lemon-ui-lemon-text-area-markdown--lemon-text-markdown-with-max-length--dark.png index 4c94f3526911..81628e334c23 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-text-area-markdown--lemon-text-markdown-with-max-length--dark.png and b/frontend/__snapshots__/lemon-ui-lemon-text-area-markdown--lemon-text-markdown-with-max-length--dark.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-text-area-markdown--lemon-text-markdown-with-max-length--light.png b/frontend/__snapshots__/lemon-ui-lemon-text-area-markdown--lemon-text-markdown-with-max-length--light.png index 7d34ce4a98a2..5654af4bd78f 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-text-area-markdown--lemon-text-markdown-with-max-length--light.png and b/frontend/__snapshots__/lemon-ui-lemon-text-area-markdown--lemon-text-markdown-with-max-length--light.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-text-area-markdown--lemon-text-markdown-with-text--dark.png b/frontend/__snapshots__/lemon-ui-lemon-text-area-markdown--lemon-text-markdown-with-text--dark.png index d35e6283f6f1..e716c78cd776 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-text-area-markdown--lemon-text-markdown-with-text--dark.png and b/frontend/__snapshots__/lemon-ui-lemon-text-area-markdown--lemon-text-markdown-with-text--dark.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-text-area-markdown--lemon-text-markdown-with-text--light.png b/frontend/__snapshots__/lemon-ui-lemon-text-area-markdown--lemon-text-markdown-with-text--light.png index 4ea2310b4973..149d8f45bb16 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-text-area-markdown--lemon-text-markdown-with-text--light.png and b/frontend/__snapshots__/lemon-ui-lemon-text-area-markdown--lemon-text-markdown-with-text--light.png differ diff --git a/frontend/package.json b/frontend/package.json index 0e599c991195..a65253a9d44c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -147,6 +147,7 @@ "fast-deep-equal": "^3.1.3", "fastpriorityqueue": "^0.7.5", "fflate": "^0.7.4", + "frimousse": "^0.3.0", "fuse.js": "^6.6.2", "heatmap.js": "^2.0.5", "hls.js": "^1.5.15", diff --git a/frontend/src/lib/components/EmojiPicker/EmojiPickerPopover.stories.tsx b/frontend/src/lib/components/EmojiPicker/EmojiPickerPopover.stories.tsx new file mode 100644 index 000000000000..c8b1f06101a7 --- /dev/null +++ b/frontend/src/lib/components/EmojiPicker/EmojiPickerPopover.stories.tsx @@ -0,0 +1,42 @@ +import { Meta, StoryFn, StoryObj } from '@storybook/react' + +import { EmojiPickerPopover } from 'lib/components/EmojiPicker/EmojiPickerPopover' + +type Story = StoryObj +const meta: Meta = { + title: 'Lemon UI/Emoji Picker Popover', + component: EmojiPickerPopover, + tags: ['autodocs'], + parameters: { + docs: { + description: { + component: 'A component that opens a popover emoji picker.', + }, + }, + }, + argTypes: { + onSelect: { + description: 'The function to run when a user chooses an emoji', + }, + defaultOpen: { + description: 'Whether to start with the popover open - defaults to false', + }, + }, +} +export default meta + +const BasicTemplate: StoryFn = (props) => { + return ( +
+ +
+ ) +} + +export const Default: Story = BasicTemplate.bind({}) +Default.args = {} + +export const Open: Story = BasicTemplate.bind({}) +Open.args = { + defaultOpen: true, +} diff --git a/frontend/src/lib/components/EmojiPicker/EmojiPickerPopover.tsx b/frontend/src/lib/components/EmojiPicker/EmojiPickerPopover.tsx new file mode 100644 index 000000000000..f447d10cbb47 --- /dev/null +++ b/frontend/src/lib/components/EmojiPicker/EmojiPickerPopover.tsx @@ -0,0 +1,102 @@ +import { + EmojiPicker, + EmojiPickerListCategoryHeaderProps, + EmojiPickerListEmojiProps, + EmojiPickerListRowProps, +} from 'frimousse' +import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { Popover } from 'lib/lemon-ui/Popover' +import { useState } from 'react' +import { IconEmojiAdd } from '@posthog/icons' + +const EmojiPickerCategoryHeader = ({ category, ...props }: EmojiPickerListCategoryHeaderProps): JSX.Element => ( +
+ {category.label} +
+) + +const EmojiPickerEmojiRow = ({ children, ...props }: EmojiPickerListRowProps): JSX.Element => ( +
+ {children} +
+) + +const EmojiPickerEmojiButton = ({ emoji, ...props }: EmojiPickerListEmojiProps): JSX.Element => ( + +) + +export interface EmojiPickerPopoverProps { + /** + * The action to take when a user selects an emoji + * receives the emoji as a string + */ + onSelect: (s: string) => void + /** + * Whether to start with the popover open or closed + * Defaults to false (closed) + */ + defaultOpen?: boolean + /** + * the data-attr to set on the button that opens and closes the popover + */ + 'data-attr'?: string +} + +export function EmojiPickerPopover({ + onSelect, + defaultOpen = false, + 'data-attr': dataAttr, +}: EmojiPickerPopoverProps): JSX.Element { + const [emojiPickerOpen, setEmojiPickerOpen] = useState(defaultOpen) + + return ( + setEmojiPickerOpen(false)} + // prefer the bottom, but will fall back to other positions based on space + placement="bottom-start" + visible={emojiPickerOpen} + overlay={ + { + onSelect(emoji) + setEmojiPickerOpen(false) + }} + > + + + + Loading… + + + No emoji found. + + + + + } + > + } + onClick={() => { + setEmojiPickerOpen(!emojiPickerOpen) + }} + size="small" + /> + + ) +} diff --git a/frontend/src/lib/lemon-ui/LemonTextArea/LemonTextArea.tsx b/frontend/src/lib/lemon-ui/LemonTextArea/LemonTextArea.tsx index a77dd1bb303b..6ec0a83ca908 100644 --- a/frontend/src/lib/lemon-ui/LemonTextArea/LemonTextArea.tsx +++ b/frontend/src/lib/lemon-ui/LemonTextArea/LemonTextArea.tsx @@ -111,7 +111,7 @@ export const LemonTextArea = React.forwardRef {hasFooter ? (
-
{actions}
+
{actions}
{rightFooter} diff --git a/frontend/src/lib/lemon-ui/LemonTextArea/LemonTextAreaMarkdown.tsx b/frontend/src/lib/lemon-ui/LemonTextArea/LemonTextAreaMarkdown.tsx index a00890f54fe2..d38fad8ebadd 100644 --- a/frontend/src/lib/lemon-ui/LemonTextArea/LemonTextAreaMarkdown.tsx +++ b/frontend/src/lib/lemon-ui/LemonTextArea/LemonTextAreaMarkdown.tsx @@ -1,7 +1,8 @@ -import { useValues } from 'kea' +import { useActions, useValues } from 'kea' import { TextContent } from 'lib/components/Cards/TextCard/TextCard' import { useUploadFiles } from 'lib/hooks/useUploadFiles' -import { IconMarkdown, IconUploadFile } from 'lib/lemon-ui/icons' +import { IconMarkdown } from 'lib/lemon-ui/icons' +import { IconImage } from '@posthog/icons' import { LemonFileInput } from 'lib/lemon-ui/LemonFileInput' import { LemonTabs } from 'lib/lemon-ui/LemonTabs' import { LemonTextArea, LemonTextAreaProps } from 'lib/lemon-ui/LemonTextArea/LemonTextArea' @@ -10,10 +11,14 @@ import { Tooltip } from 'lib/lemon-ui/Tooltip' import posthog from 'posthog-js' import React, { useRef, useState } from 'react' import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' +import { EmojiPickerPopover } from 'lib/components/EmojiPicker/EmojiPickerPopover' +import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { emojiUsageLogic } from 'lib/lemon-ui/LemonTextArea/emojiUsageLogic' export const LemonTextAreaMarkdown = React.forwardRef( function LemonTextAreaMarkdown({ value, onChange, className, ...editAreaProps }, ref): JSX.Element { const { objectStorageAvailable } = useValues(preflightLogic) + const { emojiUsed } = useActions(emojiUsageLogic) const [isPreviewShown, setIsPreviewShown] = useState(false) const dropRef = useRef(null) @@ -65,23 +70,46 @@ export const LemonTextAreaMarkdown = React.forwardRef -
- {' '} - -
- - ) : ( - -
- {' '} - -
-
- ) + } + disabledReason={ + objectStorageAvailable + ? undefined + : 'Enable object storage to add images by dragging and dropping' + } + tooltip={ + objectStorageAvailable + ? 'Click here or drag and drop to upload images' + : null + } + /> } />, + { + if (ref && 'current' in ref && ref.current) { + const textArea = ref.current + const cursorStart = textArea.selectionStart || 0 + const cursorEnd = textArea.selectionEnd || 0 + const textBefore = (value || '').slice(0, cursorStart) + const textAfter = (value || '').slice(cursorEnd) + const spaceBefore = textBefore.endsWith(' ') ? '' : ' ' + const spaceAfter = textAfter.startsWith(' ') ? '' : ' ' + const newValue = + textBefore + spaceBefore + emoji + spaceAfter + textAfter + onChange?.(newValue) + // Restore cursor position after the inserted emoji + setTimeout(() => { + textArea.selectionStart = textArea.selectionEnd = + cursorStart + spaceBefore.length + emoji.length + }, 0) + } + emojiUsed(emoji) + }} + />, ]} />
diff --git a/frontend/src/lib/lemon-ui/LemonTextArea/emojiUsageLogic.ts b/frontend/src/lib/lemon-ui/LemonTextArea/emojiUsageLogic.ts new file mode 100644 index 000000000000..cf2579b34027 --- /dev/null +++ b/frontend/src/lib/lemon-ui/LemonTextArea/emojiUsageLogic.ts @@ -0,0 +1,70 @@ +import { actions, kea, reducers, path, selectors } from 'kea' +import { now } from 'lib/dayjs' + +import { permanentlyMount } from 'lib/utils/kea-logic-builders' + +import type { emojiUsageLogicType } from './emojiUsageLogicType' + +export const defaultQuickEmojis = ['πŸ’–', 'πŸ‘', 'πŸ€”', 'πŸ‘Ž', '🌢️'] + +export const emojiUsageLogic = kea([ + path(['lib', 'lemon-ui', 'LemonTextArea', 'emojiUsage', 'logic']), + actions({ + emojiUsed: (emoji: string) => ({ emoji }), + }), + reducers({ + usedEmojis: [ + {} as Record, + { persist: true }, + { + emojiUsed: (state, { emoji }) => { + const currentTime = now().valueOf() + const thirtyDaysAgo = currentTime - 30 * 24 * 60 * 60 * 1000 + + const newState = { ...state, [emoji]: state[emoji] || [] } + newState[emoji] = [...newState[emoji], currentTime] + + newState[emoji] = newState[emoji].filter((timestamp: number) => timestamp > thirtyDaysAgo) + + if (newState[emoji].length === 0) { + delete newState[emoji] + } else { + // Limit to max 10 timestamps per emoji to prevent memory issues + if (newState[emoji].length > 10) { + newState[emoji] = newState[emoji].slice(-10) + } + } + + return newState + }, + }, + ], + }), + selectors({ + favouriteEmojis: [ + (s) => [s.usedEmojis], + (usedEmojis): string[] => { + // Get user's favorite emojis sorted by usage count + const userFavorites = Object.entries(usedEmojis) + .map(([emoji, timestamps]) => ({ + emoji, + count: timestamps.length, + })) + .sort((a, b) => b.count - a.count) // Sort by usage count descending + .slice(0, 5) // Take top 5 + .map(({ emoji }) => emoji) // Extract just the emoji strings + + // If we have fewer than 5 favorites, fill with quickEmojis (avoiding duplicates) + if (userFavorites.length < 5) { + const remainingSlots = 5 - userFavorites.length + const availableQuickEmojis = defaultQuickEmojis.filter((emoji) => !userFavorites.includes(emoji)) + const fillEmojis = availableQuickEmojis.slice(0, remainingSlots) + return [...userFavorites, ...fillEmojis] + } + + return userFavorites + }, + ], + }), + permanentlyMount(), +]) diff --git a/frontend/src/lib/lemon-ui/icons/categories.ts b/frontend/src/lib/lemon-ui/icons/categories.ts index 938610bb6c6a..667dc46898b6 100644 --- a/frontend/src/lib/lemon-ui/icons/categories.ts +++ b/frontend/src/lib/lemon-ui/icons/categories.ts @@ -109,6 +109,7 @@ export const TECHNOLOGY = { 'IconChat', 'IconThoughtBubble', 'IconChatHelp', + 'IconComment', ], Hardware: [ 'IconCompass', @@ -167,6 +168,7 @@ export const TECHNOLOGY = { 'IconMouseScrollDown', 'IconDrag', 'IconPointer', + 'IconImage', ], } @@ -191,6 +193,7 @@ export const ELEMENTS = { 'IconSort', 'IconSortAlpha', 'IconExternal', + 'IconEmojiAdd', ], Symbols: [ 'IconLock', @@ -209,6 +212,7 @@ export const ELEMENTS = { 'IconHide', 'IconStopFilled', 'IconListCheck', + 'IconEmoji', ], 'Arrows & Shapes': [ 'IconArrowLeft', @@ -288,7 +292,7 @@ export const TEAMS_AND_COMPANIES = { 'Feature Success': ['IconFlask', 'IconTestTube', 'IconMultivariateTesting', 'IconSplitTesting', 'IconBalance'], Pipeline: ['IconWebhooks', 'IconDecisionTree'], 'Product OS': ['IconNotebook', 'IconHogQL', 'IconDashboard', 'IconSupport'], - Logos: ['IconLogomark', 'IconGithub'], + Logos: ['IconLogomark', 'IconGithub', 'IconLinear'], ErrorTracking: ['IconIssue'], LLMObservability: ['IconLlmObservability', 'IconLlmPromptEvaluation', 'IconLlmPromptManagement'], } diff --git a/frontend/src/scenes/notebooks/Nodes/NotebookNodePlaylist.tsx b/frontend/src/scenes/notebooks/Nodes/NotebookNodePlaylist.tsx index bed411c627c9..143f140a9c89 100644 --- a/frontend/src/scenes/notebooks/Nodes/NotebookNodePlaylist.tsx +++ b/frontend/src/scenes/notebooks/Nodes/NotebookNodePlaylist.tsx @@ -13,10 +13,10 @@ import { notebookNodeLogic } from './notebookNodeLogic' import { JSONContent, NotebookNodeProps, NotebookNodeAttributeProperties } from '../Notebook/utils' import { SessionRecordingsPlaylist } from 'scenes/session-recordings/playlist/SessionRecordingsPlaylist' import { sessionRecordingPlayerLogic } from 'scenes/session-recordings/player/sessionRecordingPlayerLogic' -import { IconComment } from 'lib/lemon-ui/icons' import { sessionRecordingPlayerLogicType } from 'scenes/session-recordings/player/sessionRecordingPlayerLogicType' import { RecordingsUniversalFiltersEmbed } from 'scenes/session-recordings/filters/RecordingsUniversalFiltersEmbed' import { PostHogErrorBoundary } from 'posthog-js/react' +import { IconComment } from '@posthog/icons' const Component = ({ attributes, diff --git a/frontend/src/scenes/notebooks/Nodes/NotebookNodeRecording.tsx b/frontend/src/scenes/notebooks/Nodes/NotebookNodeRecording.tsx index 96c3644171f4..97c36108ece3 100644 --- a/frontend/src/scenes/notebooks/Nodes/NotebookNodeRecording.tsx +++ b/frontend/src/scenes/notebooks/Nodes/NotebookNodeRecording.tsx @@ -21,9 +21,8 @@ import { notebookNodeLogic } from './notebookNodeLogic' import { LemonSwitch } from '@posthog/lemon-ui' import { JSONContent, NotebookNodeProps, NotebookNodeAttributeProperties } from '../Notebook/utils' import { asDisplay } from 'scenes/persons/person-utils' -import { IconComment } from 'lib/lemon-ui/icons' import { NotFound } from 'lib/components/NotFound' -import { IconPerson } from '@posthog/icons' +import { IconComment, IconPerson } from '@posthog/icons' import { UUID_REGEX_MATCH_GROUPS } from './utils' const HEIGHT = 500 diff --git a/frontend/src/scenes/session-recordings/player/commenting/CommentOnRecordingButton.tsx b/frontend/src/scenes/session-recordings/player/commenting/CommentOnRecordingButton.tsx index fb00a1ef3093..07004a6fd196 100644 --- a/frontend/src/scenes/session-recordings/player/commenting/CommentOnRecordingButton.tsx +++ b/frontend/src/scenes/session-recordings/player/commenting/CommentOnRecordingButton.tsx @@ -1,12 +1,13 @@ import { useActions, useValues } from 'kea' import { sessionRecordingPlayerLogic } from 'scenes/session-recordings/player/sessionRecordingPlayerLogic' -import { - playerCommentOverlayLogic, - quickEmojis, -} from 'scenes/session-recordings/player/commenting/playerFrameCommentOverlayLogic' +import { playerCommentOverlayLogic } from 'scenes/session-recordings/player/commenting/playerFrameCommentOverlayLogic' import { LemonButton } from 'lib/lemon-ui/LemonButton' import { KeyboardShortcut } from '~/layout/navigation-3000/components/KeyboardShortcut' -import { IconComment } from 'lib/lemon-ui/icons' +import { IconEmoji, IconComment } from '@posthog/icons' +import { emojiUsageLogic } from 'lib/lemon-ui/LemonTextArea/emojiUsageLogic' +import { EmojiPickerPopover } from 'lib/components/EmojiPicker/EmojiPickerPopover' +import { useCallback, useState } from 'react' +import { Spinner } from 'lib/lemon-ui/Spinner' export function EmojiCommentRow({ onSelectEmoji }: { onSelectEmoji?: () => void }): JSX.Element { const { @@ -16,20 +17,23 @@ export function EmojiCommentRow({ onSelectEmoji }: { onSelectEmoji?: () => void const theBuiltOverlayLogic = playerCommentOverlayLogic({ recordingId: sessionRecordingId, ...logicProps }) const { addEmojiComment } = useActions(theBuiltOverlayLogic) + const { favouriteEmojis } = useValues(emojiUsageLogic) + const { emojiUsed } = useActions(emojiUsageLogic) + + const onSelectedEmoji = useCallback((emoji: string) => { + addEmojiComment(emoji) + emojiUsed(emoji) + onSelectEmoji?.() + }, []) + return (
- {quickEmojis.map((emoji) => ( - { - addEmojiComment(emoji) - onSelectEmoji?.() - }} - data-attr="emoji-quick-comment-button" - > - {emoji} + {favouriteEmojis.map((emoji) => ( + onSelectedEmoji(emoji)} data-attr="emoji-quick-comment-button"> + {emoji} ))} +
) } @@ -38,6 +42,15 @@ export function CommentOnRecordingButton(): JSX.Element { const { setIsCommenting } = useActions(sessionRecordingPlayerLogic) const { isCommenting } = useValues(sessionRecordingPlayerLogic) + const [quickEmojiIsOpen, setQuickEmojiIsOpen] = useState(false) + + const { + sessionPlayerData: { sessionRecordingId }, + logicProps, + } = useValues(sessionRecordingPlayerLogic) + const theBuiltOverlayLogic = playerCommentOverlayLogic({ recordingId: sessionRecordingId, ...logicProps }) + const { isLoading } = useValues(theBuiltOverlayLogic) + return ( } + icon={} sideAction={{ + icon: isLoading ? : , + onClick: () => { + if (isLoading) { + return + } + setQuickEmojiIsOpen(!quickEmojiIsOpen) + }, dropdown: { placement: 'bottom-end', - overlay: , + overlay: ( + { + setQuickEmojiIsOpen(!quickEmojiIsOpen) + }} + /> + ), + // because of the emoji picker popover + // we have to manually manage when the overlay closes + visible: quickEmojiIsOpen, + closeOnClickInside: false, + onClickOutside: () => { + setQuickEmojiIsOpen(!quickEmojiIsOpen) + }, }, 'data-attr': 'emoji-comment-dropdown', }} diff --git a/frontend/src/scenes/session-recordings/player/commenting/playerFrameCommentOverlayLogic.ts b/frontend/src/scenes/session-recordings/player/commenting/playerFrameCommentOverlayLogic.ts index 934bcda0bec0..c7e93fb160d7 100644 --- a/frontend/src/scenes/session-recordings/player/commenting/playerFrameCommentOverlayLogic.ts +++ b/frontend/src/scenes/session-recordings/player/commenting/playerFrameCommentOverlayLogic.ts @@ -1,4 +1,4 @@ -import { actions, connect, kea, listeners, path, props, selectors } from 'kea' +import { actions, connect, kea, listeners, path, props, reducers, selectors } from 'kea' import { forms } from 'kea-forms' import { subscriptions } from 'kea-subscriptions' import api from 'lib/api' @@ -13,8 +13,6 @@ import { sessionRecordingPlayerLogic, SessionRecordingPlayerLogicProps } from '. import { lemonToast } from 'lib/lemon-ui/LemonToast' import { isSingleEmoji } from 'scenes/session-recordings/utils' -export const quickEmojis = ['πŸ’–', 'πŸ‘', 'πŸ€”', 'πŸ‘Ž', '🌢️'] - export interface RecordingAnnotationForm { // formatted time in recording, e.g. 00:00:00, 00:00:01, 00:00:02, etc. // this is a string because we want to be able to display the time in the recording @@ -48,6 +46,15 @@ export const playerCommentOverlayLogic = kea([ actions({ editAnnotation: (annotation: RecordingAnnotationForm) => ({ annotation }), addEmojiComment: (emoji: string) => ({ emoji }), + setLoading: (isLoading: boolean) => ({ isLoading }), + }), + reducers({ + isLoading: [ + false, + { + setLoading: (_, { isLoading }: { isLoading: boolean }) => isLoading, + }, + ], }), selectors({ timestampUnits: [ @@ -87,15 +94,26 @@ export const playerCommentOverlayLogic = kea([ lemonToast.error(`Emoji comments must be emojis πŸ™ˆ, this string was too long: "${emoji}"`) return } - const apiPayload = { - date_marker: dayjs(values.currentTimestamp).toISOString(), - content: emoji, - scope: AnnotationScope.Recording, - recording_id: props.recordingId, - is_emoji: true, + const loadingTimeout = setTimeout(() => { + actions.setLoading(true) + }, 250) + + try { + const apiPayload = { + date_marker: dayjs(values.currentTimestamp).toISOString(), + content: emoji, + scope: AnnotationScope.Recording, + recording_id: props.recordingId, + is_emoji: true, + } + const createdAnnotation = await api.annotations.create(apiPayload) + actions.appendAnnotations([createdAnnotation]) + } finally { + if (loadingTimeout) { + clearTimeout(loadingTimeout) + } + actions.setLoading(false) } - const createdAnnotation = await api.annotations.create(apiPayload) - actions.appendAnnotations([createdAnnotation]) }, })), forms(({ props, values, actions }) => ({ diff --git a/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorControls.tsx b/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorControls.tsx index c01809a1d1bf..9120163c8ff8 100644 --- a/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorControls.tsx +++ b/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorControls.tsx @@ -7,11 +7,12 @@ import { IconSearch, IconStethoscope, IconTerminal, + IconComment, } from '@posthog/icons' import { LemonButton, LemonInput, SideAction, Tooltip } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' import { FEATURE_FLAGS } from 'lib/constants' -import { IconChevronRight, IconComment, IconUnverifiedEvent } from 'lib/lemon-ui/icons' +import { IconChevronRight, IconUnverifiedEvent } from 'lib/lemon-ui/icons' import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import { capitalizeFirstLetter } from 'lib/utils' import { useEffect, useState } from 'react' diff --git a/frontend/src/scenes/session-recordings/utils.test.ts b/frontend/src/scenes/session-recordings/utils.test.ts index 2c279eb3cd82..dddb31bf2837 100644 --- a/frontend/src/scenes/session-recordings/utils.test.ts +++ b/frontend/src/scenes/session-recordings/utils.test.ts @@ -1,8 +1,8 @@ -import { quickEmojis } from 'scenes/session-recordings/player/commenting/playerFrameCommentOverlayLogic' import { isSingleEmoji } from 'scenes/session-recordings/utils' +import { defaultQuickEmojis } from 'lib/lemon-ui/LemonTextArea/emojiUsageLogic' describe('session recording utils', () => { - quickEmojis.forEach((quickEmoji) => { + defaultQuickEmojis.forEach((quickEmoji) => { it(`can check ${quickEmoji} is a single emoji`, () => { expect(isSingleEmoji(quickEmoji)).toBe(true) }) diff --git a/package.json b/package.json index 9f1d3515a93a..9e46bd4056bd 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "overrides": { "playwright": "1.45.0", "typescript": "5.2.2", - "@posthog/icons": "0.24.0" + "@posthog/icons": "0.26.0" }, "patchedDependencies": { "heatmap.js@2.0.5": "patches/heatmap.js@2.0.5.patch", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7c14a10bec62..68af0b7d31dc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16,7 +16,7 @@ catalogs: overrides: playwright: 1.45.0 typescript: 5.2.2 - '@posthog/icons': 0.24.0 + '@posthog/icons': 0.26.0 patchedDependencies: dayjs@1.11.11: @@ -73,7 +73,7 @@ importers: version: 4.3.0(stylelint@15.11.0(typescript@5.2.2)) stylelint-config-standard-scss: specifier: ^11.1.0 - version: 11.1.0(postcss@8.4.31)(stylelint@15.11.0(typescript@5.2.2)) + version: 11.1.0(postcss@8.5.6)(stylelint@15.11.0(typescript@5.2.2)) stylelint-order: specifier: ^6.0.3 version: 6.0.3(stylelint@15.11.0(typescript@5.2.2)) @@ -170,7 +170,7 @@ importers: version: 3.12.1 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@18.18.4)(ts-node@10.9.1(@swc/core@1.11.4(@swc/helpers@0.5.15))(@types/node@18.18.4)(typescript@5.2.2)) + version: 29.7.0(@types/node@18.18.4)(ts-node@10.9.1(@swc/core@1.10.14(@swc/helpers@0.5.15))(@types/node@18.18.4)(typescript@5.2.2)) parcel: specifier: ^2.13.3 version: 2.13.3(@swc/helpers@0.5.15)(cssnano@7.0.6(postcss@8.5.6))(postcss@8.5.6)(relateurl@0.2.7)(svgo@3.3.2)(terser@5.19.1)(typescript@5.2.2) @@ -533,8 +533,8 @@ importers: specifier: workspace:* version: link:../common/hogvm/typescript '@posthog/icons': - specifier: 0.24.0 - version: 0.24.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + specifier: 0.26.0 + version: 0.26.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@posthog/plugin-scaffold': specifier: ^1.4.4 version: 1.4.4 @@ -787,6 +787,9 @@ importers: fflate: specifier: ^0.7.4 version: 0.7.4 + frimousse: + specifier: ^0.3.0 + version: 0.3.0(react@18.2.0)(typescript@5.2.2) fuse.js: specifier: ^6.6.2 version: 6.6.2 @@ -1193,7 +1196,7 @@ importers: version: 4.3.0(stylelint@15.11.0(typescript@5.2.2)) stylelint-config-standard-scss: specifier: ^11.1.0 - version: 11.1.0(postcss@8.5.6)(stylelint@15.11.0(typescript@5.2.2)) + version: 11.1.0(postcss@8.4.31)(stylelint@15.11.0(typescript@5.2.2)) stylelint-order: specifier: ^6.0.3 version: 6.0.3(stylelint@15.11.0(typescript@5.2.2)) @@ -1574,8 +1577,8 @@ importers: products/actions: dependencies: '@posthog/icons': - specifier: 0.24.0 - version: 0.24.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + specifier: 0.26.0 + version: 0.26.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@types/react': specifier: '*' version: 17.0.52 @@ -1601,8 +1604,8 @@ importers: products/cohorts: dependencies: '@posthog/icons': - specifier: 0.24.0 - version: 0.24.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + specifier: 0.26.0 + version: 0.26.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@types/react': specifier: '*' version: 17.0.52 @@ -1628,8 +1631,8 @@ importers: products/dashboards: dependencies: '@posthog/icons': - specifier: 0.24.0 - version: 0.24.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + specifier: 0.26.0 + version: 0.26.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@types/react': specifier: '*' version: 17.0.52 @@ -1655,8 +1658,8 @@ importers: products/early_access_features: dependencies: '@posthog/icons': - specifier: 0.24.0 - version: 0.24.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + specifier: 0.26.0 + version: 0.26.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/react': specifier: '*' version: 7.6.4(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.2.2) @@ -1685,8 +1688,8 @@ importers: products/error_tracking: dependencies: '@posthog/icons': - specifier: 0.24.0 - version: 0.24.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + specifier: 0.26.0 + version: 0.26.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/addon-actions': specifier: '*' version: 7.6.4 @@ -1751,8 +1754,8 @@ importers: products/experiments: dependencies: '@posthog/icons': - specifier: 0.24.0 - version: 0.24.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + specifier: 0.26.0 + version: 0.26.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@types/react': specifier: '*' version: 17.0.52 @@ -1778,8 +1781,8 @@ importers: products/feature_flags: dependencies: '@posthog/icons': - specifier: 0.24.0 - version: 0.24.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + specifier: 0.26.0 + version: 0.26.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@types/react': specifier: '*' version: 17.0.52 @@ -1805,8 +1808,8 @@ importers: products/games: dependencies: '@posthog/icons': - specifier: 0.24.0 - version: 0.24.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + specifier: 0.26.0 + version: 0.26.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@types/react': specifier: '*' version: 17.0.52 @@ -1832,8 +1835,8 @@ importers: products/groups: dependencies: '@posthog/icons': - specifier: 0.24.0 - version: 0.24.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + specifier: 0.26.0 + version: 0.26.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@types/react': specifier: '*' version: 17.0.52 @@ -1859,8 +1862,8 @@ importers: products/links: dependencies: '@posthog/icons': - specifier: 0.24.0 - version: 0.24.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + specifier: 0.26.0 + version: 0.26.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/react': specifier: '*' version: 7.6.4(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.2.2) @@ -1892,8 +1895,8 @@ importers: products/llm_observability: dependencies: '@posthog/icons': - specifier: 0.24.0 - version: 0.24.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + specifier: 0.26.0 + version: 0.26.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/react': specifier: '*' version: 7.6.4(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.2.2) @@ -1911,7 +1914,7 @@ importers: version: 8.57.0 jest: specifier: '*' - version: 29.7.0(@types/node@22.15.17)(ts-node@10.9.1(@swc/core@1.11.4(@swc/helpers@0.5.15))(@types/node@22.15.17)(typescript@5.2.2)) + version: 29.7.0(@types/node@18.18.4)(ts-node@10.9.1(@swc/core@1.10.14(@swc/helpers@0.5.15))(@types/node@18.18.4)(typescript@5.2.2)) kea: specifier: '*' version: 3.1.5(react@18.2.0) @@ -1937,8 +1940,8 @@ importers: products/logs: dependencies: '@posthog/icons': - specifier: 0.24.0 - version: 0.24.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + specifier: 0.26.0 + version: 0.26.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@posthog/products-error-tracking': specifier: workspace:* version: link:../error_tracking @@ -1965,7 +1968,7 @@ importers: version: 3.1.3 jest: specifier: '*' - version: 29.7.0(@types/node@22.15.17)(ts-node@10.9.1(@swc/core@1.11.4(@swc/helpers@0.5.15))(@types/node@22.15.17)(typescript@5.2.2)) + version: 29.7.0(@types/node@18.18.4)(ts-node@10.9.1(@swc/core@1.10.14(@swc/helpers@0.5.15))(@types/node@18.18.4)(typescript@5.2.2)) kea: specifier: '*' version: 3.1.5(react@18.2.0) @@ -1994,8 +1997,8 @@ importers: products/managed_migrations: dependencies: '@posthog/icons': - specifier: 0.24.0 - version: 0.24.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + specifier: 0.26.0 + version: 0.26.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@posthog/lemon-ui': specifier: '*' version: 0.0.0(antd@5.26.0(date-fns@2.29.3)(luxon@3.5.0)(moment@2.29.4)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(kea-router@3.2.1(kea@3.1.5(react@18.2.0)))(kea@3.1.5(react@18.2.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -2021,8 +2024,8 @@ importers: products/messaging: dependencies: '@posthog/icons': - specifier: 0.24.0 - version: 0.24.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + specifier: 0.26.0 + version: 0.26.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@types/react': specifier: '*' version: 17.0.52 @@ -2069,8 +2072,8 @@ importers: products/notebooks: dependencies: '@posthog/icons': - specifier: 0.24.0 - version: 0.24.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + specifier: 0.26.0 + version: 0.26.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@types/react': specifier: '*' version: 17.0.52 @@ -2096,8 +2099,8 @@ importers: products/persons: dependencies: '@posthog/icons': - specifier: 0.24.0 - version: 0.24.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + specifier: 0.26.0 + version: 0.26.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@types/react': specifier: '*' version: 17.0.52 @@ -2123,8 +2126,8 @@ importers: products/product_analytics: dependencies: '@posthog/icons': - specifier: 0.24.0 - version: 0.24.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + specifier: 0.26.0 + version: 0.26.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@types/react': specifier: '*' version: 17.0.52 @@ -2150,8 +2153,8 @@ importers: products/replay: dependencies: '@posthog/icons': - specifier: 0.24.0 - version: 0.24.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + specifier: 0.26.0 + version: 0.26.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@types/react': specifier: '*' version: 17.0.52 @@ -2177,8 +2180,8 @@ importers: products/revenue_analytics: dependencies: '@posthog/icons': - specifier: 0.24.0 - version: 0.24.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + specifier: 0.26.0 + version: 0.26.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/react': specifier: '*' version: 7.6.4(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.2.2) @@ -2207,8 +2210,8 @@ importers: products/surveys: dependencies: '@posthog/icons': - specifier: 0.24.0 - version: 0.24.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + specifier: 0.26.0 + version: 0.26.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@types/react': specifier: '*' version: 17.0.52 @@ -2234,8 +2237,8 @@ importers: products/user_interviews: dependencies: '@posthog/icons': - specifier: 0.24.0 - version: 0.24.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + specifier: 0.26.0 + version: 0.26.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@types/react': specifier: '*' version: 17.0.52 @@ -2261,8 +2264,8 @@ importers: products/web_analytics: dependencies: '@posthog/icons': - specifier: 0.24.0 - version: 0.24.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + specifier: 0.26.0 + version: 0.26.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@types/react': specifier: '*' version: 17.0.52 @@ -4986,8 +4989,8 @@ packages: resolution: {integrity: sha512-B8hZ8Dh2EoJoDb7Gx38ylBQM92oON/X2IxXCb7BfYStk3m17nStcAyaCsc2zbvxC0fFfTMU8lFRiFSEJmijkyg==} engines: {node: '>=12'} - '@posthog/icons@0.24.0': - resolution: {integrity: sha512-Lf/xac+TFAkM5y/WkpZQI8m7JP1PULI1HHCXoYtUv59y4vFA7D5pYQQkN0r8HIwMLxdCgzAJ1J5hh1vKHVcCOw==} + '@posthog/icons@0.26.0': + resolution: {integrity: sha512-Ig0B7AM3HzJpUbcmXxXVPlCvq8a+C8xlhs117ieBfwb9Q9Kp12p3ptz0CoolIRTvcJZeCGriTLx1PQYcmo2Saw==} peerDependencies: react: '>=16.14.0' react-dom: '>=16.14.0' @@ -10691,6 +10694,15 @@ packages: resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} engines: {node: '>= 0.6'} + frimousse@0.3.0: + resolution: {integrity: sha512-kO6LMoKY/cLAYEhXXtqLRaLIE6L/DagpFPrUZaLv3LsUa1/8Iza3HhwZcgN8eZ+weXnhv69eoclNUPohcCa/IQ==} + peerDependencies: + react: ^18 || ^19 + typescript: 5.2.2 + peerDependenciesMeta: + typescript: + optional: true + fromentries@1.3.2: resolution: {integrity: sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==} @@ -16570,8 +16582,8 @@ packages: resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} engines: {node: '>= 10.0.0'} - unlayer-types@1.279.0: - resolution: {integrity: sha512-J8cYUsH2tdBtVoF+n9wIELToNle738hE+mJtGQiTo9G2LOSOt7P6OO4IKVZwx3x7Ir9sbBLOwy2ik9rMiKuvPw==} + unlayer-types@1.281.0: + resolution: {integrity: sha512-YbsljFNAwwzvU+l7ulXZJ83+FpBNi9gd3fT9+nqSLMGPs9BF4Di1AEdNcnMyDH9CJsJfSpcuIOYHuymqnp2+Gw==} unpipe@1.0.0: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} @@ -19836,41 +19848,6 @@ snapshots: - supports-color - ts-node - '@jest/core@29.7.0(ts-node@10.9.1(@swc/core@1.11.4(@swc/helpers@0.5.15))(@types/node@22.15.17)(typescript@5.2.2))': - dependencies: - '@jest/console': 29.7.0 - '@jest/reporters': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 18.18.4 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - ci-info: 3.8.0 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@18.18.4)(ts-node@10.9.1(@swc/core@1.11.4(@swc/helpers@0.5.15))(@types/node@22.15.17)(typescript@5.2.2)) - jest-haste-map: 29.7.0 - jest-message-util: 29.7.0 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-resolve-dependencies: 29.7.0 - jest-runner: 29.7.0 - jest-runtime: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - jest-watcher: 29.7.0 - micromatch: 4.0.8 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-ansi: 6.0.1 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - ts-node - '@jest/create-cache-key-function@29.7.0': dependencies: '@jest/types': 29.6.3 @@ -21088,7 +21065,7 @@ snapshots: '@posthog/clickhouse@1.7.0': {} - '@posthog/icons@0.24.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + '@posthog/icons@0.26.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -23609,7 +23586,7 @@ snapshots: commander: 9.4.1 expect-playwright: 0.8.0 glob: 10.4.5 - jest: 29.7.0 + jest: 29.7.0(@types/node@18.18.4)(ts-node@10.9.1(@swc/core@1.10.14(@swc/helpers@0.5.15))(@types/node@18.18.4)(typescript@5.2.2)) jest-circus: 29.7.0 jest-environment-node: 29.7.0 jest-junit: 16.0.0 @@ -26610,21 +26587,6 @@ snapshots: - supports-color - ts-node - create-jest@29.7.0(@types/node@22.15.17)(ts-node@10.9.1(@swc/core@1.11.4(@swc/helpers@0.5.15))(@types/node@22.15.17)(typescript@5.2.2)): - dependencies: - '@jest/types': 29.6.3 - chalk: 4.1.2 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@22.15.17)(ts-node@10.9.1(@swc/core@1.11.4(@swc/helpers@0.5.15))(@types/node@22.15.17)(typescript@5.2.2)) - jest-util: 29.7.0 - prompts: 2.4.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - create-require@1.1.1: {} crelt@1.0.5: {} @@ -27836,7 +27798,7 @@ snapshots: eslint-import-resolver-node@0.3.9: dependencies: - debug: 3.2.7(supports-color@5.5.0) + debug: 3.2.7(supports-color@8.1.1) is-core-module: 2.13.1 resolve: 1.22.8 transitivePeerDependencies: @@ -27844,7 +27806,7 @@ snapshots: eslint-module-utils@2.8.0(@typescript-eslint/parser@7.1.1(eslint@8.57.0)(typescript@5.2.2))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0): dependencies: - debug: 3.2.7(supports-color@5.5.0) + debug: 3.2.7(supports-color@8.1.1) optionalDependencies: '@typescript-eslint/parser': 7.1.1(eslint@8.57.0)(typescript@5.2.2) eslint: 8.57.0 @@ -27870,7 +27832,7 @@ snapshots: array.prototype.findlastindex: 1.2.3 array.prototype.flat: 1.3.2 array.prototype.flatmap: 1.3.2 - debug: 3.2.7(supports-color@5.5.0) + debug: 3.2.7(supports-color@8.1.1) doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 @@ -28420,6 +28382,12 @@ snapshots: fresh@0.5.2: {} + frimousse@0.3.0(react@18.2.0)(typescript@5.2.2): + dependencies: + react: 18.2.0 + optionalDependencies: + typescript: 5.2.2 + fromentries@1.3.2: {} fs-constants@1.0.0: {} @@ -29691,25 +29659,6 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@29.7.0: - dependencies: - '@jest/core': 29.7.0(ts-node@10.9.1(@swc/core@1.11.4(@swc/helpers@0.5.15))(@types/node@18.18.4)(typescript@5.2.2)) - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - chalk: 4.1.2 - create-jest: 29.7.0(@types/node@22.15.17)(ts-node@10.9.1(@swc/core@1.11.4(@swc/helpers@0.5.15))(@types/node@22.15.17)(typescript@5.2.2)) - exit: 0.1.2 - import-local: 3.1.0 - jest-config: 29.7.0(@types/node@22.15.17)(ts-node@10.9.1(@swc/core@1.11.4(@swc/helpers@0.5.15))(@types/node@22.15.17)(typescript@5.2.2)) - jest-util: 29.7.0 - jest-validate: 29.7.0 - yargs: 17.7.1 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - jest-cli@29.7.0(@types/node@18.18.4)(ts-node@10.9.1(@swc/core@1.10.14(@swc/helpers@0.5.15))(@types/node@18.18.4)(typescript@5.2.2)): dependencies: '@jest/core': 29.7.0(ts-node@10.9.1(@swc/core@1.10.14(@swc/helpers@0.5.15))(@types/node@18.18.4)(typescript@5.2.2)) @@ -29748,25 +29697,6 @@ snapshots: - supports-color - ts-node - jest-cli@29.7.0(@types/node@22.15.17)(ts-node@10.9.1(@swc/core@1.11.4(@swc/helpers@0.5.15))(@types/node@22.15.17)(typescript@5.2.2)): - dependencies: - '@jest/core': 29.7.0(ts-node@10.9.1(@swc/core@1.11.4(@swc/helpers@0.5.15))(@types/node@22.15.17)(typescript@5.2.2)) - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - chalk: 4.1.2 - create-jest: 29.7.0(@types/node@22.15.17)(ts-node@10.9.1(@swc/core@1.11.4(@swc/helpers@0.5.15))(@types/node@22.15.17)(typescript@5.2.2)) - exit: 0.1.2 - import-local: 3.1.0 - jest-config: 29.7.0(@types/node@22.15.17)(ts-node@10.9.1(@swc/core@1.11.4(@swc/helpers@0.5.15))(@types/node@22.15.17)(typescript@5.2.2)) - jest-util: 29.7.0 - jest-validate: 29.7.0 - yargs: 17.7.1 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - jest-config@29.7.0(@types/node@18.18.4)(ts-node@10.9.1(@swc/core@1.10.14(@swc/helpers@0.5.15))(@types/node@18.18.4)(typescript@5.2.2)): dependencies: '@babel/core': 7.26.0 @@ -29829,68 +29759,6 @@ snapshots: - babel-plugin-macros - supports-color - jest-config@29.7.0(@types/node@18.18.4)(ts-node@10.9.1(@swc/core@1.11.4(@swc/helpers@0.5.15))(@types/node@22.15.17)(typescript@5.2.2)): - dependencies: - '@babel/core': 7.26.0 - '@jest/test-sequencer': 29.7.0 - '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.26.0) - chalk: 4.1.2 - ci-info: 3.8.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 29.7.0 - jest-environment-node: 29.7.0 - jest-get-type: 29.6.3 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-runner: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - micromatch: 4.0.8 - parse-json: 5.2.0 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - optionalDependencies: - '@types/node': 18.18.4 - ts-node: 10.9.1(@swc/core@1.11.4(@swc/helpers@0.5.15))(@types/node@22.15.17)(typescript@5.2.2) - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - jest-config@29.7.0(@types/node@22.15.17)(ts-node@10.9.1(@swc/core@1.11.4(@swc/helpers@0.5.15))(@types/node@22.15.17)(typescript@5.2.2)): - dependencies: - '@babel/core': 7.26.0 - '@jest/test-sequencer': 29.7.0 - '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.26.0) - chalk: 4.1.2 - ci-info: 3.8.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 29.7.0 - jest-environment-node: 29.7.0 - jest-get-type: 29.6.3 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-runner: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - micromatch: 4.0.8 - parse-json: 5.2.0 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - optionalDependencies: - '@types/node': 22.15.17 - ts-node: 10.9.1(@swc/core@1.11.4(@swc/helpers@0.5.15))(@types/node@22.15.17)(typescript@5.2.2) - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - jest-diff@29.7.0: dependencies: chalk: 4.1.2 @@ -30005,7 +29873,7 @@ snapshots: jest-playwright-preset@4.0.0(jest-circus@29.7.0)(jest-environment-node@29.7.0)(jest-runner@29.7.0)(jest@29.7.0): dependencies: expect-playwright: 0.8.0 - jest: 29.7.0 + jest: 29.7.0(@types/node@18.18.4)(ts-node@10.9.1(@swc/core@1.10.14(@swc/helpers@0.5.15))(@types/node@18.18.4)(typescript@5.2.2)) jest-circus: 29.7.0 jest-environment-node: 29.7.0 jest-process-manager: 0.4.0 @@ -30163,7 +30031,7 @@ snapshots: dependencies: ansi-escapes: 6.0.0 chalk: 5.4.1 - jest: 29.7.0 + jest: 29.7.0(@types/node@18.18.4)(ts-node@10.9.1(@swc/core@1.10.14(@swc/helpers@0.5.15))(@types/node@18.18.4)(typescript@5.2.2)) jest-regex-util: 29.6.3 jest-watcher: 29.7.0 slash: 5.1.0 @@ -30194,18 +30062,6 @@ snapshots: merge-stream: 2.0.0 supports-color: 8.1.1 - jest@29.7.0: - dependencies: - '@jest/core': 29.7.0(ts-node@10.9.1(@swc/core@1.11.4(@swc/helpers@0.5.15))(@types/node@18.18.4)(typescript@5.2.2)) - '@jest/types': 29.6.3 - import-local: 3.1.0 - jest-cli: 29.7.0 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - jest@29.7.0(@types/node@18.18.4)(ts-node@10.9.1(@swc/core@1.10.14(@swc/helpers@0.5.15))(@types/node@18.18.4)(typescript@5.2.2)): dependencies: '@jest/core': 29.7.0(ts-node@10.9.1(@swc/core@1.10.14(@swc/helpers@0.5.15))(@types/node@18.18.4)(typescript@5.2.2)) @@ -30230,18 +30086,6 @@ snapshots: - supports-color - ts-node - jest@29.7.0(@types/node@22.15.17)(ts-node@10.9.1(@swc/core@1.11.4(@swc/helpers@0.5.15))(@types/node@22.15.17)(typescript@5.2.2)): - dependencies: - '@jest/core': 29.7.0(ts-node@10.9.1(@swc/core@1.11.4(@swc/helpers@0.5.15))(@types/node@22.15.17)(typescript@5.2.2)) - '@jest/types': 29.6.3 - import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@22.15.17)(ts-node@10.9.1(@swc/core@1.11.4(@swc/helpers@0.5.15))(@types/node@22.15.17)(typescript@5.2.2)) - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - jiti@2.4.2: {} jmespath@0.16.0: {} @@ -33705,7 +33549,7 @@ snapshots: react-email-editor@1.7.11(react@18.2.0): dependencies: react: 18.2.0 - unlayer-types: 1.279.0 + unlayer-types: 1.281.0 react-error-overlay@6.0.9: {} @@ -35460,27 +35304,6 @@ snapshots: optionalDependencies: '@swc/core': 1.11.4(@swc/helpers@0.5.15) - ts-node@10.9.1(@swc/core@1.11.4(@swc/helpers@0.5.15))(@types/node@22.15.17)(typescript@5.2.2): - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.9 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.3 - '@types/node': 22.15.17 - acorn: 8.10.0 - acorn-walk: 8.2.0 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.2 - make-error: 1.3.6 - typescript: 5.2.2 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - optionalDependencies: - '@swc/core': 1.11.4(@swc/helpers@0.5.15) - optional: true - ts-pattern@4.3.0: {} ts-toolbelt@9.6.0: {} @@ -35729,7 +35552,7 @@ snapshots: universalify@2.0.0: {} - unlayer-types@1.279.0: {} + unlayer-types@1.281.0: {} unpipe@1.0.0: {}