diff --git a/ee/tabby-ui/app/(dashboard)/activities/components/activity.tsx b/ee/tabby-ui/app/(dashboard)/activities/components/activity.tsx new file mode 100644 index 000000000000..61345a6a3296 --- /dev/null +++ b/ee/tabby-ui/app/(dashboard)/activities/components/activity.tsx @@ -0,0 +1,358 @@ +'use client' + +import React from 'react' +import moment from 'moment' +import { useTheme } from 'next-themes' +import { DateRange } from 'react-day-picker' +import ReactJson from 'react-json-view' +import { toast } from 'sonner' +import { useQuery } from 'urql' + +import { DEFAULT_PAGE_SIZE } from '@/lib/constants' +import { graphql } from '@/lib/gql/generates' +import { EventKind, ListUserEventsQuery } from '@/lib/gql/generates/graphql' +import { Member, useAllMembers } from '@/lib/hooks/use-all-members' +import { QueryVariables } from '@/lib/tabby/gql' +import { Button } from '@/components/ui/button' +import { Card, CardContent } from '@/components/ui/card' +import { + IconChevronLeft, + IconChevronRight, + IconFileSearch +} from '@/components/ui/icons' +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectTrigger, + SelectValue +} from '@/components/ui/select' +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow +} from '@/components/ui/table' +import { + Tooltip, + TooltipContent, + TooltipTrigger +} from '@/components/ui/tooltip' +import DateRangePicker from '@/components/date-range-picker' +import LoadingWrapper from '@/components/loading-wrapper' + +const DEFAULT_DATE_RANGE = '-24h' +const KEY_SELECT_ALL = 'all' + +export const listUserEvents = graphql(/* GraphQL */ ` + query ListUserEvents( + $after: String + $before: String + $first: Int + $last: Int + $start: DateTimeUtc! + $end: DateTimeUtc! + $users: [ID!] + ) { + userEvents( + after: $after + before: $before + first: $first + last: $last + start: $start + end: $end + users: $users + ) { + edges { + node { + id + userId + createdAt + kind + payload + } + cursor + } + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + } + } + } +`) + +export default function Activity() { + const defaultFromDate = moment().add(parseInt(DEFAULT_DATE_RANGE, 10), 'day') + const defaultToDate = moment() + + const [members] = useAllMembers() + const [dateRange, setDateRange] = React.useState({ + from: defaultFromDate.toDate(), + to: defaultToDate.toDate() + }) + const [page, setPage] = React.useState(1) + const [userEvents, setUserEvents] = + React.useState() + const [selectedMember, setSelectedMember] = React.useState(KEY_SELECT_ALL) + + const [queryVariables, setQueryVariables] = React.useState< + Omit, 'start' | 'end'> + >({ + last: DEFAULT_PAGE_SIZE + }) + + const [{ data, error, fetching }] = useQuery({ + query: listUserEvents, + variables: { + start: moment(dateRange.from!).utc().format(), + end: dateRange.to + ? moment(dateRange.to).utc().format() + : moment(dateRange.from!).utc().format(), + users: selectedMember === KEY_SELECT_ALL ? undefined : [selectedMember], + ...queryVariables + } + }) + + React.useEffect(() => { + if (data?.userEvents.edges.length) { + setUserEvents(data.userEvents) + } + }, [data]) + + React.useEffect(() => { + if (error?.message) { + toast.error(error.message) + } + }, [error]) + + const updateDateRange = (range: DateRange) => { + setDateRange(range) + setPage(1) + setQueryVariables({ last: DEFAULT_PAGE_SIZE }) + } + + return ( + +
+
+
+
+ + + +
+ + + {(!data?.userEvents.edges || + data?.userEvents.edges.length === 0) && ( + + +

+ No data available for the chosen dates +

+

+ Please try a different date range +

+
+ )} + + {data?.userEvents.edges && data?.userEvents.edges.length > 0 && ( + <> + + + + + Event + People + Time + + + + {userEvents?.edges + .sort( + (a, b) => + new Date(b.node.createdAt).getTime() - + new Date(a.node.createdAt).getTime() + ) + .map(userEvent => ( + + ))} + +
+
+ + )} +
+ + {(data?.userEvents.pageInfo?.hasNextPage || + data?.userEvents.pageInfo?.hasPreviousPage) && ( +
+
+ {' '} + Page {page} +
+
+ + +
+
+ )} +
+
+
+
+ ) +} + +function ActivityRow({ + activity, + members +}: { + activity: ListUserEventsQuery['userEvents']['edges'][0]['node'] + members: Member[] +}) { + const { theme } = useTheme() + // const [members] = useAllMembers() + const [isExpanded, setIsExpanded] = React.useState(false) + + let payloadJson + try { + payloadJson = JSON.parse(activity.payload) as { + [key: string]: { language?: string } + } + } catch (error: any) { + if (error?.message) { + toast.error(error.message) + } + } + + if (!payloadJson) return null + + let tooltip = '' + switch (activity.kind) { + case EventKind.Completion: { + tooltip = 'Code completion supplied' + break + } + + case EventKind.Dismiss: { + tooltip = 'Code completion viewed but not used' + break + } + case EventKind.Select: { + tooltip = 'Code completion accepted and inserted' + break + } + case EventKind.View: { + tooltip = 'Code completion shown in editor' + break + } + } + return ( + <> + setIsExpanded(!isExpanded)} + > + + + {activity.kind} + +

{tooltip}

+
+
+
+ + {members.find(user => user.id === activity.userId)?.email || + activity.userId} + + + {moment(activity.createdAt).isBefore(moment().subtract(1, 'days')) + ? moment(activity.createdAt).format('YYYY-MM-DD HH:mm') + : moment(activity.createdAt).fromNow()} + +
+ + {isExpanded && ( + + + + + + )} + + ) +} diff --git a/ee/tabby-ui/app/(dashboard)/activities/page.tsx b/ee/tabby-ui/app/(dashboard)/activities/page.tsx new file mode 100644 index 000000000000..150f4fed124f --- /dev/null +++ b/ee/tabby-ui/app/(dashboard)/activities/page.tsx @@ -0,0 +1,11 @@ +import { Metadata } from 'next' + +import Activity from './components/activity' + +export const metadata: Metadata = { + title: 'Activities' +} + +export default function Page() { + return +} diff --git a/ee/tabby-ui/app/(dashboard)/components/sidebar.tsx b/ee/tabby-ui/app/(dashboard)/components/sidebar.tsx index ecd158421dcb..7b3c5170aaef 100644 --- a/ee/tabby-ui/app/(dashboard)/components/sidebar.tsx +++ b/ee/tabby-ui/app/(dashboard)/components/sidebar.tsx @@ -68,6 +68,7 @@ export default function Sidebar({ children, className }: SidebarProps) { Cluster Jobs Reports + Activities ({ - from: moment() - .subtract(parseInt(DATE_OPTIONS.LAST14DAYS, 10), 'day') - .toDate(), + from: moment().add(parseInt(DEFAULT_DATE_RANGE, 10), 'day').toDate(), to: moment().toDate() }) const [selectedMember, setSelectedMember] = useState(KEY_SELECT_ALL) const [selectedLanguage, setSelectedLanguage] = useState([]) - const [showDateFilter, setShowDateFilter] = useState(false) - const [selectDateFilter, setSelectDateFilter] = useState( - DATE_OPTIONS.LAST14DAYS - ) - const [showDateRangerPicker, setShowDateRangerPicker] = useState(false) - const [calendarDateRange, setCalendarDateRange] = useState< - DateRange | undefined - >({ - from: moment() - .subtract(parseInt(DATE_OPTIONS.LAST14DAYS, 10), 'day') - .toDate(), - to: moment().toDate() - }) - const [showDateUntilNowPicker, setShowDateUntilNowPicker] = useState(false) - const [dateUntilNow, setDateUntilNow] = useState( - moment().toDate() - ) // Query stats of selected date range const [{ data: dailyStatsData, fetching: fetchingDailyState }] = useQuery({ @@ -236,58 +206,6 @@ export function Report() { })) } - const onDateFilterChange = (value: DATE_OPTIONS) => { - switch (value) { - case DATE_OPTIONS.TODAY: { - setDateRange({ - from: moment().startOf('day').toDate(), - to: moment().toDate() - }) - break - } - case DATE_OPTIONS.YESTERDAY: { - setDateRange({ - from: moment().subtract(1, 'd').startOf('day').toDate(), - to: moment().subtract(1, 'd').endOf('day').toDate() - }) - break - } - default: { - setDateRange({ - from: moment() - .subtract(parseInt(value, 10), 'day') - .startOf('day') - .toDate(), - to: moment().toDate() - }) - } - } - setSelectDateFilter(value) - } - - const onDateFilterOpenChange = (open: boolean) => { - if (!open && !showDateRangerPicker && !showDateUntilNowPicker) { - setShowDateFilter(false) - } - } - - const applyDateRange = () => { - setShowDateFilter(false) - setShowDateRangerPicker(false) - setSelectDateFilter(DATE_OPTIONS.CUSTOM_RANGE) - setDateRange(calendarDateRange!) - } - - const applyDateUntilNow = () => { - setShowDateFilter(false) - setShowDateUntilNowPicker(false) - setSelectDateFilter(DATE_OPTIONS.CUSTOM_DATE) - setDateRange({ - from: moment(dateUntilNow).startOf('day').toDate(), - to: moment().toDate() - }) - } - return (
@@ -366,9 +284,7 @@ export function Report() { )} {selectedLanguage.length === 1 && (

- {toProgrammingLanguageDisplayName( - selectedLanguage[0] - )} + {getLanguageDisplayName(selectedLanguage[0])}

)} {selectedLanguage.length > 1 && ( @@ -421,9 +337,7 @@ export function Report() { >
- - {toProgrammingLanguageDisplayName(value)} - + {getLanguageDisplayName(value)} ) })} @@ -446,135 +360,17 @@ export function Report() { -
- - - - - date > new Date()} - /> - -
- - -
-
-
- - - - date > new Date()} - /> - -
- - -
-
-
-
+
diff --git a/ee/tabby-ui/app/(home)/components/stats.tsx b/ee/tabby-ui/app/(home)/components/stats.tsx index b6a477394fec..cb34957cfbcf 100644 --- a/ee/tabby-ui/app/(home)/components/stats.tsx +++ b/ee/tabby-ui/app/(home)/components/stats.tsx @@ -26,13 +26,12 @@ import { Language } from '@/lib/gql/generates/graphql' import { useMe } from '@/lib/hooks/use-me' -import { toProgrammingLanguageDisplayName } from '@/lib/language-utils' +import { getLanguageColor, getLanguageDisplayName } from '@/lib/language-utils' import { queryDailyStats, queryDailyStatsInPastYear } from '@/lib/tabby/query' import { Card, CardContent } from '@/components/ui/card' import { Skeleton } from '@/components/ui/skeleton' import LoadingWrapper from '@/components/loading-wrapper' -import languageColors from '../language-colors.json' import { CompletionCharts } from './completion-charts' const DATE_RANGE = 6 @@ -50,13 +49,6 @@ type LanguageStats = Record< } > -const getLanguageColorMap = (): Record => { - return Object.entries(languageColors).reduce((acc, cur) => { - const [lan, color] = cur - return { ...acc, [lan.toLocaleLowerCase()]: color } - }, {}) -} - function ActivityCalendar({ data }: { @@ -107,7 +99,7 @@ const LanguageLabel: React.FC< textAnchor="start" dominantBaseline="middle" > - {toProgrammingLanguageDisplayName(value as Language)} + {getLanguageDisplayName(value as Language)} ) } @@ -136,7 +128,7 @@ function LanguageTooltip({ {views}

- {toProgrammingLanguageDisplayName(name)} + {getLanguageDisplayName(name)}

@@ -152,7 +144,6 @@ export default function Stats() { const searchParams = useSearchParams() const sample = searchParams.get('sample') === 'true' - const colorMap = getLanguageColorMap() const startDate = moment() .subtract(DATE_RANGE, 'day') .startOf('day') @@ -344,7 +335,7 @@ export default function Stats() { } /> {languageData.map((entry, index) => { - const lanColor = colorMap[entry.name.toLocaleLowerCase()] + const lanColor = getLanguageColor(entry.name) const color = lanColor ? lanColor : theme === 'dark' diff --git a/ee/tabby-ui/components/date-range-picker.tsx b/ee/tabby-ui/components/date-range-picker.tsx new file mode 100644 index 000000000000..458834a6a92f --- /dev/null +++ b/ee/tabby-ui/components/date-range-picker.tsx @@ -0,0 +1,260 @@ +'use client' + +import React from 'react' +import moment, { unitOfTime } from 'moment' +import { DateRange } from 'react-day-picker' + +import { cn } from '@/lib/utils' +import { Button } from '@/components/ui/button' +import { Calendar } from '@/components/ui/calendar' +import { Card, CardContent } from '@/components/ui/card' +import { IconCheck } from '@/components/ui/icons' +import { + Select, + SelectContent, + SelectItem, + SelectSeparator, + SelectTrigger, + SelectValue +} from '@/components/ui/select' +import { Separator } from '@/components/ui/separator' + +enum DATE_OPTIONS { + 'TODAY' = 'today', + 'YESTERDAY' = 'yesterday', + 'CUSTOM_DATE' = 'custom_date', + 'CUSTOM_RANGE' = 'custom_range' +} + +const parseDateValue = (value: string) => { + return { + number: parseInt(value, 10) || -1, + unit: value[value.length - 1] || 'h' + } +} + +export default function DateRangePicker({ + options, + onSelect, + defaultValue, + hasToday, + hasYesterday +}: { + options: { label: string; value: string }[] + onSelect?: (range: DateRange) => void + defaultValue?: string + hasToday?: boolean + hasYesterday?: boolean +}) { + defaultValue = defaultValue || options[0].value + const defaultDate = parseDateValue(defaultValue) + const [dateRange, setDateRange] = React.useState({ + from: moment() + .add( + defaultDate.number, + defaultDate.unit as unitOfTime.DurationConstructor + ) + .toDate(), + to: moment().toDate() + }) + const [showDateFilter, setShowDateFilter] = React.useState(false) + const [selectDateFilter, setSelectDateFilter] = React.useState(defaultValue) + const [showDateRangerPicker, setShowDateRangerPicker] = React.useState(false) + const [calendarDateRange, setCalendarDateRange] = React.useState< + DateRange | undefined + >({ + from: moment() + .add( + defaultDate.number, + defaultDate.unit as unitOfTime.DurationConstructor + ) + .toDate(), + to: moment().toDate() + }) + const [showDateUntilNowPicker, setShowDateUntilNowPicker] = + React.useState(false) + const [dateUntilNow, setDateUntilNow] = React.useState( + moment().toDate() + ) + + const onDateFilterChange = (value: DATE_OPTIONS | string) => { + switch (value) { + case DATE_OPTIONS.TODAY: { + updateDateRange({ + from: moment().startOf('day').toDate(), + to: moment().toDate() + }) + break + } + case DATE_OPTIONS.YESTERDAY: { + updateDateRange({ + from: moment().subtract(1, 'd').startOf('day').toDate(), + to: moment().subtract(1, 'd').endOf('day').toDate() + }) + break + } + default: { + const { unit, number } = parseDateValue(value) + let from = moment().add(number, unit as unitOfTime.DurationConstructor) + if (!['h', 's', 'ms'].includes(unit)) from = from.startOf('day') + updateDateRange({ + from: from.toDate(), + to: moment().toDate() + }) + } + } + setSelectDateFilter(value) + } + + const onDateFilterOpenChange = (open: boolean) => { + if (!open && !showDateRangerPicker && !showDateUntilNowPicker) { + setShowDateFilter(false) + } + } + + const applyDateRange = () => { + setShowDateFilter(false) + setShowDateRangerPicker(false) + setSelectDateFilter(DATE_OPTIONS.CUSTOM_RANGE) + updateDateRange({ + from: calendarDateRange?.from, + to: moment(calendarDateRange?.to).endOf('day').toDate() + }) + } + + const applyDateUntilNow = () => { + setShowDateFilter(false) + setShowDateUntilNowPicker(false) + setSelectDateFilter(DATE_OPTIONS.CUSTOM_DATE) + updateDateRange({ + from: moment(dateUntilNow).startOf('day').toDate(), + to: moment().toDate() + }) + } + + const updateDateRange = (range: DateRange) => { + if (onSelect) onSelect(range) + setDateRange(range) + } + + return ( +
+ + + + + date > new Date()} + /> + +
+ + +
+
+
+ + + + date > new Date()} + /> + +
+ + +
+
+
+
+ ) +} diff --git a/ee/tabby-ui/components/ui/date-range-picker.tsx b/ee/tabby-ui/components/ui/date-range-picker.tsx deleted file mode 100644 index a016bb802464..000000000000 --- a/ee/tabby-ui/components/ui/date-range-picker.tsx +++ /dev/null @@ -1,82 +0,0 @@ -'use client' - -import * as React from 'react' -import { CalendarIcon } from '@radix-ui/react-icons' -import { addDays, format } from 'date-fns' -import moment from 'moment' -import { DateRange } from 'react-day-picker' - -import { cn } from '@/lib/utils' -import { Button } from '@/components/ui/button' -import { Calendar } from '@/components/ui/calendar' -import { - Popover, - PopoverContent, - PopoverTrigger -} from '@/components/ui/popover' - -export default function DatePickerWithRange({ - dateRange, - className, - buttonClassName, - contentAlign, - onOpenChange -}: React.HTMLAttributes & { - dateRange?: DateRange - buttonClassName?: string - contentAlign?: 'start' | 'end' | 'center' - onOpenChange?: (isOpen: boolean, date: DateRange | undefined) => void - onSelectDateRange?: (date: DateRange | undefined) => void -}) { - const [date, setDate] = React.useState({ - from: dateRange?.from || new Date(2022, 0, 20), - to: dateRange?.to || addDays(new Date(2022, 0, 20), 20) - }) - - const toggleOpen = (isOpen: boolean) => { - if (onOpenChange) onOpenChange(isOpen, date) - } - - return ( -
- - - - - - date > new Date()} - /> - - -
- ) -} diff --git a/ee/tabby-ui/app/(dashboard)/reports/use-all-members.tsx b/ee/tabby-ui/lib/hooks/use-all-members.tsx similarity index 98% rename from ee/tabby-ui/app/(dashboard)/reports/use-all-members.tsx rename to ee/tabby-ui/lib/hooks/use-all-members.tsx index b179ebe37797..6d6c33d2c4ef 100644 --- a/ee/tabby-ui/app/(dashboard)/reports/use-all-members.tsx +++ b/ee/tabby-ui/lib/hooks/use-all-members.tsx @@ -5,7 +5,7 @@ import { DEFAULT_PAGE_SIZE } from '@/lib/constants' import { QueryVariables } from '@/lib/tabby/gql' import { listUsers } from '@/lib/tabby/query' -type Member = { +export type Member = { id: string email: string } diff --git a/ee/tabby-ui/lib/language-utils/index.ts b/ee/tabby-ui/lib/language-utils/index.ts index e16c135a47fd..37536cf0c60f 100644 --- a/ee/tabby-ui/lib/language-utils/index.ts +++ b/ee/tabby-ui/lib/language-utils/index.ts @@ -1,10 +1,18 @@ import path from 'path' -import { has } from 'lodash-es' +import { has, isNil } from 'lodash-es' import { Language as ProgrammingLanguage } from '@/lib/gql/generates/graphql' +import languageColors from './language-colors.json' import languages from './languages' +const languageColorMapping: Record = Object.entries( + languageColors +).reduce((acc, cur) => { + const [lan, color] = cur + return { ...acc, [lan.toLocaleLowerCase()]: color } +}, {}) + // Fork from // https://github.com/TomerAberbach/filename2prism/ export const filename2prism: (filename: string) => Array = filename => { @@ -38,13 +46,20 @@ export const filename2prism: (filename: string) => Array = filename => { .filter(Boolean) } -export const toProgrammingLanguageDisplayName = ( - lan: ProgrammingLanguage +export const getLanguageDisplayName = ( + lan?: string, + defaultLan?: string ): string => { + const returnDefault = () => (!isNil(defaultLan) ? defaultLan : 'Other') + if (!lan) return returnDefault() + + const indexInSupportedLanguages = Object.values(ProgrammingLanguage) + .map(lan => lan.toLocaleLowerCase()) + .indexOf(lan.toLocaleLowerCase()) + if (indexInSupportedLanguages === -1) return returnDefault() + const displayName = - Object.keys(ProgrammingLanguage)[ - Object.values(ProgrammingLanguage).indexOf(lan) - ] || '' + Object.keys(ProgrammingLanguage)[indexInSupportedLanguages] const mapping: Record = { csharp: 'C#', cpp: 'C++', @@ -53,3 +68,7 @@ export const toProgrammingLanguageDisplayName = ( } return mapping[displayName.toLocaleLowerCase()] || displayName } + +export const getLanguageColor = (lan: string): string | undefined => { + return languageColorMapping[lan.toLowerCase()] +} diff --git a/ee/tabby-ui/app/(home)/language-colors.json b/ee/tabby-ui/lib/language-utils/language-colors.json similarity index 100% rename from ee/tabby-ui/app/(home)/language-colors.json rename to ee/tabby-ui/lib/language-utils/language-colors.json diff --git a/ee/tabby-ui/package.json b/ee/tabby-ui/package.json index 7c9dae2e0c07..8a5532c7b398 100644 --- a/ee/tabby-ui/package.json +++ b/ee/tabby-ui/package.json @@ -80,6 +80,7 @@ "react-dom": "^18.2.0", "react-hook-form": "^7.48.2", "react-intersection-observer": "^9.4.4", + "react-json-view": "^1.21.3", "react-markdown": "^8.0.7", "react-nice-avatar": "^1.5.0", "react-resizable-panels": "^1.0.7", diff --git a/ee/tabby-ui/yarn.lock b/ee/tabby-ui/yarn.lock index cca4c67d5df6..f2efe65156cf 100644 --- a/ee/tabby-ui/yarn.lock +++ b/ee/tabby-ui/yarn.lock @@ -545,7 +545,7 @@ dependencies: regenerator-runtime "^0.14.0" -"@babel/runtime@^7.13.10", "@babel/runtime@^7.20.13", "@babel/runtime@^7.20.7", "@babel/runtime@^7.3.1": +"@babel/runtime@^7.13.10", "@babel/runtime@^7.20.13", "@babel/runtime@^7.20.7": version "7.23.1" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.1.tgz#72741dc4d413338a91dcb044a86f3c0bc402646d" integrity sha512-hC2v6p8ZSI/W0HUzh3V8C5g+NwSKzKPtJwSpTjwl0o297GP9+ZLQSkdvHz46CM3LqyoXxq+5G9komY+eSqSO0g== @@ -559,7 +559,7 @@ dependencies: regenerator-runtime "^0.14.0" -"@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7": +"@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7": version "7.24.4" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.4.tgz#de795accd698007a66ba44add6cc86542aff1edd" integrity sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA== @@ -3320,6 +3320,11 @@ base-64@^0.1.0: resolved "https://registry.yarnpkg.com/base-64/-/base-64-0.1.0.tgz#780a99c84e7d600260361511c4877613bf24f6bb" integrity sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA== +base16@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/base16/-/base16-1.0.0.tgz#e297f60d7ec1014a7a971a39ebc8a98c0b681e70" + integrity sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ== + base64-js@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-0.0.8.tgz#1101e9544f4a76b1bc3b26d452ca96d7a35e7978" @@ -4607,12 +4612,19 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" +fbemitter@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/fbemitter/-/fbemitter-3.0.0.tgz#00b2a1af5411254aab416cd75f9e6289bee4bff3" + integrity sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw== + dependencies: + fbjs "^3.0.0" + fbjs-css-vars@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz#216551136ae02fe255932c3ec8775f18e2c078b8" integrity sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ== -fbjs@^3.0.0: +fbjs@^3.0.0, fbjs@^3.0.1: version "3.0.5" resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-3.0.5.tgz#aa0edb7d5caa6340011790bd9249dbef8a81128d" integrity sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg== @@ -4681,6 +4693,14 @@ flatted@^3.2.7: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf" integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== +flux@^4.0.1: + version "4.0.4" + resolved "https://registry.yarnpkg.com/flux/-/flux-4.0.4.tgz#9661182ea81d161ee1a6a6af10d20485ef2ac572" + integrity sha512-NCj3XlayA2UsapRpM7va6wU1+9rE5FIL7qoMcmxWHRzbp0yujihMBm9BBHZ1MDIk5h5o2Bl6eGiCe8rYELAmYw== + dependencies: + fbemitter "^3.0.0" + fbjs "^3.0.1" + focus-trap-react@^10.1.1: version "10.2.2" resolved "https://registry.yarnpkg.com/focus-trap-react/-/focus-trap-react-10.2.2.tgz#3b57cdee506e16c3cb75d2cb939b2d509b450489" @@ -5780,6 +5800,16 @@ lodash.castarray@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.castarray/-/lodash.castarray-4.4.0.tgz#c02513515e309daddd4c24c60cfddcf5976d9115" integrity sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q== +lodash.curry@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.curry/-/lodash.curry-4.1.1.tgz#248e36072ede906501d75966200a86dab8b23170" + integrity sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA== + +lodash.flow@^3.3.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/lodash.flow/-/lodash.flow-3.5.0.tgz#87bf40292b8cf83e4e8ce1a3ae4209e20071675a" + integrity sha512-ff3BX/tSioo+XojX4MOsOMhJw0nZoUEF011LX8g8d3gvjVbxd89cCio4BCXronjxcTUIJUoqKEUA+n4CqvvRPw== + lodash.isplainobject@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" @@ -7044,6 +7074,11 @@ punycode@^2.1.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== +pure-color@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/pure-color/-/pure-color-1.3.0.tgz#1fe064fb0ac851f0de61320a8bf796836422f33e" + integrity sha512-QFADYnsVoBMw1srW7OVKEYjG+MbIa49s54w1MA1EDY6r2r/sTcKKYqRX1f4GYvnXP7eN/Pe9HFcX+hwzmrXRHA== + pvtsutils@^1.3.2, pvtsutils@^1.3.5: version "1.3.5" resolved "https://registry.yarnpkg.com/pvtsutils/-/pvtsutils-1.3.5.tgz#b8705b437b7b134cd7fd858f025a23456f1ce910" @@ -7070,6 +7105,16 @@ react-activity-calendar@^2.2.8: chroma-js "^2.4.2" date-fns "^3.2.0" +react-base16-styling@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/react-base16-styling/-/react-base16-styling-0.6.0.tgz#ef2156d66cf4139695c8a167886cb69ea660792c" + integrity sha512-yvh/7CArceR/jNATXOKDlvTnPKPmGZz7zsenQ3jUwLzHkNUR0CvY3yGYJbWJ/nnxsL8Sgmt5cO3/SILVuPO6TQ== + dependencies: + base16 "^1.0.0" + lodash.curry "^4.0.1" + lodash.flow "^3.3.0" + pure-color "^1.2.0" + react-day-picker@^8.10.0: version "8.10.0" resolved "https://registry.yarnpkg.com/react-day-picker/-/react-day-picker-8.10.0.tgz#729c5b9564967a924213978fb9c0751884a60595" @@ -7103,6 +7148,21 @@ react-is@^18.0.0, react-is@^18.2.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== +react-json-view@^1.21.3: + version "1.21.3" + resolved "https://registry.yarnpkg.com/react-json-view/-/react-json-view-1.21.3.tgz#f184209ee8f1bf374fb0c41b0813cff54549c475" + integrity sha512-13p8IREj9/x/Ye4WI/JpjhoIwuzEgUAtgJZNBJckfzJt1qyh24BdTm6UQNGnyTq9dapQdrqvquZTo3dz1X6Cjw== + dependencies: + flux "^4.0.1" + react-base16-styling "^0.6.0" + react-lifecycles-compat "^3.0.4" + react-textarea-autosize "^8.3.2" + +react-lifecycles-compat@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" + integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== + react-markdown@^8.0.7: version "8.0.7" resolved "https://registry.yarnpkg.com/react-markdown/-/react-markdown-8.0.7.tgz#c8dbd1b9ba5f1c5e7e5f2a44de465a3caafdf89b" @@ -7186,7 +7246,7 @@ react-syntax-highlighter@^15.5.0: prismjs "^1.27.0" refractor "^3.6.0" -react-textarea-autosize@^8.4.1: +react-textarea-autosize@^8.3.2, react-textarea-autosize@^8.4.1: version "8.5.3" resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-8.5.3.tgz#d1e9fe760178413891484847d3378706052dd409" integrity sha512-XT1024o2pqCuZSuBt9FwHlaDeNtVrtCXu0Rnz88t1jUGheCLa3PhjE1GH8Ctm2axEtvdCl5SUHYschyQ0L5QHQ==